/* -*- c-basic-offset: 8 -*-
   rdesktop: A Remote Desktop Protocol client.
   win32 calls
   Copyright (C) Jay Sorg 2006

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "precomp.h"

#include "uimain.h"

extern char g_username[];
extern char g_hostname[];
extern char g_servername[];
extern char g_password[];
extern char g_shell[];
extern char g_directory[];
extern char g_domain[];
extern int g_width;
extern int g_height;
extern int g_tcp_sck;
extern int g_server_depth;
extern int g_tcp_port_rdp; /* in tcp.c */
extern int pal_entries[];

static HWND g_Wnd = 0;
static HINSTANCE g_Instance = 0;
static HCURSOR g_cursor = 0;
static int g_block = 0;
static int g_xoff = 0; /* offset from window to client coords */
static int g_yoff = 0;
static int g_xscroll = 0; /* current scroll position */
static int g_yscroll = 0;
static int g_screen_width = 0;
static int g_screen_height = 0;
static int g_wnd_cwidth = 0; /* set from WM_SIZE */
static int g_wnd_cheight = 0;
static int g_fullscreen = 0;
static int g_workarea = 0;
static int g_mousex = 0; /* in client coords */
static int g_mousey = 0;
//static int g_width_height_set = 0;

static int g_clip_left = 0;
static int g_clip_top = 0;
static int g_clip_right = 800;
static int g_clip_bottom = 600;
static RECT g_wnd_clip; /* this client area of whats actually visible */
                        /* set from WM_SIZE */

/*****************************************************************************/
static void
str_to_uni(TCHAR * sizex, char * size1)
{
  int len;
  int i;

  len = strlen(size1);
  for (i = 0; i < len; i++)
  {
    sizex[i] = size1[i];
  }
  sizex[len] = 0;
}

/*****************************************************************************/
static void
uni_to_str(char * sizex, TCHAR * size1)
{
  int len;
  int i;

  len = lstrlen(size1);
  for (i = 0; i < len; i++)
  {
    sizex[i] = (char)size1[i];
  }
  sizex[len] = 0;
}

/*****************************************************************************/
/* returns non zero if it processed something */
static int
check_sck(void)
{
  fd_set rfds;
  struct timeval tm;
  int count;
  int rv;

  rv = 0;
  if (g_block == 0)
  {
    g_block = 1;
    /* see if there really is data */
    FD_ZERO(&rfds);
    FD_SET((unsigned int)g_tcp_sck, &rfds);
    ZeroMemory(&tm, sizeof(tm));
    count = select(g_tcp_sck + 1, &rfds, 0, 0, &tm);
    if (count > 0)
    {
      if (ui_read_wire())
      {
        rv = 1;
      }
      else
      {
        PostQuitMessage(0);
      }
    }
    g_block = 0;
  }
  return rv;
}

/*****************************************************************************/
void
mi_error(char * msg)
{
#ifdef WITH_DEBUG
  printf(msg);
#else /* WITH_DEBUG */
  TCHAR lmsg[512];
  TCHAR ltitle[512];

  str_to_uni(lmsg, msg);
  str_to_uni(ltitle, "Error");
  MessageBox(g_Wnd, lmsg, ltitle, MB_OK);
#endif /* WITH_DEBUG */
}

/*****************************************************************************/
static int
get_scan_code_from_ascii(int code)
{
  int rv;

  rv = 0;
  switch (code & 0xff)
  {
    case 0x30: rv = 0x0b; break; // 0
    case 0x31: rv = 0x02; break; // 1
    case 0x32: rv = 0x03; break; // 2
    case 0x33: rv = 0x04; break; // 3
    case 0x34: rv = 0x05; break; // 4
    case 0x35: rv = 0x06; break; // 5
    case 0x36: rv = 0x07; break; // 6
    case 0x37: rv = 0x08; break; // 7
    case 0x38: rv = 0x09; break; // 8
    case 0x39: rv = 0x0a; break; // 9

    case 0xbd: rv = 0x0c; break; // -
    case 0xbb: rv = 0x0d; break; // =
    case 0x08: rv = 0x0e; break; // backspace
    case 0x09: rv = 0x0f; break; // tab
    case 0xdb: rv = 0x1b; break; // ]
    case 0xdd: rv = 0x1a; break; // [
    case 0x14: rv = 0x3a; break; // capslock
    case 0xba: rv = 0x27; break; // ;
    case 0xde: rv = 0x28; break; // '
    case 0x10: rv = 0x2a; break; // shift
    case 0xbc: rv = 0x33; break; // ,
    case 0xbe: rv = 0x34; break; // .
    case 0xbf: rv = 0x35; break; // /
    case 0x0d: rv = 0x1c; break; // enter
    case 0x27: rv = 0x4d; break; // arrow right
    case 0x25: rv = 0x4b; break; // arrow left
    case 0x26: rv = 0x48; break; // arrow up
    case 0x28: rv = 0x50; break; // arrow down
    case 0x20: rv = 0x39; break; // space
    case 0xdc: rv = 0x2b; break; // backslash
    case 0xc0: rv = 0x29; break; // `
    case 0x11: rv = 0x1d; break; // ctl

    case 0x41: rv = 0x1e; break; // a
    case 0x42: rv = 0x30; break; // b
    case 0x43: rv = 0x2e; break; // c
    case 0x44: rv = 0x20; break; // d
    case 0x45: rv = 0x12; break; // e
    case 0x46: rv = 0x21; break; // f
    case 0x47: rv = 0x22; break; // g
    case 0x48: rv = 0x23; break; // h
    case 0x49: rv = 0x17; break; // i
    case 0x4a: rv = 0x24; break; // j
    case 0x4b: rv = 0x25; break; // k
    case 0x4c: rv = 0x26; break; // l
    case 0x4d: rv = 0x32; break; // m
    case 0x4e: rv = 0x31; break; // n
    case 0x4f: rv = 0x18; break; // o
    case 0x50: rv = 0x19; break; // p
    case 0x51: rv = 0x10; break; // q
    case 0x52: rv = 0x13; break; // r
    case 0x53: rv = 0x1f; break; // s
    case 0x54: rv = 0x14; break; // t
    case 0x55: rv = 0x16; break; // u
    case 0x56: rv = 0x2f; break; // v
    case 0x57: rv = 0x11; break; // w
    case 0x58: rv = 0x2d; break; // x
    case 0x59: rv = 0x15; break; // y
    case 0x5a: rv = 0x2c; break; // z
  }
  return rv;
}

/*****************************************************************************/
static void
mi_scroll(int dx, int dy)
{
  HRGN rgn;

  rgn = CreateRectRgn(0, 0, 0, 0);
  ScrollWindowEx(g_Wnd, dx, dy, 0, 0, rgn, 0, SW_ERASE);
  InvalidateRgn(g_Wnd, rgn, 0);
  DeleteObject(rgn);
}

/*****************************************************************************/
int
mi_read_keyboard_state(void)
{
  short keydata;
  int code;

  code = 0;
  keydata = GetKeyState(VK_SCROLL);
  if (keydata & 0x0001)
  {
    code |= 1;
  }
  keydata = GetKeyState(VK_NUMLOCK);
  if (keydata & 0x0001)
  {
    code |= 2;
  }
  keydata = GetKeyState(VK_CAPITAL);
  if (keydata & 0x0001)
  {
    code |= 4;
  }
  return code;
}

/*****************************************************************************/
static void
mi_check_modifier(void)
{
  int code;

  code = mi_read_keyboard_state();
  ui_set_modifier_state(code);
}

/*****************************************************************************/
static LRESULT
handle_WM_SETCURSOR(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  if (g_mousex >= g_wnd_clip.left &&
      g_mousey >= g_wnd_clip.top &&
      g_mousex < g_wnd_clip.right &&
      g_mousey < g_wnd_clip.bottom)
  {
    SetCursor(g_cursor);
  }
  /* need default behavoir here */
  return DefWindowProc(hWnd, message, wParam, lParam);
}

/*****************************************************************************/
static LRESULT
handle_WM_NCHITTEST(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  POINT pt;

  pt.x = LOWORD(lParam);
  pt.y = HIWORD(lParam);
  if (ScreenToClient(g_Wnd, &pt))
  {
    g_mousex = pt.x;
    g_mousey = pt.y;
  }
  return DefWindowProc(hWnd, message, wParam, lParam);
}

/*****************************************************************************/
static LRESULT
handle_WM_MOUSEMOVE(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  g_mousex = LOWORD(lParam);
  g_mousey = HIWORD(lParam);
  ui_mouse_move(g_mousex + g_xscroll, g_mousey + g_yscroll);
  return 0;
}

/*****************************************************************************/
static LRESULT
handle_WM_LBUTTONDOWN(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  g_mousex = LOWORD(lParam);
  g_mousey = HIWORD(lParam);
  ui_mouse_button(1, g_mousex + g_xscroll, g_mousey + g_yscroll, 1);
  return 0;
}

/*****************************************************************************/
static LRESULT
handle_WM_LBUTTONUP(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  g_mousex = LOWORD(lParam);
  g_mousey = HIWORD(lParam);
  ui_mouse_button(1, g_mousex + g_xscroll, g_mousey + g_yscroll, 0);
  return 0;
}

/*****************************************************************************/
static LRESULT
handle_WM_RBUTTONDOWN(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  g_mousex = LOWORD(lParam);
  g_mousey = HIWORD(lParam);
  ui_mouse_button(2, g_mousex + g_xscroll, g_mousey + g_yscroll, 1);
  return 0;
}

/*****************************************************************************/
static LRESULT
handle_WM_RBUTTONUP(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  g_mousex = LOWORD(lParam);
  g_mousey = HIWORD(lParam);
  ui_mouse_button(2, g_mousex + g_xscroll, g_mousey + g_yscroll, 0);
  return 0;
}

/*****************************************************************************/
static LRESULT
handle_WM_MBUTTONDOWN(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  g_mousex = LOWORD(lParam);
  g_mousey = HIWORD(lParam);
  ui_mouse_button(3, g_mousex + g_xscroll, g_mousey + g_yscroll, 1);
  return 0;
}

/*****************************************************************************/
static LRESULT
handle_WM_MBUTTONUP(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  g_mousex = LOWORD(lParam);
  g_mousey = HIWORD(lParam);
  ui_mouse_button(3, g_mousex + g_xscroll, g_mousey + g_yscroll, 0);
  return 0;
}

/*****************************************************************************/
static LRESULT
handle_WM_MOUSEWHEEL(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  int delta;

  delta = ((signed short)HIWORD(wParam)); /* GET_WHEEL_DELTA_WPARAM(wParam); */
  if (delta > 0)
  {
    ui_mouse_button(4, 0, 0, 1);
    ui_mouse_button(4, 0, 0, 0);
  }
  else
  {
    ui_mouse_button(5, 0, 0, 1);
    ui_mouse_button(5, 0, 0, 0);
  }
  return 0;
}

/*****************************************************************************/
static LRESULT
handle_WM_KEY(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  int scancode;
  int ext;
  int down;

  ext = HIWORD(lParam);
  scancode = ext;
  down = !(ext & 0x8000);
  scancode &= 0xff;
  if (scancode == 0)
  {
    scancode = get_scan_code_from_ascii(wParam);
  }
  ext &= 0x0100;
  if (scancode == 0x0045) /* num lock */
  {
    ext &= ~0x0100;
  }
  if (down)
  {
    ui_key_down(scancode, ext);
  }
  else
  {
    ui_key_up(scancode, ext);
  }
  return 0;
}

/*****************************************************************************/
static LRESULT
handle_WM_PAINT(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  PAINTSTRUCT ps;
  RECT rect;
  HBRUSH brush;

  BeginPaint(hWnd, &ps);
  /* paint the area outside the rdp screen with one colour */
  rect = ps.rcPaint;
  rect.left = UI_MAX(rect.left, g_width);
  if (!IsRectEmpty(&rect))
  {
    brush = CreateSolidBrush(RGB(0, 0, 255));
    FillRect(ps.hdc, &rect, brush);
    DeleteObject(brush);
  }
  rect = ps.rcPaint;
  rect.top = UI_MAX(rect.top, g_height);
  if (!IsRectEmpty(&rect))
  {
    brush = CreateSolidBrush(RGB(0, 0, 255));
    FillRect(ps.hdc, &rect, brush);
    DeleteObject(brush);
  }
  rect = ps.rcPaint;
  EndPaint(hWnd, &ps);
  ui_invalidate(rect.left + g_xscroll,
                rect.top + g_yscroll,
                (rect.right - rect.left) + 1,
                (rect.bottom - rect.top) + 1);
  return 0;
}

/*****************************************************************************/
static LRESULT
handle_WM_SIZE(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  int oldxscroll;
  int oldyscroll;

  if (wParam == SIZE_MINIMIZED)
  {
    return DefWindowProc(hWnd, message, wParam, lParam);
  }
  g_wnd_cwidth = LOWORD(lParam); /* client width / height */
  g_wnd_cheight = HIWORD(lParam);
  g_wnd_clip.left = 0;
  g_wnd_clip.top = 0;
  g_wnd_clip.right = g_wnd_clip.left + g_wnd_cwidth;
  g_wnd_clip.bottom = g_wnd_clip.top + g_wnd_cheight;
  if (g_wnd_cwidth < g_width || g_wnd_cheight < g_height)
  {
    SetScrollRange(g_Wnd, SB_HORZ, 0, g_width - g_wnd_cwidth, 1);
    SetScrollRange(g_Wnd, SB_VERT, 0, g_height - g_wnd_cheight, 1);
  }
  oldxscroll = g_xscroll;
  oldyscroll = g_yscroll;
  if (g_wnd_cwidth >= g_width)
  {
    g_xscroll = 0;
  }
  else
  {
    g_xscroll = UI_MIN(g_xscroll, g_width - g_wnd_cwidth);
  }
  if (g_wnd_cheight >= g_height)
  {
    g_yscroll = 0;
  }
  else
  {
    g_yscroll = UI_MIN(g_yscroll, g_height - g_wnd_cheight);
  }
  mi_scroll(oldxscroll - g_xscroll, oldyscroll - g_yscroll);
  if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
  {
    /* check the caps, num, and scroll lock here */
    mi_check_modifier();
  }
  return 0;
}

/*****************************************************************************/
static LRESULT
handle_WM_SIZING(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  LPRECT prect;
  int width;
  int height;
  int style;

  prect = (LPRECT) lParam; /* total window rect */
  width = (prect->right - prect->left) - (g_xoff * 2);
  height = (prect->bottom - prect->top) - (g_yoff + g_xoff);
  if (height < g_height || width < g_width)
  {
    style = GetWindowLongPtr(g_Wnd, GWL_STYLE);
    if (!(style & WS_HSCROLL))
    {
      style |= WS_HSCROLL | WS_VSCROLL;
      SetWindowLongPtr(g_Wnd, GWL_STYLE, style);
      g_xscroll = 0;
      g_yscroll = 0;
      SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
      SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
    }
  }
  else if (height >= g_height && width >= g_width)
  {
    style = GetWindowLongPtr(g_Wnd, GWL_STYLE);
    if (style & WS_HSCROLL)
    {
      style &= ~WS_HSCROLL;
      style &= ~WS_VSCROLL;
      SetWindowLongPtr(g_Wnd, GWL_STYLE, style);
      g_xscroll = 0;
      g_yscroll = 0;
    }
  }
  return 0;
}

/*****************************************************************************/
static LRESULT
handle_WM_HSCROLL(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  int code;
  int oldxscroll;

  code = (int) LOWORD(wParam); /* scroll bar value */
  if (code == SB_LINELEFT)
  {
    oldxscroll = g_xscroll;
    g_xscroll--;
    g_xscroll = UI_MAX(g_xscroll, 0);
    SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
    mi_scroll(oldxscroll - g_xscroll, 0);
  }
  else if (code == SB_LINERIGHT)
  {
    oldxscroll = g_xscroll;
    g_xscroll++;
    g_xscroll = UI_MIN(g_xscroll, g_width - g_wnd_cwidth);
    SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
    mi_scroll(oldxscroll - g_xscroll, 0);
  }
  else if (code == SB_PAGELEFT)
  {
    oldxscroll = g_xscroll;
    g_xscroll -= g_wnd_cwidth / 2;
    g_xscroll = UI_MAX(g_xscroll, 0);
    SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
    mi_scroll(oldxscroll - g_xscroll, 0);
  }
  else if (code == SB_PAGERIGHT)
  {
    oldxscroll = g_xscroll;
    g_xscroll += g_wnd_cwidth / 2;
    g_xscroll = UI_MIN(g_xscroll, g_width - g_wnd_cwidth);
    SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
    mi_scroll(oldxscroll - g_xscroll, 0);
  }
  else if (code == SB_BOTTOM)
  {
    oldxscroll = g_xscroll;
    g_xscroll = g_width - g_wnd_cwidth;
    SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
    mi_scroll(oldxscroll - g_xscroll, 0);
  }
  else if (code == SB_TOP)
  {
    oldxscroll = g_xscroll;
    g_xscroll = 0;
    SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
    mi_scroll(oldxscroll - g_xscroll, 0);
  }
  else if (code == SB_THUMBPOSITION)
  {
    oldxscroll = g_xscroll;
    g_xscroll = (signed short) HIWORD(wParam);
    SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
    mi_scroll(oldxscroll - g_xscroll, 0);
  }
  return 0;
}

/*****************************************************************************/
static LRESULT
handle_WM_VSCROLL(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  int code;
  int oldyscroll;

  code = (int) LOWORD(wParam); /* scroll bar value */
  if (code == SB_LINELEFT)
  {
    oldyscroll = g_yscroll;
    g_yscroll--;
    g_yscroll = UI_MAX(g_yscroll, 0);
    SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
    mi_scroll(0, oldyscroll - g_yscroll);
  }
  else if (code == SB_LINERIGHT)
  {
    oldyscroll = g_yscroll;
    g_yscroll++;
    g_yscroll = UI_MIN(g_yscroll, g_height - g_wnd_cheight);
    SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
    mi_scroll(0, oldyscroll - g_yscroll);
  }
  else if (code == SB_PAGELEFT)
  {
    oldyscroll = g_yscroll;
    g_yscroll -= g_wnd_cheight / 2;
    g_yscroll = UI_MAX(g_yscroll, 0);
    SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
    mi_scroll(0, oldyscroll - g_yscroll);
  }
  else if (code == SB_PAGERIGHT)
  {
    oldyscroll = g_yscroll;
    g_yscroll += g_wnd_cheight / 2;
    g_yscroll = UI_MIN(g_yscroll, g_height - g_wnd_cheight);
    SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
    mi_scroll(0, oldyscroll - g_yscroll);
  }
  else if (code == SB_BOTTOM)
  {
    oldyscroll = g_yscroll;
    g_yscroll = g_height - g_wnd_cheight;
    SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
    mi_scroll(0, oldyscroll - g_yscroll);
  }
  else if (code == SB_TOP)
  {
    oldyscroll = g_yscroll;
    g_yscroll = 0;
    SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
    mi_scroll(0, oldyscroll - g_yscroll);
  }
  else if (code == SB_THUMBPOSITION)
  {
    oldyscroll = g_yscroll;
    g_yscroll = (signed short) HIWORD(wParam);
    SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
    mi_scroll(0, oldyscroll - g_yscroll);
  }
  return 0;
}


/*****************************************************************************/
LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message)
  {
    case WM_SETCURSOR:
      return handle_WM_SETCURSOR(hWnd, message, wParam, lParam);
    case 0x0084: /* WinCE don't have this WM_NCHITTEST: */
      return handle_WM_NCHITTEST(hWnd, message, wParam, lParam);
    case WM_MOUSEMOVE:
      return handle_WM_MOUSEMOVE(hWnd, message, wParam, lParam);
    case WM_LBUTTONDOWN:
      return handle_WM_LBUTTONDOWN(hWnd, message, wParam, lParam);
    case WM_LBUTTONUP:
      return handle_WM_LBUTTONUP(hWnd, message, wParam, lParam);
    case WM_RBUTTONDOWN:
      return handle_WM_RBUTTONDOWN(hWnd, message, wParam, lParam);
    case WM_RBUTTONUP:
      return handle_WM_RBUTTONUP(hWnd, message, wParam, lParam);
    case WM_MBUTTONDOWN:
      return handle_WM_MBUTTONDOWN(hWnd, message, wParam, lParam);
    case WM_MBUTTONUP:
      return handle_WM_MBUTTONUP(hWnd, message, wParam, lParam);
    /* some windows compilers don't have these defined like vc6 */
    case 0x020a: /* WM_MOUSEWHEEL: */
      return handle_WM_MOUSEWHEEL(hWnd, message, wParam, lParam);
    case WM_KEYDOWN:
    case WM_KEYUP:
    case WM_SYSKEYDOWN:
    case WM_SYSKEYUP:
      return handle_WM_KEY(hWnd, message, wParam, lParam);
    case WM_CHAR:
    case WM_DEADCHAR:
    case WM_SYSCHAR:
    case WM_SYSDEADCHAR:
      break;
    case WM_PAINT:
      return handle_WM_PAINT(hWnd, message, wParam, lParam);
    case WM_DESTROY:
      PostQuitMessage(0);
      break;
    case WM_APP + 1:
    case WM_TIMER:
      check_sck();
      break;
    case WM_SIZE:
      return handle_WM_SIZE(hWnd, message, wParam, lParam);
    case 532: /* not defined in wince WM_SIZING: */
      return handle_WM_SIZING(hWnd, message, wParam, lParam);
    case WM_HSCROLL:
      return handle_WM_HSCROLL(hWnd, message, wParam, lParam);
    case WM_VSCROLL:
      return handle_WM_VSCROLL(hWnd, message, wParam, lParam);
    case WM_SETFOCUS:
      mi_check_modifier();
      return DefWindowProc(hWnd, message, wParam, lParam);
    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
  }
  return 0;
}

/*****************************************************************************/
static HRGN
mi_clip(HDC dc)
{
  HRGN rgn;

  rgn = CreateRectRgn(g_clip_left + g_xoff - g_xscroll,
                      g_clip_top + g_yoff - g_yscroll,
                      g_clip_right + g_xoff - g_xscroll,
                      g_clip_bottom + g_yoff - g_yscroll);
  SelectClipRgn(dc, rgn);
  IntersectClipRect(dc, g_wnd_clip.left + g_xoff, g_wnd_clip.top + g_yoff,
                    g_wnd_clip.right + g_xoff, g_wnd_clip.bottom + g_yoff);
  return rgn;
}

/*****************************************************************************/
/* returns non zero if ok */
int
mi_create_window(void)
{
  RECT rc;
  WNDCLASS wc;
  TCHAR classname[512];
  TCHAR caption[512];
  DWORD style;
  int x;
  int y;
  int w;
  int h;

  if (g_Wnd != 0 || g_Instance != 0)
  {
    return 0;
  }
  g_Instance = GetModuleHandle(NULL);
  ZeroMemory(&wc, sizeof(wc));
  wc.lpfnWndProc = WndProc; /* points to window procedure */
  /* name of window class */
  str_to_uni(classname, "rdesktop");
  wc.lpszClassName = classname;
  str_to_uni(caption, "ReactOS Remote Desktop");
  wc.hIcon = LoadIcon(g_Instance,
                      MAKEINTRESOURCE(IDI_MSTSC));
  /* Register the window class. */
  if (!RegisterClass(&wc))
  {
    return 0; /* Failed to register window class */
  }
  rc.left = 0;
  rc.right = rc.left + UI_MIN(g_width, g_screen_width);
  rc.top = 0;
  rc.bottom = rc.top + UI_MIN(g_height, g_screen_height);

  if (g_fullscreen)
  {
    style = WS_POPUP;
  }
  else
  {
    style = WS_OVERLAPPED | WS_CAPTION | WS_POPUP | WS_MINIMIZEBOX |
            WS_SYSMENU | WS_SIZEBOX | WS_MAXIMIZEBOX;
  }
  if (g_screen_width < g_width || g_screen_height < g_height)
  {
    style |= WS_HSCROLL | WS_VSCROLL;
  }
  AdjustWindowRectEx(&rc, style, 0, 0);
  x = CW_USEDEFAULT;
  y = CW_USEDEFAULT;
  w = rc.right - rc.left;
  h = rc.bottom - rc.top;

  g_Wnd = CreateWindow(wc.lpszClassName, caption,
                       style, x, y, w, h,
                       (HWND) NULL, (HMENU) NULL, g_Instance,
                       (LPVOID) NULL);
  g_clip_left = 0;
  g_clip_top = 0;
  g_clip_right = g_clip_left + g_width;
  g_clip_bottom = g_clip_top + g_height;
  if (g_workarea)
  {
    ShowWindow(g_Wnd, SW_SHOWMAXIMIZED);
  }
  else
  {
    ShowWindow(g_Wnd, SW_SHOWNORMAL);
  }
  UpdateWindow(g_Wnd);

  WSAAsyncSelect(g_tcp_sck, g_Wnd, WM_APP + 1, FD_READ);
  SetTimer(g_Wnd, 1, 333, 0);

  return 1;
}

/*****************************************************************************/
int
mi_main_loop(void)
{
  MSG msg;

  while (GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

/*****************************************************************************/
void
mi_warning(char * msg)
{
}

/*****************************************************************************/
static void
mi_show_error(char * caption)
{
  LPVOID lpMsgBuf;
  TCHAR lcaption[512];

  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR) &lpMsgBuf, 0, NULL);
#ifdef WITH_DEBUG
  printf(lpMsgBuf);
#else /* WITH_DEBUG */
  str_to_uni(lcaption, caption);
  MessageBox(g_Wnd, (LPTSTR) lpMsgBuf, lcaption,
             MB_OK | MB_ICONINFORMATION);
#endif /* WITH_DEBUG */
  LocalFree(lpMsgBuf);
}

/*****************************************************************************/
void
mi_paint_rect(char * data, int width, int height, int x, int y, int cx, int cy)
{
  HBITMAP bitmap;
  BITMAPINFO bi;
  HDC dc;
  HDC maindc;
  HGDIOBJ save;
  HRGN rgn;
  void * bits;
  int i;
  int j;
  int colour;
  int red;
  int green;
  int blue;
  int index;

  ZeroMemory(&bi, sizeof(bi));
  bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
  bi.bmiHeader.biWidth = width;
  bi.bmiHeader.biHeight = -height;
  bi.bmiHeader.biPlanes = 1;
  bi.bmiHeader.biBitCount = 32;
  bi.bmiHeader.biCompression = BI_RGB;
  maindc = GetWindowDC(g_Wnd);
  bitmap = CreateDIBSection(maindc, &bi, DIB_RGB_COLORS, (void **) &bits, 0, 0);
  if (bitmap == 0)
  {
    mi_show_error("CreateDIBSection failed");
  }

  if (g_server_depth == 8)
  {
    for (i = cy - 1; i >= 0; i--)
    {
      for (j = cx - 1; j >= 0; j--)
      {
        colour = ((unsigned char*)data)[i * cx + j];
        red = (pal_entries[colour & 0xff] & 0xff0000) >> 16;
        green = (pal_entries[colour & 0xff] & 0xff00) >> 8;
        blue = pal_entries[colour & 0xff] & 0xff;
        MAKE_COLOUR32(colour, red, green, blue);
        ((unsigned int*)bits)[i * cx + j] = colour;
      }
    }
  }
  else if (g_server_depth == 15)
  {
    for (i = cy - 1; i >= 0; i--)
    {
      for (j = cx - 1; j >= 0; j--)
      {
        colour = ((unsigned short*)data)[i * cx + j];
        SPLIT_COLOUR15(colour, red, green, blue);
        MAKE_COLOUR32(colour, red, green, blue);
        ((unsigned int*)bits)[i * cx + j] = colour;
      }
    }
  }
  else if (g_server_depth == 16)
  {
    for (i = cy - 1; i >= 0; i--)
    {
      for (j = cx - 1; j >= 0; j--)
      {
        colour = ((unsigned short*)data)[i * cx + j];
        SPLIT_COLOUR16(colour, red, green, blue);
        MAKE_COLOUR32(colour, red, green, blue);
        ((unsigned int*)bits)[i * cx + j] = colour;
      }
    }
  }
  else if (g_server_depth == 24)
  {
    for (i = cy - 1; i >= 0; i--)
    {
      for (j = cx - 1; j >= 0; j--)
      {
        index = (i * cx + j) * 3;
        red = ((unsigned char*)data)[index + 2];
        green = ((unsigned char*)data)[index + 1];
        blue = ((unsigned char*)data)[index];
        MAKE_COLOUR32(colour, red, green, blue);
        ((unsigned int*)bits)[i * cx + j] = colour;
      }
    }
  }
  else if (g_server_depth == 32)
  {
    memcpy(bits, data, cx*cy*4);
  }
  dc = CreateCompatibleDC(maindc);
  if (dc == 0)
  {
    mi_show_error("CreateCompatibleDC failed");
  }
  save = SelectObject(dc, bitmap);
  rgn = mi_clip(maindc);
  BitBlt(maindc, x + g_xoff - g_xscroll, y + g_yoff - g_yscroll, cx, cy, dc,
         0, 0, SRCCOPY);
  SelectObject(dc, save);
  DeleteObject(bitmap);
  DeleteDC(dc);
  ReleaseDC(g_Wnd, maindc);
  DeleteObject(rgn);

}

static INT
GetPortNumber(PCHAR szAddress)
{
    PCHAR szPort;
    INT iPort = TCP_PORT_RDP;

    szPort = strtok(szAddress, ":");

    if (szPort != NULL)
    {
        szPort = strtok(NULL, ":");

        if (szPort != NULL)
        {
            iPort = atoi(szPort);

            if (iPort <= 0 || iPort > 0xFFFF)
                iPort = TCP_PORT_RDP;
        }
    }

    return iPort;
}

static VOID
SetDomainAndUsername(PCHAR pName)
{
    PCHAR pDomain;
    PCHAR pUsername;

    strcpy(g_domain, "");
    strcpy(g_username, "");

    pDomain = strtok(pName, "\\");

    if(pDomain == NULL)
        return;

    pUsername = strtok(NULL, "\\");

    if(pUsername == NULL)
    {
        strcpy(g_username, pDomain);
        return;
    }

    strcpy(g_username, pUsername);
    strcpy(g_domain, pDomain);
    return;
}

static BOOL
ParseCommandLine(LPWSTR lpCmdLine,
                 PRDPSETTINGS pRdpSettings,
                 BOOL *bSkipDlg)
{
    LPWSTR lpStr = lpCmdLine;
    WCHAR szSeps[] = L"/";
    LPWSTR lpToken;
    BOOL bRet = TRUE;

    *bSkipDlg = TRUE;

    if (*lpCmdLine != L'/')
    {
        LoadRdpSettingsFromFile(pRdpSettings, lpCmdLine);
    }
    else
    {
        /* default to screen size, 16bpp */
        SetIntegerToSettings(pRdpSettings, L"session bpp", 16);
        SetIntegerToSettings(pRdpSettings, L"desktopwidth", GetSystemMetrics(SM_CXSCREEN));
        SetIntegerToSettings(pRdpSettings, L"desktopheight", GetSystemMetrics(SM_CYSCREEN));

        lpToken = wcstok(lpStr, szSeps);
        while (lpToken)
        {
            if (wcsncmp(lpToken, L"edit", 4) == 0)
            {
                lpToken += 5;
                LoadRdpSettingsFromFile(pRdpSettings, lpToken);
                *bSkipDlg = FALSE;
                break;
            }

            if (*lpToken == L'v')
            {
                lpToken += 2;
                SetStringToSettings(pRdpSettings, L"full address", lpToken);
            }
            else if (*lpToken == L'w')
            {
                lpToken += 2;
                SetIntegerToSettings(pRdpSettings, L"desktopwidth", _wtoi(lpToken));
            }
            else if (*lpToken == L'h')
            {
                lpToken += 2;
                SetIntegerToSettings(pRdpSettings, L"desktopheight", _wtoi(lpToken));
            }
            else if (*lpToken == L'f')
            {
                SetIntegerToSettings(pRdpSettings, L"screen mode id", 2);
            }

            lpToken = wcstok(NULL, szSeps);
        }
    }

    return bRet;
}

/*****************************************************************************/
int WINAPI
wWinMain(HINSTANCE hInstance,
         HINSTANCE hPrevInstance,
         LPWSTR lpCmdLine,
         int nCmdShow)
{
    PRDPSETTINGS pRdpSettings;
    WSADATA d;
    int ret = 1;

    if (WSAStartup(MAKEWORD(2, 0), &d) == 0)
    {
        pRdpSettings = HeapAlloc(GetProcessHeap(),
                                 0,
                                 sizeof(RDPSETTINGS));
        if (pRdpSettings)
        {
            pRdpSettings->pSettings = NULL;
            pRdpSettings->NumSettings = 0;

            if (InitRdpSettings(pRdpSettings))
            {
                BOOL bSkipDlg = FALSE;

                if (*lpCmdLine)
                    ParseCommandLine(lpCmdLine, pRdpSettings,&bSkipDlg);
                else
                    LoadRdpSettingsFromFile(pRdpSettings, NULL);

                if (bSkipDlg || OpenRDPConnectDialog(hInstance,
                                                      pRdpSettings))
                {
                    char szValue[MAXVALUE];
                    DWORD dwSize = MAXVALUE;

                    uni_to_str(szValue, GetStringFromSettings(pRdpSettings, L"full address"));

                    /* GetPortNumber also removes possible trailing port number from address */
                    g_tcp_port_rdp = GetPortNumber(szValue);
                    strcpy(g_servername, szValue);
                    uni_to_str(szValue, GetStringFromSettings(pRdpSettings, L"username"));
                    SetDomainAndUsername(szValue);
                    strcpy(g_password, "");
                    if (GetComputerNameA(szValue, &dwSize))
                        strcpy(g_hostname, szValue);
                    else
                        strcpy(g_hostname, tcp_get_address());
                    g_server_depth = GetIntegerFromSettings(pRdpSettings, L"session bpp");
                    g_screen_width = GetSystemMetrics(SM_CXSCREEN);
                    g_screen_height = GetSystemMetrics(SM_CYSCREEN);
                    g_width = GetIntegerFromSettings(pRdpSettings, L"desktopwidth");
                    g_height = GetIntegerFromSettings(pRdpSettings, L"desktopheight");
                    if (GetIntegerFromSettings(pRdpSettings, L"screen mode id") == 2)
                    {
                        g_fullscreen = 1;
                        g_xoff = 0;
                        g_yoff = 0;
                    }
                    else
                    {
                        g_xoff = GetSystemMetrics(SM_CXEDGE) * 2;
                        g_yoff = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYEDGE) * 2;
                    }

                    ui_main();
                    ret = 0;
                }

                HeapFree(GetProcessHeap(),
                         0,
                         pRdpSettings->pSettings);
            }

            HeapFree(GetProcessHeap(),
                     0,
                     pRdpSettings);
      }

        WSACleanup();
    }

  return ret;
}


/*****************************************************************************/
void
mi_begin_update(void)
{
}

/*****************************************************************************/
void
mi_end_update(void)
{
}

/*****************************************************************************/
void
mi_fill_rect(int x, int y, int cx, int cy, int colour)
{
  HBRUSH brush;
  RECT rect;
  HDC maindc;
  HRGN rgn;
  int red;
  int green;
  int blue;

  if (g_server_depth == 8)
  {
    red = (pal_entries[colour & 0xff] & 0xff0000) >> 16;
    green = (pal_entries[colour & 0xff] & 0xff00) >> 8;
    blue = pal_entries[colour & 0xff] & 0xff;
  }
  else if (g_server_depth == 15)
  {
    SPLIT_COLOUR15(colour, red, green, blue);
  }
  else if (g_server_depth == 16)
  {
    SPLIT_COLOUR16(colour, red, green, blue);
  }
  else if (g_server_depth == 24 || g_server_depth == 32)
  {
    red = (colour>>16)&0xff;
    green = (colour>>8)&0xff;
    blue =  colour&0xff;
  }
  maindc = GetWindowDC(g_Wnd);
  rgn = mi_clip(maindc);
  brush = CreateSolidBrush(RGB(red, green, blue));
  rect.left = x + g_xoff - g_xscroll;
  rect.top = y + g_yoff - g_yscroll;
  rect.right = rect.left + cx;
  rect.bottom = rect.top + cy;
  FillRect(maindc, &rect, brush);
  DeleteObject(brush);
  ReleaseDC(g_Wnd, maindc);
  DeleteObject(rgn);
}

/*****************************************************************************/
void
mi_line(int x1, int y1, int x2, int y2, int colour)
{
  HPEN pen;
  HDC maindc;
  HGDIOBJ save;
  HRGN rgn;
  int red;
  int green;
  int blue;

  if (g_server_depth == 8)
  {
    red = (pal_entries[colour & 0xff] & 0xff0000) >> 16;
    green = (pal_entries[colour & 0xff] & 0xff00) >> 8;
    blue = pal_entries[colour & 0xff] & 0xff;
  }
  else if (g_server_depth == 15)
  {
    SPLIT_COLOUR15(colour, red, green, blue);
  }
  else if (g_server_depth == 16)
  {
    SPLIT_COLOUR16(colour, red, green, blue);
  }
  else if (g_server_depth == 24 || g_server_depth == 32)
  {
    red = (colour>>16)&0xff;
    green = (colour>>8)&0xff;
    blue =  colour&0xff;
  }
  maindc = GetWindowDC(g_Wnd);
  rgn = mi_clip(maindc);
  pen = CreatePen(PS_SOLID, 0, RGB(red, green, blue));
  save = SelectObject(maindc, pen);
  MoveToEx(maindc, x1 + g_xoff - g_xscroll, y1 + g_yoff - g_yscroll, 0);
  LineTo(maindc, x2 + g_xoff - g_xscroll, y2 + g_yoff - g_yscroll);
  SelectObject(maindc, save);
  DeleteObject(pen);
  ReleaseDC(g_Wnd, maindc);
  DeleteObject(rgn);
}

/*****************************************************************************/
void
mi_screen_copy(int x, int y, int cx, int cy, int srcx, int srcy)
{
  RECT rect;
  RECT clip_rect;
  RECT draw_rect;
  HRGN rgn;
  int ok_to_ScrollWindowEx;

  ok_to_ScrollWindowEx = 1;

  if (!ok_to_ScrollWindowEx)
  {
    rgn = CreateRectRgn(x - g_xscroll, y - g_yscroll,
                        (x - g_xscroll) + cx,
                        (y - g_yscroll) + cy);
    InvalidateRgn(g_Wnd, rgn, 0);
    DeleteObject(rgn);
  }
  else
  {
    /* this is all in client coords */
    rect.left = srcx - g_xscroll;
    rect.top = srcy - g_yscroll;
    rect.right = rect.left + cx;
    rect.bottom = rect.top + cy;
    clip_rect.left = g_clip_left - g_xscroll;
    clip_rect.top = g_clip_top - g_yscroll;
    clip_rect.right = g_clip_right - g_xscroll;
    clip_rect.bottom = g_clip_bottom - g_yscroll;
    if (IntersectRect(&draw_rect, &clip_rect, &g_wnd_clip))
    {
      rgn = CreateRectRgn(0, 0, 0, 0);
      ScrollWindowEx(g_Wnd, x - srcx, y - srcy, &rect, &draw_rect,
                     rgn, 0, SW_ERASE);
      InvalidateRgn(g_Wnd, rgn, 0);
      DeleteObject(rgn);
    }
  }
}

/*****************************************************************************/
void
mi_set_clip(int x, int y, int cx, int cy)
{
  g_clip_left = x;
  g_clip_top = y;
  g_clip_right = g_clip_left + cx;
  g_clip_bottom = g_clip_top + cy;
}

/*****************************************************************************/
void
mi_reset_clip(void)
{
  g_clip_left = 0;
  g_clip_top = 0;
  g_clip_right = g_clip_left + g_width;
  g_clip_bottom = g_clip_top + g_height;
}

/*****************************************************************************/
void *
mi_create_cursor(unsigned int x, unsigned int y,
                 int width, int height,
                 unsigned char * andmask, unsigned char * xormask)
{
  HCURSOR hCur;

  hCur = CreateCursor(g_Instance, x, y, width, height, andmask, xormask);
  if (hCur == 0)
  {
    hCur = LoadCursor(NULL, IDC_ARROW);
  }
  return hCur;
}

/*****************************************************************************/
void
mi_destroy_cursor(void * cursor)
{
  if (g_cursor == cursor)
  {
    g_cursor = 0;
  }
  DestroyCursor(cursor);
}

/*****************************************************************************/
void
mi_set_cursor(void * cursor)
{
  g_cursor = cursor;
  SetCursor(g_cursor);
}

/*****************************************************************************/
void
mi_set_null_cursor(void)
{
}

