#include "window.hpp"
#include "control.hpp"

ATOM aProp, aPropHigh;

#if defined( PTR16) || defined (WIN32)
  #define PUTPWIN(hWnd,this) \
    SetProp(hWnd,(LPSTR)MAKELONG(aProp,0),(HANDLE)this)
  #define GETPWIN(hWnd) \
    (Window *)GetProp(hWnd,(LPSTR)MAKELONG(aProp,0))
  #define DELPWIN(hWnd) \
    (Window *)RemoveProp(hWnd,(LPSTR)MAKELONG(aProp,0))
#else
  #define PUTPWIN(hWnd,this)\
    SetProp(hWnd,(LPSTR)MAKELONG(aProp,0),(HANDLE)this);\
    SetProp(hWnd,(LPSTR)MAKELONG(aPropHigh,0),\
    (HANDLE)((DWORD)this >>16))
  #define GETPWIN(hWnd) (Window *)\
    ((DWORD)GetProp(hWnd,(LPSTR)MAKELONG(aProp,0))|\
    ((DWORD)GetProp(hWnd,(LPSTR)MAKELONG(aPropHigh,0)) <<16))
  #define DELPWIN(hWnd) (Window *)\
    ((DWORD)RemoveProp(hWnd,(LPSTR)MAKELONG(aProp,0))|\
    ((DWORD)RemoveProp(hWnd,(LPSTR)MAKELONG(aPropHigh,0)) <<16))
#endif // PTR16

// Routines to associate instance pointers with windows
void PutWin(HWND hWnd, Window * pWin) { PUTPWIN(hWnd,pWin);};
Window * GetWin(HWND hWnd) {return GETPWIN(hWnd);};
Window*  DelWin(HWND hWnd) {return DELPWIN(hWnd);};

//#ifdef WIN32
 // WNDPROC Window::Handler;
//#else
  FARPROC Window::Handler;        // proc instance for CPPWinProc
//#endif
HANDLE Window::hAccel;          // Current accelerator table

//  Set window message proc to CPPWinProc
void Window::SetHandler()
{
//#ifdef WIN32
//  DefaultHandler = (WNDPROC) GetWindowLong(hWnd,GWL_WNDPROC);
//#else
  DefaultHandler = (FARPROC) GetWindowLong(hWnd,GWL_WNDPROC);
//#endif
  SetWindowLong(hWnd,GWL_WNDPROC,(LONG) Handler);
PUTPWIN(hWnd,(HANDLE) this);
}

// Create a window
void Window::Create(LPSTR caption, LONG style, int x, int y,
		int width, int height, HWND hPwnd, HMENU hMenu)
{

  WNDCLASS wc;
  wc.style         = 0;
  wc.lpfnWndProc   = (WNDPROC) DefWindowProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = hInst;
  wc.hIcon         = LoadIcon(NULL,IDI_APPLICATION);
  wc.hCursor       = LoadCursor(NULL,IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  wc.lpszMenuName  = 0;

  hWnd = CreateWindow(Register(wc),
                        caption,style,x,y,width,height,
                        hPwnd,hMenu,hInst,NULL);

  if (!hWnd) Error(IDS_APPLERROR, (LPSTR)"Window Creation");
  SetHandler();
}

Window::Window() {
  hWnd=0;
  HorzScrollBar=VertScrollBar=NULL;
  if (!Handler) {
  #ifdef WIN32
    Handler = (FARPROC)CPPWinProc;
  #else
    Handler = MakeProcInstance((FARPROC)CPPWinProc, hInst);
  #endif
    aProp=AddAtom("pWindow");
    aPropHigh=AddAtom("pWindowHigh");
  }
}

LPSTR Window::Register(WNDCLASS& wc) {
	Error(IDS_APPLERROR,(LPSTR)"No Register method");
return NULL;
}

Window::~Window()
{
  if (HorzScrollBar) delete HorzScrollBar;
  if (VertScrollBar) delete VertScrollBar;

  if (hWnd) {
    Show(SW_HIDE);
    //Destroy instances associated with child windows
    HWND hCwnd= GetWindow (hWnd, GW_CHILD);
    while ( hCwnd ){
       Window * pWin=(Window*) GETPWIN(hCwnd);
       do {
            hCwnd=GetWindow(hCwnd,GW_HWNDNEXT);
       } while (hCwnd  && GetParent(hCwnd)!=hWnd);
       if (pWin) delete pWin;
    }

    if (DELPWIN(hWnd) && DefaultHandler)
      SetWindowLong(hWnd,GWL_WNDPROC,(LONG) DefaultHandler);
    //if this is the top level window quit the application
    if (!GetParent(hWnd)) PostQuitMessage(0);
    DestroyWindow(hWnd);
    hWnd=0;
  }
}

// The application message loop
int Window::MessageLoop()
{
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
      if(   msg.message >=WM_KEYFIRST 
         && msg.message <=WM_KEYLAST) {
         Window * pWin=GETPWIN(GetParent(msg.hwnd));
         if (pWin && pWin->DialogMessage(msg)) continue;
      }
      if (!hAccel ||
        !TranslateAccelerator (hWnd, hAccel, &msg)){
        TranslateMessage (&msg);
        DispatchMessage (&msg);
      }
    }
    return msg.wParam;
}


// The window message function, translates messages
// to calls to virtual functions
LONG Window::MessageProc( HWND hWnd, UINT msg, Event& evt)
{
  POINT Pt;

  switch (msg){

    case WM_QUERYENDSESSION:
      // OK to end Windows session?
      return QueryClose();

    case WM_CLOSE:
      // if OK delete this instance
      if (QueryClose ()) delete this;
      return TRUE;
      
    case WM_DESTROY:
      if (DELPWIN(hWnd) && DefaultHandler)
        SetWindowLong(hWnd,GWL_WNDPROC,(LONG) DefaultHandler);
      Window::hWnd=0;
      delete this;
      break;
          
    case WM_INITMENU:
      return InitMenu((HMENU)evt.wParam);

    case WM_SIZE:
      return Size(LOWORD(evt.lParam),
                  HIWORD(evt.lParam),evt.wParam);

    case WM_PAINT:
      return Paint();

    case WM_SETFOCUS:
      return SetFocus((HWND)evt.wParam);

    case WM_KILLFOCUS:
      return KillFocus((HWND)evt.wParam);

    case WM_CHAR:
      return Char(evt.wParam, LOWORD(evt.lParam));

    case WM_KEYDOWN:
      if (KeyDown(evt.wParam, 
         *(KEYCODES*)&evt.lParam)) return TRUE;
      if (VertScrollBar && 
        VertScrollBar->KeyDown(evt.wParam, 
        *(KEYCODES*)&evt.lParam)) return TRUE;
      if (HorzScrollBar && 
        HorzScrollBar->KeyDown(evt.wParam, 
        *(KEYCODES*)&evt.lParam)) return TRUE;
      return FALSE;

    case WM_LBUTTONDOWN:
      Pt.x=LOWORD(evt.lParam); Pt.y=HIWORD(evt.lParam);
      return LButtonDown(Pt, evt.wParam);

    case WM_LBUTTONUP:
      Pt.x=LOWORD(evt.lParam); Pt.y=HIWORD(evt.lParam);
      return LButtonUp(Pt, evt.wParam);

    case WM_LBUTTONDBLCLK:
      Pt.x=LOWORD(evt.lParam); Pt.y=HIWORD(evt.lParam);
      return LButtonDblClk(Pt, evt.wParam);
      
    case WM_RBUTTONDOWN:
      Pt.x=LOWORD(evt.lParam); Pt.y=HIWORD(evt.lParam);
      return RButtonDown(Pt, evt.wParam);

    case WM_RBUTTONUP:
      Pt.x=LOWORD(evt.lParam); Pt.y=HIWORD(evt.lParam);
      return RButtonUp(Pt, evt.wParam);
      
    case WM_MOUSEMOVE:
      Pt.x=LOWORD(evt.lParam); Pt.y=HIWORD(evt.lParam);
      return MouseMove(Pt, evt.wParam);
      
    case WM_MOUSEACTIVATE:
      MouseActivate((HWND)evt.wParam, LOWORD(evt.lParam));
      if(DefaultHandler)
	return CallWindowProc (DefaultHandler,
	       hWnd, msg, evt.wParam, evt.lParam);
      else return FALSE;


    case WM_COMMAND:
    {
      WORD wNotifyCode;         // notification code
      WORD wID;                 // item, control, or accelerator ID
      HWND hWndCtl;             // handle of control

#ifdef WIN32
      wNotifyCode = HIWORD(evt.wParam); 
      wID = LOWORD(evt.wParam); 
      hWndCtl = (HWND) evt.lParam; 
#else
      wID = (int) evt.wParam; 
      hWndCtl = (HWND) LOWORD(evt.lParam);
      wNotifyCode = HIWORD(evt.lParam); 
#endif
      if (hWndCtl) {
        // if message is from a control, call the control method
	Control * pControl =
	  (Control*) (void*)GETPWIN(hWndCtl);
	if (pControl)
	  return pControl->CodeHandler(wNotifyCode);
      }
      return Command(wID,wNotifyCode, hWndCtl);
    }

    case WM_DRAWITEM:
    {
      // Call DrawItem method in control
      Control * pControl=
	(Control*) (void*)GETPWIN((HWND)
	  (((DRAWITEMSTRUCT FAR *) evt.lParam)->hwndItem));
      if (pControl)
        pControl->DrawItem((DRAWITEMSTRUCT FAR * )evt.lParam);
      break;
    }

#ifdef WIN32       
    case  WM_CTLCOLORMSGBOX:
    case  WM_CTLCOLOREDIT:
    case  WM_CTLCOLORLISTBOX:
    case  WM_CTLCOLORBTN:
    case  WM_CTLCOLORDLG:
    case  WM_CTLCOLORSCROLLBAR:
    case  WM_CTLCOLORSTATIC:
#else
    case  WM_CTLCOLOR:
#endif
    {
      // Call CtlColor method in control
      Control * pControl=
         (Control*) (void*)GETPWIN((HWND)evt.lParam);
      HBRUSH Brush=NULL;
      if (pControl) Brush=pControl->CtlColor((HDC)evt.wParam);
      if (Brush) return (LONG)Brush;
      if(DefaultHandler)
	return CallWindowProc (DefaultHandler,
	       hWnd, msg, evt.wParam, evt.lParam);
      else return FALSE;
    }
       
    case WM_MEASUREITEM:
    {
      // Call MeasureItem method in control
      MEASUREITEMSTRUCT FAR * pMs=
	  (MEASUREITEMSTRUCT FAR *)evt.lParam;
      Control * pControl=
	(Control*) (void*)GETPWIN(GetDlgItem(hWnd,pMs->CtlID));
      if (pControl) pControl->MeasureItem(pMs);
      else {
	pMs->itemHeight=ItemMeasurement::itemHeight;
	pMs->itemWidth=ItemMeasurement::itemWidth;
      }
      break;
    }

    case WM_HSCROLL:
    {
      HWND hCtl;
      WORD nPos;
#ifdef WIN32
      hCtl=(HWND)evt.lParam;
      nPos=HIWORD(evt.wParam);
#else
      hCtl=(HWND)HIWORD(evt.lParam);
      nPos=LOWORD(evt.lParam);
#endif
      if (hCtl){
	Control * pScroll=
	  (Control *)(void*)GETPWIN(hCtl);
	if (pScroll)
	  pScroll->CodeHandler((WORD)evt.wParam,nPos);
      }
      else  if (HorzScrollBar){ 
	  ((Control *)(void*)HorzScrollBar)
	    ->CodeHandler((WORD)evt.wParam,nPos);
      }
      break;
    }

    case WM_VSCROLL:
    {
      HWND hCtl;
      WORD nPos;
#ifdef WIN32
      hCtl=(HWND)evt.lParam;
      nPos=HIWORD(evt.wParam);
#else
      hCtl=(HWND)HIWORD(evt.lParam);
      nPos=LOWORD(evt.lParam);
#endif
      if (hCtl){
	Control * pScroll=
	  (Control *)(void*)GETPWIN(hCtl);
	if (pScroll)
	  pScroll->CodeHandler((WORD)evt.wParam,nPos);
      }
      else  if (VertScrollBar){ 
	  ((Control *)(void*)VertScrollBar)
	    ->CodeHandler((WORD)evt.wParam,nPos);
      }
      break;
    }

    case WM_USER_DELETE:
#ifdef PTR16
      delete (WinObj *)(WORD)evt.lParam;
#else
      delete (WinObj *)evt.lParam;
#endif
      break;

    case WM_USER_DESTROY:
      DestroyWindow((HWND)evt.wParam);
      break;

    default:
      if (DefaultHandler)
	return CallWindowProc (DefaultHandler,
	       hWnd, msg, evt.wParam, evt.lParam);
  } // switch
  return FALSE;
} // MessageProc

BOOL Window::Size(UINT Width, UINT Height, UINT Type) {
  if (!DefaultHandler) return FALSE;
  return CallWindowProc (DefaultHandler,
         hWnd, WM_SIZE, Type, MAKELONG(Width, Height));
}

BOOL Window::Paint() {
  if (!DefaultHandler) return FALSE;
  return CallWindowProc (DefaultHandler, hWnd, WM_PAINT, 0,0);
}

BOOL Window::Command( UINT Id, UINT Code, HWND hControl) {
  if (!DefaultHandler) return FALSE;
#ifdef WIN32
  return CallWindowProc (DefaultHandler,
    hWnd, WM_COMMAND, MAKELONG(Id, Code), (DWORD)hControl);
#else
  return CallWindowProc (DefaultHandler,
    hWnd, WM_COMMAND, Id, MAKELONG(hControl, Code));
#endif
}

LONG CALLBACK CPPWinProc(HWND hWnd, UINT Message,
			   UINT wParam,LONG lParam)
{
  Window* pWin = GETPWIN(hWnd);
  Event evt(wParam,lParam);
  if (pWin) {
    // There is a C++ instance for the window
    return pWin->MessageProc(hWnd,Message,evt);
  }
  else if (Message==WM_INITDIALOG && lParam) {
       // Associate a dialog box C++ instance
       // with the window
       PUTPWIN(hWnd,lParam);
#ifdef PTR16
       return ((Window*)(WORD)lParam)
         ->MessageProc(hWnd,Message,evt);
#else
       return ((Window*)lParam)
         ->MessageProc(hWnd,Message,evt);
#endif
  }
  else if (Message== WM_MEASUREITEM) {
    // return the current owner-draw item size
    MEASUREITEMSTRUCT FAR * pMs=
      (MEASUREITEMSTRUCT  FAR *)lParam;
    pMs->itemHeight=ItemMeasurement::itemHeight;
    pMs->itemWidth=ItemMeasurement::itemWidth;
    return TRUE;
  }
  else return FALSE;
} // CPPWinProc

void Window::Move(RECT * rc, BOOL repaint) {
  MoveWindow (hWnd,
	      rc->left,
	      rc->top,
	      rc->right-rc->left,
	      rc->bottom-rc->top,
	      repaint);
}
