#include <common/guiwnd.hpp>
#include <common/commdlg.hpp>
#include <common/purehdc.hpp>
#include <common/instance.hpp>
#include <common/string.hpp>
#include <common/rect.hpp>
#include <common/point.hpp>
#include <common/gdipoint.hpp>
#include <common/puremenu.hpp>
#include <common/font.hpp>

GUIWindow::~GUIWindow()
{
  destroy();
}

bool GUIWindow::insertModelessDialog(GUIWindow &modelessDialog)
{
  if(!isValid()||!modelessDialog.isValid())return false;
  mModelessDialogs.insert(&WindowPointer(&modelessDialog));
	return true;
}

void GUIWindow::removeModelessDialog(GUIWindow &modelessDialog)
{
  if(!isValid())return;
  mModelessDialogs.remove(&WindowPointer(&modelessDialog));
}

GUIWindow &GUIWindow::operator=(const GUIWindow &/*someGUIWindow*/)
{ // private implementation
  return *this;
}

GUIWindow &GUIWindow::operator=(HWND /*hWnd*/)
{ // private implementation
	return *this;
}

BOOL GUIWindow::validate(const Rect &validRect)
{
  if(!isValid())return FALSE;
	return ::ValidateRect(mhWnd,(RECT*)((Rect&)validRect));
}

WORD GUIWindow::width(void)const
{
  Rect winRect;
	clientRect(winRect);
	return winRect.right();
}

WORD GUIWindow::height(void)const
{
  Rect winRect;
	clientRect(winRect);
	return winRect.bottom();
}

void GUIWindow::windowRect(Rect &windowRect)const
{
	if(!isValid())return;
  ::GetWindowRect(*this,(RECT FAR*)windowRect);
}

void GUIWindow::clientRect(Rect &clientRect)const
{
  if(!isValid())return;
	::GetClientRect(*this,(RECT FAR*)clientRect);
}

void GUIWindow::setFont(const Font &someFont,BOOL redraw)const
{
  if(!isValid())return;
	sendMessage(WM_SETFONT,(WPARAM)(HFONT)someFont,MAKELPARAM(redraw,0));
}

WORD GUIWindow::moveWindow(const Rect &winRect,WORD repaint)const
{
  if(!isValid())return FALSE;
	return ::MoveWindow(mhWnd,winRect.left(),winRect.top(),(winRect.right()-winRect.left())+1,(winRect.bottom()-winRect.top())+1,repaint);
}

WORD GUIWindow::moveWindow(int left,int top,int right,int bottom,WORD repaint)const
{
  if(!isValid())return FALSE;
	return ::MoveWindow(mhWnd,left,top,right,bottom,repaint);
}

void GUIWindow::move(const GDIPoint &topLeftPoint,bool repaint)
{
  move(Point(topLeftPoint.x(),topLeftPoint.y()),repaint);
}

void GUIWindow::move(const Point &topLeftPoint,bool repaint)
{
	RECT windowRect;

	if(!(HWND)*this)return;
	::GetWindowRect(*this,&windowRect);
	WORD windowWidth((windowRect.right-windowRect.left));
	WORD windowHeight((windowRect.bottom-windowRect.top));
	::MoveWindow(*this,topLeftPoint.x(),topLeftPoint.y(),windowWidth,windowHeight,TRUE);
  if(repaint)::UpdateWindow(*this);
}

void GUIWindow::size(const Point &dimensionPoint)
{
	Rect windowRect;

	if(!isValid())return;
	::GetWindowRect(*this,(RECT FAR *)windowRect);
	::MoveWindow(*this,windowRect.left(),windowRect.top(),dimensionPoint.x(),dimensionPoint.y(),FALSE);
}

BOOL GUIWindow::setCaption(const String &captionString)const
{
  if(!isValid())return FALSE;
  return ::SetWindowText(*this,(LPSTR)captionString);
}

BOOL GUIWindow::setWindowPos(const GDIPoint &startPos,int width,int height)const
{
  if(!isValid())return FALSE;
	return ::SetWindowPos(*this,HWND_BOTTOM,startPos.x(),startPos.y(),width,height,SWP_NOZORDER);
}

int GUIWindow::windowText(String &strText)const
{
  if(!isValid())return FALSE;
  strText.reserve(String::MaxString*2);
  return ::GetWindowText(*this,strText,String::MaxString*2);
}

WORD GUIWindow::clientToScreen(Rect &clientRect)const
{
  GDIPoint screenPoint;

	if(!isValid())return FALSE;
	screenPoint.x(clientRect.left());
	screenPoint.y(clientRect.top());
	::ClientToScreen(mhWnd,&((POINT&)screenPoint));
	clientRect.left(screenPoint.x());
	clientRect.top(screenPoint.y());
	screenPoint.x(clientRect.right());
	screenPoint.y(clientRect.bottom());
	::ClientToScreen(mhWnd,&((POINT&)screenPoint));
	clientRect.right(screenPoint.x());
	clientRect.bottom(screenPoint.y());
	return TRUE;
}

WORD GUIWindow::screenToClient(Rect &clientRect)const
{
  GDIPoint screenPoint;

	if(!isValid())return FALSE;
	screenPoint.x(clientRect.left());
	screenPoint.y(clientRect.top());
	::ScreenToClient(mhWnd,&((POINT&)screenPoint));
	clientRect.left(screenPoint.x());
	clientRect.top(screenPoint.y());
	screenPoint.x(clientRect.right());
	screenPoint.y(clientRect.bottom());
	::ScreenToClient(mhWnd,&((POINT&)screenPoint));
	clientRect.right(screenPoint.x());
	clientRect.bottom(screenPoint.y());
	return TRUE;
}

BOOL GUIWindow::screenToClient(GDIPoint &somePoint)const
{
  if(!isValid())return FALSE;
	return ::ScreenToClient(mhWnd,&((POINT&)somePoint));
}

BOOL GUIWindow::clientToScreen(GDIPoint &somePoint)const
{
  if(!isValid())return FALSE;
	return ::ClientToScreen(mhWnd,&((POINT&)somePoint));
}

WORD GUIWindow::killTimer(WORD timerIdentifier)
{
	if(!isValidHandler(VectorHandler::TimerHandler,timerIdentifier))return FALSE;
	::KillTimer((HWND)*this,timerIdentifier);
	return FALSE;
}

void GUIWindow::destroy(void)
{
	if(!isValid())return;
  if(disposition()&Destroy)::DestroyWindow((HWND)*this);
	setHandle(0);
}

void GUIWindow::postQuitMessage(int exitCode)const
{
  ::PostQuitMessage(exitCode);
}

void GUIWindow::yieldTask(void)const
{
	bool isDialogMessage;
	MSG msg;

	if(::PeekMessage(&msg,0,0,0,PM_REMOVE))
	{
	  isDialogMessage=false;
	  for(int mindex=0;mindex<mModelessDialogs.size()&&!isDialogMessage;mindex++)
		if(::IsDialogMessage(*((((GUIWindow&)*this).mModelessDialogs)[mindex]),&msg))
		  isDialogMessage=true;
		if(!isDialogMessage)
		{
		  if(mAccelerator.isOkay()&&mAccelerator.translate(*this,msg))
			{
			  ::DispatchMessage(&msg);
			}
			else
			{
	      ::TranslateMessage(&msg);
	      ::DispatchMessage(&msg);
			}
		}
	}
}

int GUIWindow::messageLoop(void)const
{
	bool isDialogMessage;
  MSG msg;

	while(::GetMessage(&msg,NULL,0,0))
	{
	  isDialogMessage=false;
	  for(int mindex=0;mindex<mModelessDialogs.size()&&!isDialogMessage;mindex++)
		if(::IsDialogMessage(*((((GUIWindow&)*this).mModelessDialogs)[mindex]),&msg))isDialogMessage=true;
		if(isDialogMessage)continue;
    if(!mAccelerator.translateMDISysAccelerator(*this,msg)&&!mAccelerator.translate(*this,msg))
		{
      ::TranslateMessage(&msg);
      ::DispatchMessage(&msg);
		}
		else 
		{
			::DispatchMessage(&msg);
		}
	}
	return msg.wParam;
}

LONG GUIWindow::handlePaintMessage(HWND hWnd,WORD /*message*/,WPARAM wParam,LPARAM /*lParam*/)
{
	PAINTSTRUCT paintStruct;
	LONG returnCode;

	::BeginPaint(hWnd,(PAINTSTRUCT FAR *)&paintStruct);
	PaintInformation *lpPaintInformation=new PaintInformation(PureDevice(paintStruct.hdc));
	(PAINTSTRUCT&)*lpPaintInformation=paintStruct;
	returnCode=callHandlers(VectorHandler::PaintHandler,CallbackData(wParam,(LPARAM)(LONG)lpPaintInformation));
	delete lpPaintInformation;
	::EndPaint(hWnd,(PAINTSTRUCT FAR*)&paintStruct);
	return returnCode;
}

HWND GUIWindow::findWindow(const String &strWindowName)const
{
  if(!isValid())return FALSE;
	return ::FindWindowEx(mhWnd,0,0,(char*)(String&)strWindowName);
}

HWND GUIWindow::createWindow(const String &className,const String &windowName,DWORD dwStyle,const Rect &initRect,HWND hParentWnd,HMENU hMenu,HINSTANCE hInstance,void FAR *lpInstanceData)
{
	destroy();
	if(!hInstance)hInstance=processInstance();
	mhWnd=::CreateWindow((LPSTR)className,(LPSTR)windowName,dwStyle,initRect.left(),initRect.top(),
		initRect.right(),initRect.bottom(),hParentWnd,hMenu,hInstance,lpInstanceData);
	return mhWnd;
}

HWND GUIWindow::createWindow(DWORD dwExStyle,const String &className,const String &windowName,DWORD dwStyle,const Rect &initRect,HWND hParentWnd,HMENU hMenu,HINSTANCE hInstance,void FAR *lpInstanceData)
{
  destroy();
	if(!hInstance)hInstance=processInstance();
	mhWnd=::CreateWindowEx(dwExStyle,(LPSTR)className,(LPSTR)windowName,dwStyle,initRect.left(),initRect.top(),initRect.right(),initRect.bottom(),hParentWnd,hMenu,hInstance,lpInstanceData);
	return mhWnd;
}

LRESULT GUIWindow::postMessage(HWND hPostWindow,UINT message,WPARAM wParam,LPARAM lParam)const
{
	if(HWND_BROADCAST!=hPostWindow&&!::IsWindow(hPostWindow))return FALSE;
  return ::PostMessage(hPostWindow,message,wParam,lParam);
}

LRESULT GUIWindow::sendMessage(HWND hPostWindow,UINT message,WPARAM wParam,LPARAM lParam)const
{
	if(HWND_BROADCAST!=hPostWindow&&!::IsWindow(hPostWindow))return FALSE;
  return ::SendMessage(hPostWindow,message,wParam,lParam);
}

LRESULT GUIWindow::sendMessage(UINT message,WPARAM wParam,LPARAM lParam)const
{
  if(!isValid())return FALSE;
	return sendMessage(mhWnd,message,wParam,lParam);
}

BOOL GUIWindow::setMenu(const PureMenu &somePureMenu)const
{
  if(!isValid()||!somePureMenu.isOkay())return FALSE;
	return ::SetMenu(mhWnd,(HMENU)somePureMenu);
}

BOOL GUIWindow::setIcon(const String &strIcon)
{
  if(!isValid())return FALSE;
	return (::SetClassLong(mhWnd,GCL_HICON,(LPARAM)::LoadIcon(processInstance(),(LPSTR)(String&)strIcon))?TRUE:FALSE);
}

BOOL GUIWindow::drawMenuBar(void)const
{
  if(!isValid())return FALSE;
	return ::DrawMenuBar(mhWnd);
}

String GUIWindow::getClassName(void)const
{
  String strClassName;
  if(!isValid())return strClassName;
	::GetClassName(mhWnd,strClassName.str(),String::MaxString);
	return strClassName;
}

#if defined(__FLAT__)
HINSTANCE GUIWindow::processInstance(void)const
{
	if(!isValid())return ::GetModuleHandle(0);
  return (HINSTANCE)::GetWindowLong(mhWnd,GWL_HINSTANCE);
}
#else
HINSTANCE GUIWindow::processInstance(void)const
{
	if(!isValid())return (HINSTANCE)0;
	return (HINSTANCE)::GetWindowWord(mhWnd,GWW_HINSTANCE);
}
#endif

// *** virtuals

int GUIWindow::windowProcedure(HWND /*hWnd*/,WORD /*message*/,WPARAM /*wParam*/,LPARAM /*lParam*/)
{
  return 0;
}

// *** statics

#if defined(_MSC_VER)
long FAR PASCAL GUIWindow::WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
#else
long FAR PASCAL _export GUIWindow::WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
#endif
{
	GUIWindow FAR *lpGUIWindow=(GUIWindow FAR*)InstanceData::getInstanceData(hWnd);
	if(0==lpGUIWindow)
	{
		if(WM_NCCREATE==message)
		{
			::DefWindowProc(hWnd,message,wParam,lParam);
			LPCREATESTRUCT lpcs((LPCREATESTRUCT)lParam);
			lpGUIWindow=(GUIWindow *)lpcs->lpCreateParams;
			if(!lpGUIWindow)return ::DefWindowProc(hWnd,message,wParam,lParam);
			InstanceData::setInstanceData(hWnd,(void FAR*)lpGUIWindow);
			lpGUIWindow->setHandle(hWnd);
      lpGUIWindow->disposition(Destroy); 
			return lpGUIWindow->windowProcedure(hWnd,message,wParam,lParam);
		}
		else if(WM_INITDIALOG==message)
		{
			lpGUIWindow=(GUIWindow*)lParam;
			if(!lpGUIWindow)return FALSE;
			InstanceData::setInstanceData(hWnd,(void FAR*)lpGUIWindow);
			lpGUIWindow->setHandle(hWnd);
      lpGUIWindow->disposition(Destroy);
			return (int)lpGUIWindow->callHandlers(VectorHandler::InitDialogHandler,CallbackData(wParam,lParam,hWnd));
		}
		else if(WM_NCDESTROY==message)return ::DefWindowProc(hWnd,message,wParam,lParam);
		else return ::DefWindowProc(hWnd,message,wParam,lParam);
	}
	switch(message)
	{
		case WM_CREATE :
		  {
		    Rect sizeRect;
				lpGUIWindow->clientRect(sizeRect);
			}
		  break;
	  case WM_SIZE :
		  break;
	}
	return lpGUIWindow->windowProcedure(hWnd,message,wParam,lParam);
}

#if defined(_MSC_VER)
int FAR PASCAL GUIWindow::DlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
#else
int FAR PASCAL _export GUIWindow::DlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
#endif
{
	GUIWindow *lpGUIWindow=(GUIWindow*)InstanceData::getInstanceData(hWnd);
	if(0==lpGUIWindow)
	{
		if(WM_INITDIALOG==message)
		{
			lpGUIWindow=(GUIWindow*)lParam;
			if(!lpGUIWindow)return FALSE;
			InstanceData::setInstanceData(hWnd,(void FAR*)lpGUIWindow);
			lpGUIWindow->setHandle(hWnd);
      lpGUIWindow->disposition(Destroy);
			return (int)lpGUIWindow->callHandlers(VectorHandler::InitDialogHandler,CallbackData(wParam,lParam,hWnd));
		}
		return FALSE;
	}
	return lpGUIWindow->windowProcedure(hWnd,message,wParam,lParam);
}

#if defined(_MSC_VER)
UINT FAR PASCAL GUIWindow::OFHookProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
#else
UINT FAR PASCAL _export GUIWindow::OFHookProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
#endif
{
	GUIWindow *lpGUIWindow=(GUIWindow*)InstanceData::getInstanceData(hWnd);
	if(0==lpGUIWindow)
	{
		if(WM_INITDIALOG==message)
		{
		  OPENFILENAME *lpOPENFILENAME=(OPENFILENAME*)lParam;
			if(!lpOPENFILENAME)return FALSE;
			lpGUIWindow=(GUIWindow*)lpOPENFILENAME->lCustData;
			if(!lpGUIWindow)return FALSE;
			InstanceData::setInstanceData(hWnd,(void FAR*)lpGUIWindow);
			lpGUIWindow->setHandle(hWnd);
      lpGUIWindow->disposition(Destroy);
			return (int)lpGUIWindow->callHandlers(VectorHandler::InitDialogHandler,CallbackData(wParam,lParam,hWnd));
		}
		return FALSE;
	}
	return lpGUIWindow->windowProcedure(hWnd,message,wParam,lParam);
}

#if defined(_MSC_VER)
LONG FAR PASCAL GUIWindow::FrameProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
#else
LONG FAR PASCAL _export GUIWindow::FrameProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
#endif
{
	GUIWindow *lpGUIWindow=(GUIWindow*)InstanceData::getInstanceData(hWnd);
  if(lpGUIWindow==0)
  {
    if(WM_CREATE==message)  
    {
      ::DefFrameProc(hWnd,(HWND)0,message,wParam,lParam);
      LPCREATESTRUCT lpcs=(LPCREATESTRUCT)lParam;
      lpGUIWindow=(GUIWindow *)lpcs->lpCreateParams;
			InstanceData::setInstanceData(hWnd,(void FAR*)lpGUIWindow);
      lpGUIWindow->setHandle(hWnd);
			lpGUIWindow->disposition(Destroy);
      return lpGUIWindow->windowProcedure(hWnd,message,wParam,lParam);
    }
    else if(WM_CREATE==message)return lpGUIWindow->windowProcedure(hWnd,message,wParam,lParam);
    else return ::DefFrameProc(hWnd,(HWND)0,message,wParam,lParam);
  }
  else return lpGUIWindow->windowProcedure(hWnd,message,wParam,lParam);
}

#if defined(_MSC_VER)
LONG FAR PASCAL GUIWindow::MDIProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
#else
LONG FAR PASCAL _export GUIWindow::MDIProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
#endif
{
	GUIWindow *lpGUIWindow=(GUIWindow*)InstanceData::getInstanceData(hWnd);

  if(lpGUIWindow==0)
  {
    if(WM_NCCREATE==message)  
    {
	    ::DefMDIChildProc(hWnd,message,wParam,lParam);
		  LPCREATESTRUCT lpcs=(LPCREATESTRUCT)lParam;
		  LPMDICREATESTRUCT lpcm=(LPMDICREATESTRUCT)lpcs->lpCreateParams;
		  lpGUIWindow=(GUIWindow *)lpcm->lParam;
		  if(!lpGUIWindow)return FALSE;
			InstanceData::setInstanceData(hWnd,(void FAR*)lpGUIWindow);
			lpGUIWindow->setHandle(hWnd);
			lpGUIWindow->disposition(Destroy);
			return lpGUIWindow->windowProcedure(hWnd,message,wParam,lParam);
    }
    else if(WM_CREATE==message)return lpGUIWindow->windowProcedure(hWnd,message,wParam,lParam);
    else return ::DefMDIChildProc(hWnd,message,wParam,lParam);
  }
  else return lpGUIWindow->windowProcedure(hWnd,message,wParam,lParam);
}
