#pragma warning (disable:4786)
#include "ai test.hpp"
#include "windows.hpp"
#include "globals.hpp"

LRESULT WINAPI RootWndProc (HWND thewnd,UINT themsg,WPARAM wParam,LPARAM lParam);

void InitWindows (bool firstpass)
{
	WNDCLASS rootclass;

	if (firstpass)				// on the first pass so set the global window to null
	{
		return;
	}
	rootclass.style = CS_OWNDC;
	rootclass.lpfnWndProc = RootWndProc;
	rootclass.cbClsExtra = 0;
	rootclass.cbWndExtra = 4;
	rootclass.hInstance = g_hInstance;
	rootclass.hIcon = LoadIcon (NULL,IDI_APPLICATION);
	rootclass.hCursor = LoadCursor (NULL,IDC_ARROW);
	rootclass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
	rootclass.lpszMenuName = NULL;
	rootclass.lpszClassName = WINDOW_CLASS_NAME;

	if (!RegisterClass (&rootclass))						// register the class for the root window
		throw std::runtime_error ("unable to register window class");
}

void KillWindows (void)
{
	UnregisterClass (WINDOW_CLASS_NAME,g_hInstance);
}

LRESULT WINAPI RootWndProc (HWND thewnd,UINT themsg,WPARAM wParam,LPARAM lParam)
{
	LPCREATESTRUCT cdata;
	CWindow *window;

	switch (themsg)
	{
	case WM_CREATE:
		cdata = reinterpret_cast<LPCREATESTRUCT> (lParam);
		window = reinterpret_cast<CWindow *> (cdata->lpCreateParams);
		if (window != 0)
		{
			window->Window (thewnd);
			SetWindowLong (thewnd,GWL_USERDATA,reinterpret_cast<long> (window));
			return window->WndProc (thewnd,themsg,wParam,lParam);
		}
		break;
	default:
		window = reinterpret_cast<CWindow *> (GetWindowLong (thewnd,GWL_USERDATA));
		if (window != NULL)
			return window->WndProc (thewnd,themsg,wParam,lParam);
		break;
	}
	return DefWindowProc (thewnd,themsg,wParam,lParam);
}

CWindow::CWindow (void)
{
	m_Window = NULL;
}

CWindow::~CWindow (void)
{
}

void CWindow::Create (const CString& title,int width,int height,int offw,int offh,bool scale)
{
	CRect arect;
	DWORD style = c_dwWindowedStyle;

	m_Scale = scale;
	if (offw == 0)
		offw = width;
	if (offh == 0)
		offh = height;
	m_BackWidth = offw;
	m_BackHeight = offh;
	if (width != offw)
	{
		if (!m_Scale)
		{
			style |= WS_HSCROLL;
		}
		style |= WS_THICKFRAME;
		style |= WS_MAXIMIZEBOX;
		style |= WS_MINIMIZEBOX;
	}
	if (height != offh)
	{
		if (!m_Scale)
		{
			style |= WS_VSCROLL;
		}
		style |= WS_THICKFRAME;
		style |= WS_MAXIMIZEBOX;
		style |= WS_MINIMIZEBOX;
	}
	arect.left = 0;
	arect.top = 0;
	arect.right = width;
	arect.bottom = height;
	AdjustWindowRectEx (&arect,style,true,c_dwWindowedStyleEx);
	Window( CreateWindowEx (c_dwWindowedStyleEx,		// create our root/main window in the requested size
		WINDOW_CLASS_NAME,WINDOW_NAME,
		style,
		CW_USEDEFAULT,CW_USEDEFAULT,arect.right - arect.left,arect.bottom - arect.top,
		NULL,NULL,g_hInstance,this));

	if (m_Window == NULL)								// if we fail in creation then throw an exception
		throw std::runtime_error ("root window did not create");

	SetWindowText (m_Window,(LPCTSTR) title);
	g_szTitle = (LPCSTR) title;
	g_szClassName = WINDOW_CLASS_NAME;
	InitialUpdate ();
}

void CDIBWindow::Create (const CString& title,int width,int height,int offw,int offh,bool scale)
{
	if (offw == 0)
		offw = width;
	if (offh == 0)
		offh = height;
	m_Background.Create (offw,offh,16);
	CWindow::Create (title,width,height,offw,offh,scale);
}
#if 0
void CDDWindow::Create (const CString& title,int width,int height,int offw,int offh,bool scale)
{
	if (offw == 0)
		offw = width;
	if (offh == 0)
		offh = height;
	m_DirectBackground.Create (offw,offh,16,false,true);
	CDIBWindow::Create (title,width,height,offw,offh,scale);
}
#endif

void CDIBWindow::Render (HDC thedc,LPPAINTSTRUCT data)
{
	int xpos,ypos;
	CRect b;
	HDC src;
	bool ret;

	if (IsValid ())
	{
		xpos = GetScrollPos (m_Window,SB_HORZ);
		ypos = GetScrollPos (m_Window,SB_VERT);
		if (data)
		{
			b = data->rcPaint;
		}
		else
		{
			GetClientRect (m_Window,&b);
		}
		src = m_Background.GetDC ();
		if (m_Scale)
		{
			ret = StretchBlt (thedc,0,0,b.Width (),b.Height (),src,0,0,m_BackWidth,m_BackHeight,SRCCOPY);
		}
		else
		{
			ret = BitBlt (thedc,b.left,b.top,b.Width (),b.Height(),src,b.left,b.top,SRCCOPY);
		}
		m_Background.ReleaseDC (src);
	}
}
#if 0
void CDDWindow::Render (HDC thedc,LPPAINTSTRUCT data)
{
	int xpos,ypos;
	CRect bounds;
	HDC src,dest;

	if (IsValid ())
	{
		xpos = GetScrollPos (m_Window,SB_HORZ);
		ypos = GetScrollPos (m_Window,SB_VERT);
		if (data)
		{
			bounds = data->rcPaint;
		}
		else
		{
			GetClientRect (m_Window,&bounds);
		}
		src = m_DirectBackground.GetDC ();
		dest = m_Background.GetDC ();
		bool ret = BitBlt (dest,bounds.left,bounds.top,bounds.Width (),bounds.Height(),src,xpos,ypos,SRCCOPY);
		m_Background.ReleaseDC (dest);
		m_DirectBackground.ReleaseDC (src);
	}
	CDIBWindow::Render (thedc,data);
}
#endif

LRESULT CWindow::WndProc (HWND thewnd,UINT themsg,WPARAM wParam,LPARAM lParam)
{
	int w,h;
	CString fred;
	SCROLLINFO sinfo;
	LPMINMAXINFO data;
	CRect client,window,offset;
	int scrollcode,scrollpos;
	HDC thedc;
	PAINTSTRUCT paint;

	assert (thewnd == m_Window);
	switch (themsg)
	{
	case WM_PAINT:
		thedc = BeginPaint (thewnd,&paint);
		Render (thedc,&paint);
		EndPaint (thewnd,&paint);
		break;
	case WM_SIZE:
		if (!m_Scale)
		{
			w = m_BackWidth - LOWORD (lParam);
			h = m_BackHeight - HIWORD (lParam);
			if (w<0)
				w = 0;
			if (h<0)
				h = 0;

			sinfo.cbSize = sizeof (SCROLLINFO);
			sinfo.fMask = SIF_RANGE + SIF_DISABLENOSCROLL;
			sinfo.nMin = 0;
			sinfo.nMax = h;
	//		sinfo.nPage = min (HIWORD (lParam),m_BackHeight - HIWORD (lParam));
			SetScrollInfo (thewnd,SB_VERT,&sinfo,true);

			sinfo.cbSize = sizeof (SCROLLINFO);
			sinfo.fMask = SIF_RANGE + SIF_DISABLENOSCROLL;
			sinfo.nMin = 0;
			sinfo.nMax = w;
	//		sinfo.nPage = min (LOWORD (lParam),m_BackWidth - LOWORD (lParam));
			SetScrollInfo (thewnd,SB_HORZ,&sinfo,true);
		}
		else
		{
			InvalidateRect (thewnd,NULL,false);
		}
		break;
	case WM_VSCROLL:
		scrollcode = LOWORD (wParam);
		scrollpos = GetScrollPos (thewnd,SB_VERT);
		switch (scrollcode)
		{
		case SB_BOTTOM:
			scrollpos = m_BackHeight;
			break;
		case SB_LINEDOWN:
			scrollpos += 10;
			break;
		case SB_LINEUP:
			scrollpos -= 10;
			break;
		case SB_PAGEDOWN:
			scrollpos += 100;
			break;
		case SB_PAGEUP:
			scrollpos -= 100;
			break;
		case SB_THUMBPOSITION:
			scrollpos = HIWORD (wParam);
			break;
		case SB_THUMBTRACK:
			scrollpos = HIWORD (wParam);
			break;
		case SB_TOP:
			scrollpos = 0;
			break;
		}
		if (scrollpos < 0)
			scrollpos = 0;
		GetClientRect (thewnd,&client);
		if (scrollpos > (m_BackHeight - client.Height ()))
			scrollpos = m_BackHeight - client.Height ();
		SetScrollPos (thewnd,SB_VERT,scrollpos,true);
		break;
	case WM_HSCROLL:
		scrollcode = LOWORD (wParam);
		scrollpos = GetScrollPos (thewnd,SB_HORZ);
		switch (scrollcode)
		{
		case SB_BOTTOM:
			scrollpos = m_BackWidth;
			break;
		case SB_LINEDOWN:
			scrollpos += 10;
			break;
		case SB_LINEUP:
			scrollpos -= 10;
			break;
		case SB_PAGEDOWN:
			scrollpos += 100;
			break;
		case SB_PAGEUP:
			scrollpos -= 100;
			break;
		case SB_THUMBPOSITION:
			scrollpos = HIWORD (wParam);
			break;
		case SB_THUMBTRACK:
			scrollpos = HIWORD (wParam);
			break;
		case SB_TOP:
			scrollpos = 0;
			break;
		}
		if (scrollpos < 0)
			scrollpos = 0;
		GetClientRect (thewnd,&client);
		if (scrollpos > (m_BackWidth - client.Width ()))
			scrollpos = m_BackWidth - client.Width ();
		SetScrollPos (thewnd,SB_HORZ,scrollpos,true);
		break;
	case WM_GETMINMAXINFO:
		GetClientRect (thewnd,&client);
		GetWindowRect (thewnd,&window);

		offset.left = client.left - window.left;
		offset.right = window.right - client.right;
		offset.top = client.top - window.top;
		offset.bottom = window.bottom - client.bottom;
		offset.left += offset.right;
		offset.top += offset.bottom;
	
		data = reinterpret_cast<LPMINMAXINFO> (lParam);
		data->ptMinTrackSize.x = 50;
		data->ptMinTrackSize.y = 50;
		data->ptMaxTrackSize.x = m_BackWidth + offset.left;
		data->ptMaxTrackSize.y = m_BackHeight + offset.top;
		data->ptMaxSize.x = m_BackWidth + offset.left;
		data->ptMaxSize.y = m_BackHeight + offset.top;
		data->ptMaxPosition.x = 20;
		data->ptMaxPosition.y = 20;
		break;
	case WM_LBUTTONDOWN:
		LButtonDown (LOWORD (lParam),HIWORD (lParam));
		break;
	case WM_RBUTTONDOWN:
		RButtonDown (LOWORD (lParam),HIWORD (lParam));
		break;
	case WM_LBUTTONUP:
		LButtonUp (LOWORD (lParam),HIWORD (lParam));
		break;
	case WM_RBUTTONUP:
		RButtonUp (LOWORD (lParam),HIWORD (lParam));
		break;
	case WM_MOUSEMOVE:
		MouseMove (LOWORD (lParam),HIWORD (lParam));
		break;
	}
	return DefWindowProc (thewnd,themsg,wParam,lParam);
}
