#define ID_Message 	101
#define ID_Edit		102
#define ID_OK 		103
#define ID_Cancel	104
#define ID_Timer	105

// 以下を #defineすれば少しは速くなるかもしれない。
// ただし、Font, Brush, Penのプロパティを変えたときに明示的に
// Updateメソッドを呼ぶ必要あり。

// #define NOAUTOUPDATE


char* prog;
HINSTANCE hInst;
HWND toplevel;
TForm* Form1;

LRESULT CALLBACK WindowFunc(HWND, UINT, WPARAM, LPARAM);
BOOL    CALLBACK DialogFunc(HWND, UINT, WPARAM, LPARAM);
static COLORREF MyGetSysColor(TColor c);

int main(int argc,char *argv[]) {
  prog = argv[0];
/* #ifdef __BORLANDC__ */
  STARTUPINFO s;
  GetStartupInfo(&s);

  _argc = argc; _argv = argv;
  return WinMain(GetModuleHandle(NULL), NULL, GetCommandLine(), s.wShowWindow);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
		   LPSTR lpszCmdLine, int nCmdShow) {
/* #endif */ /* __BORLANDC__ */
  HWND hwnd;
  WNDCLASS wc1;
  MSG msg;

  hInst = GetModuleHandle(NULL); 
  static PAINTSTRUCT hpaint;
  static TForm   frm;
  static TCanvas cvs;
  static TFont   fnt;
  static TBrush  brs;
  static TPen    pen;
  static TTimer  tmr;
  Form1 = &frm;
  Form1->Timer=&tmr;
  Form1->Canvas = &cvs;
  Form1->Canvas->Pen = &pen;
  Form1->Canvas->Brush = &brs;
  Form1->Canvas->Font = &fnt;

  wc1.lpszClassName= "MyWin"; 
  wc1.lpszMenuName = NULL;
  wc1.hInstance    = hInst;
  wc1.lpfnWndProc  = WindowFunc;
  wc1.hCursor      = LoadCursor(NULL, IDC_ARROW);
  wc1.hIcon        = LoadIcon(NULL, IDI_APPLICATION);
  wc1.hbrBackground= CreateSolidBrush(MyGetSysColor(Form1->Color));
  wc1.style        = 0;
  wc1.cbClsExtra   = 0;
  wc1.cbWndExtra   = 0;
  if(!RegisterClass(&wc1))return 0;

  Form1->Handle = toplevel = 
  hwnd=CreateWindow(wc1.lpszClassName, prog, WS_OVERLAPPEDWINDOW,
		    CW_USEDEFAULT, CW_USEDEFAULT, Form1->Width, Form1->Height,
		    HWND_DESKTOP, NULL, wc1.hInstance, NULL);
  Form1->FormCreate(NULL);
  ShowWindow(hwnd, SW_SHOWDEFAULT);
  Form1->Canvas->Handle = BeginPaint(hwnd, &hpaint);
  InvalidateRect(hwnd, NULL, false);
  UpdateWindow(hwnd); 


  while(GetMessage(&msg,NULL,0,0)) {
     /* TranslateMessage(&msg); */
     DispatchMessage(&msg);
  }

  EndPaint(hwnd, &hpaint);
  return msg.wParam;
}

LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message, 
                            WPARAM wParam, LPARAM lParam) {
  static int ShiftState = 0;
  switch(message)  {
    case WM_PAINT:
      Form1->FormPaint(NULL);
      ValidateRect(hwnd,NULL);
      return 0;
    
    case WM_LBUTTONDOWN:
	Form1->FormMouseDown(NULL, mbLeft/* which button */,
			     (TShiftState)wParam/* ShiftState */,
		      LOWORD(lParam)/* x */, HIWORD(lParam)/* y */);
        return 0;

    case WM_RBUTTONDOWN:
	Form1->FormMouseDown(NULL, mbRight/* which button */,
			     (TShiftState)wParam/* ShiftState */,  
			     LOWORD(lParam)/* x */, HIWORD(lParam)/* y */);
        return 0;

    case WM_KEYDOWN:
    {
	char c = (char)wParam;
	switch (c) {
	case VK_SHIFT:
	    ShiftState |= ssShift;
	    return 0;
	case VK_CONTROL:
	    ShiftState |= ssCtrl;
	    return 0;
	default:
	    Form1->FormKeyDown(NULL, c, (TShiftState)ShiftState);
	    return 0;
	}
	return 0;
    }

    case WM_KEYUP:
      switch ((char)wParam) {
      case VK_SHIFT:
          ShiftState &= ~ssShift;
          return 0;
      case VK_CONTROL:
          ShiftState &= ~ssCtrl;
          return 0;
      default:
          return 0;
      }
      return 0;

    case WM_TIMER:
      Form1->TimerTimer(NULL);
      return 0;

    case WM_CLOSE:
      Form1->Close();
      return 0;

    default:
      return DefWindowProc(hwnd,message,wParam,lParam);
  }
} 

void ShowMessage(AnsiString message) {
    MessageBox(toplevel, message.c_str(), prog, MB_OK);
}

static char DialogFuncResult[256];
BOOL CALLBACK DialogFunc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch(message) {
    case WM_INITDIALOG:
	/* for debug
	printf("Hello\n");
	*/
	return 0;
    case WM_COMMAND:
	if (LOWORD(wParam) == ID_OK && HIWORD(wParam) == BN_CLICKED) {
	    GetDlgItemText(hwnd, ID_Edit,
			   DialogFuncResult, sizeof(DialogFuncResult));
	    EndDialog(hwnd, (int)DialogFuncResult);
	    return 1;
	} else	if (LOWORD(wParam) == ID_Cancel && HIWORD(wParam) == BN_CLICKED)  {
	    EndDialog(hwnd, (int)DialogFuncResult);
	    return 1;
	} else {
	    return 0;
	}
    case WM_CLOSE:
	EndDialog(hwnd, (int)DialogFuncResult);
	return 1;

    default:
	return 0;
    }
}

#define AsType(ty, p, v)  *((ty *)p) = (v); p+=(sizeof(ty))/(sizeof(char))

AnsiString InputBox(AnsiString atitle, AnsiString amessage, AnsiString ainit)  {
    static char dlgtmpl[2048];
    WCHAR utitle[256], umessage[256],  uinit[256];
    int stitle, smessage, sinit;
    static WCHAR uok[] = {' ', 'O', 'K', ' ', '\0'},
	         ucancel[] = {'C', 'a', 'n', 'c', 'e', 'l', '\0'};
    int sok = 4, scancel = 6;
    static DLGTEMPLATE 
      dlg =	
	{WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_CENTER | DS_MODALFRAME
	   /* style */,
	 0 /* exStyle */, 0 /* cdit */,
	 0 /* x */, 0 /* y */, 136 /* cx */, 42 /* cy */};
    static DLGITEMTEMPLATE
	msg = {WS_CHILD | WS_VISIBLE | SS_LEFT /* style */, 0 /* exStyle */,
	       5 /* x */, 3 /* y */, 126 /* cx */, 9 /* cy */,
	       ID_Message /* id */},
        inp = {WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_GROUP | ES_LEFT /* style */, 
	       WS_EX_CLIENTEDGE /* exStyle */, 
	       5 /* x */, 13 /* y */, 126 /* cx */, 9 /* cy */,
	       ID_Edit /* id */},
        okb = {WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON /* style */,
	       0 /* exStyle */, 
	       32 /* x */, 27 /* y */, 34 /* cx */, 10 /* cy */,
	       ID_OK /* id */}, 
        ccb = {WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON /* style */,
	       0 /* exStyle */,
	       68 /* x */, 27 /* y */, 34 /* cx */, 10	/* cy */, 
	       ID_Cancel /* id */}; 
	   
    char* ptr = dlgtmpl;
    WORD *nItems;
    char *title, *message, *init;
    title = atitle.c_str();
    message = amessage.c_str();
    init = ainit.c_str();
    
    stitle = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, title,
				 strlen(title), utitle, sizeof(utitle));
    smessage = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, message,
				   strlen(message),
				     umessage, sizeof(umessage));
    sinit = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, init, strlen(init),
				uinit, sizeof(uinit));

    memcpy(ptr, &dlg, sizeof(DLGTEMPLATE));
    nItems = &(((DLGTEMPLATE *)ptr)->cdit);
    ptr+=sizeof(DLGTEMPLATE);
    AsType(WORD, ptr, 0x0000);	/* menu */
    AsType(WORD, ptr, 0x0000);	/* windowClass */
    memcpy(ptr, utitle, (sizeof(WCHAR)*stitle)); 	/* タイトル */
    ptr+=sizeof(WCHAR)*stitle;
    AsType(WCHAR, ptr, 0);
    while (((int)ptr)%sizeof(DWORD)) {ptr++;}	/* DWORD boundary */
    /* Dialog Box end */

    /* static （メッセージ）*/
    (*nItems)++;
    memcpy(ptr, &msg, sizeof(DLGITEMTEMPLATE));
    ptr+=sizeof(DLGITEMTEMPLATE);
    AsType(WORD, ptr, 0xFFFF); 	/* windowClass */
    AsType(WORD, ptr, 0x0082);	/* = static */
    memcpy(ptr, umessage, (sizeof(WCHAR)*smessage)); 	/* title */
    ptr+=sizeof(WCHAR)*smessage;
    AsType(WCHAR, ptr, 0);
    while (((int)ptr)%sizeof(DWORD)) {ptr++;}	/* DWORD boundary */
    /* Creation Data なし */
    /* static 終わり */

    /* edit （入力するところ）*/
    (*nItems)++;
    memcpy(ptr, &inp, sizeof(DLGITEMTEMPLATE));
    ptr+=sizeof(DLGITEMTEMPLATE);
    AsType(WORD, ptr, 0xFFFF); 	/* windowClass */
    AsType(WORD, ptr, 0x0081);	/* = edit */
    memcpy(ptr, uinit, (sizeof(WCHAR)*sinit)); 	/* 初期値 */
    ptr+=sizeof(WCHAR)*sinit;
    AsType(WCHAR, ptr, 0);
    while (((int)ptr)%sizeof(DWORD)) {ptr++;}	/* DWORD boundary */
    /* NO Creation Data */
    /* edit 終わり */

    /* OK ボタン */
    (*nItems)++;
    memcpy(ptr, &okb, sizeof(DLGITEMTEMPLATE));
    ptr+=sizeof(DLGITEMTEMPLATE);
    AsType(WORD, ptr, 0xFFFF); 	/* windowClass */
    AsType(WORD, ptr, 0x0080);	/* = button */
    memcpy(ptr, uok, (sizeof(WCHAR)*sok)); 	/* "OK" */
    ptr+=sizeof(WCHAR)*sok;
    AsType(WCHAR, ptr, 0);
    while (((int)ptr)%sizeof(DWORD)) {ptr++;}	/* DWORD boundary */
    AsType(DWORD, ptr, 0);	/* Creation Data なし */
    /* ok 終わり */
    
   /* Cancel ボタン */
    (*nItems)++;
    memcpy(ptr, &ccb, sizeof(DLGITEMTEMPLATE));
    ptr+=sizeof(DLGITEMTEMPLATE);
    AsType(WORD, ptr, 0xFFFF); 	/* windowClass */
    AsType(WORD, ptr, 0x0080);	/* = button */
    memcpy(ptr, ucancel, (sizeof(WCHAR)*scancel)); 	/* "Cancel" */
    ptr+=sizeof(WCHAR)*scancel;
    AsType(WCHAR, ptr, 0);
    while (((int)ptr)%sizeof(DWORD)) {ptr++;}	/* DWORD boundary */
    AsType(DWORD, ptr, 0);	/* Creation Data なし　*/
    /* cancel 終わり */

    memcpy(DialogFuncResult, init, min((int)sizeof(DialogFuncResult), sinit+1));
     
    return AnsiString((char *)DialogBoxIndirect(hInst, (LPCDLGTEMPLATE)dlgtmpl,
						toplevel, (DLGPROC)DialogFunc));
}

// -------------------------------------------------------------------
static COLORREF MyGetSysColor(TColor c) {
    if (c&0x80000000) {
	return GetSysColor(ColorTable[c&0xFF]);
    } else {
	return c;
    }
}

inline POINT Point(int lx, int ly) {
  POINT ret;
  ret.x = lx;  ret.y = ly;
  return ret;
}

inline TRect Rect(int ALeft, int ATop, int ARight, int ABottom) {
    TRect ret;
    ret.Left=ALeft; ret.Top=ATop;
    ret.Right=ARight; ret.Bottom=ABottom;
    return ret;
}

void TTimer::SetEnabled(bool b) {
    if (b==Enabled) {
        return;
    } else if (Enabled=b) /* ==ではない! */ {
        id = SetTimer(toplevel, ID_Timer, Interval, NULL);
    } else {
        KillTimer(toplevel, id);
    }
}

void TTimer::SetInterval(UINT i) {
    if (Interval==i) {
        return;
    } else if (Interval=i, Enabled)  {
	KillTimer(toplevel, id); /* 一度殺す */
	id = SetTimer(toplevel, ID_Timer, Interval, NULL);
    }
}

void TForm::Close() {
    DestroyWindow(Handle);
    PostQuitMessage(0);
} 

void TForm::Repaint() {
    TColor oldc = Canvas->Brush->Color;
    TBrushStyle olds = Canvas->Brush->Style;
    Canvas->Brush->Color= Color;
    Canvas->Brush->Style = bsSolid;
    Canvas->FillRect(Rect(0, 0, Width, Height));
    InvalidateRect(Handle, NULL, false);
    Canvas->Brush->Color = oldc;
    Canvas->Brush->Style = olds;
} 

void TPen::Update(HDC hdc) {
    HPEN old;
    SetROP2(hdc, PenModeTable[Mode]);
    Handle = CreatePen(PenStyleTable[Style], Width, MyGetSysColor(Color));
    old = (HPEN)SelectObject(hdc, Handle);
    DeleteObject(old);
}

void TFont::Update(HDC hdc) {
    HFONT old;
    SetTextColor(hdc, MyGetSysColor(Color));
    Handle = CreateFont(Size, 0, 0, 0, (Style&fsBold)?700:400,
    		    Style&fsItalic, Style&fsUnderline, Style&fsStrikeOut,
    		    SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS,
    		    CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE,
    		    "MSMINCHO");
    old = (HFONT)SelectObject(hdc, Handle);
    DeleteObject(old);
}

void TBrush::Update(HDC hdc) {
    HBRUSH old;
    
    switch (Style) {
    case bsSolid:
        Handle = CreateSolidBrush(MyGetSysColor(Color));
	SetBkMode(hdc, OPAQUE);
	SetBkColor(hdc, MyGetSysColor(Color));
        break;
    case bsClear:
        Handle = (HBRUSH)GetStockObject(HOLLOW_BRUSH);
	SetBkMode(hdc, TRANSPARENT);
        break;
    default:
        Handle = CreateHatchBrush(BrushStyleTable[Style], MyGetSysColor(Color));
	SetBkMode(hdc, TRANSPARENT);
        break;
    }
    old = (HBRUSH)SelectObject(hdc, Handle);
    DeleteObject(old);
}

void TCanvas::Update() {
    Font->Update(Handle);
    Pen->Update(Handle);
    Brush->Update(Handle);
}

void TCanvas::TextOut(int x, int y, AnsiString str) {
#ifndef NOAUTOUPDATE
    Font->Update(Handle);
    Brush->Update(Handle);
#endif	
    ::TextOut(Handle, x, y, str.c_str(), str.Length());
}

void TCanvas::LineTo(int x, int y) {
#ifndef NOAUTOUPDATE
    Pen->Update(Handle);
#endif	
    ::LineTo(TCanvas::Handle, x, y);
}

void TCanvas::MoveTo(int x, int y) {
    ::MoveToEx(Handle, x, y, NULL);
}

void TCanvas::Rectangle(int x1, int y1, int x2, int y2) {
#ifndef NOAUTOUPDATE
    Brush->Update(Handle);
    Pen->Update(Handle);
#endif	
    ::Rectangle(Handle, x1, y1, x2, y2);
}

void TCanvas::FrameRect(TRect rect) {
#ifndef NOAUTOUPDATE
    Brush->Update(Handle);
#endif	
    ::FrameRect(Handle, (RECT *)(&rect), Brush->Handle);	
}

void TCanvas::FillRect(TRect rect) {
#ifndef NOAUTOUPDATE
    Brush->Update(Handle);
#endif	
    ::FillRect(Handle, (RECT *)(&rect), Brush->Handle);
}

void TCanvas::Ellipse(int x1, int y1, int x2, int y2) {
#ifndef NOAUTOUPDATE
    Brush->Update(Handle);
    Pen->Update(Handle);
#endif	
    ::Ellipse(Handle, x1, y1, x2, y2);
}

void TCanvas::Arc(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
#ifndef NOAUTOUPDATE
    Pen->Update(Handle);
#endif	
    ::Arc(Handle, x1, y1, x2, y2, x3, y3, x4, y4);
}

void TCanvas::Chord(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
#ifndef NOAUTOUPDATE
    Brush->Update(Handle);
    Pen->Update(Handle);
#endif	
    ::Chord(Handle, x1, y1, x2, y2, x3, y3, x4, y4);
}

void TCanvas::Pie(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
#ifndef NOAUTOUPDATE
    Brush->Update(Handle);
    Pen->Update(Handle);
#endif	
    ::Pie(Handle, x1, y1, x2, y2, x3, y3, x4, y4);
}

void TCanvas::Polyline(const POINT *Points, const int n) {
#ifndef NOAUTOUPDATE
    Pen->Update(Handle);
#endif	
    ::Polyline(Handle, Points, n);
}

void TCanvas::Polygon(const POINT *Points, const int n) {
#ifndef NOAUTOUPDATE
    Brush->Update(Handle);
    Pen->Update(Handle);
#endif	
    ::Polygon(Handle, Points, n);
}

void TCanvas::RoundRect(int x1, int y1, int x2, int y2, int x3, int y3) {
#ifndef NOAUTOUPDATE
    Brush->Update(Handle);
    Pen->Update(Handle);
#endif	
    ::RoundRect(Handle, x1, y1, x2, y2, x3, y3);
}

void TCanvas::PolyBezier(const POINT *Points, const int n) {
#ifndef NOAUTOUPDATE
    Pen->Update(Handle);
#endif	
    ::PolyBezier(Handle, Points, n);
}

void TCanvas::PolyBezierTo(const POINT *Points, const int n) {
#ifndef NOAUTOUPDATE
    Pen->Update(Handle);
#endif	
    ::PolyBezierTo(Handle, Points, n);
}


