// Common, groups WinWrap and WinSystem
// Copyright Alexander Liss

#include "winsystem.h"
#include "gdi.h"

AppInstanceData *G_app_instance_data=0;

struct CreationCallbackData
{
int (*callback)(LPVOID);
LPVOID data;
};


BOOL CALLBACK property_destruction_callback(HWND hWnd,LPTSTR lpszString,HANDLE , DWORD  )       
{ 
    ::RemoveProp(hWnd, lpszString); 
 
    return TRUE; 
} 

BOOL CALLBACK window_destruction_callback(HWND hwnd, LPARAM lParam )
{
	Window w(hwnd);

	w.destroy();

	return TRUE;
}



	// WindowDriver

LRESULT CALLBACK WindowDriver::generic_procedure(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
	if(!IsWindow(hWnd)) return 1;

	int g=0;
	WindowMessageData message(uMsg,wParam,lParam);
	GenericWindow window(hWnd);

	WindowDriver *p=window.get_driver();


	if(p)
	{

		g=p->respond(window,message);

		if(g==-1) return 1; // error

		if(!g) return 0;	// processed
		
	}

	BOOL z=::DefWindowProc( hWnd, uMsg, wParam, lParam );

	if(z && message.message==WM_DESTROY)
	{
		z=window.destroy();
	}

	return z;
}


BOOL CALLBACK WindowDriver::dialog_procedure(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
	if(!IsWindow(hWnd)) return 1;

	int g=0;
	WindowMessageData message(uMsg,wParam,lParam);
	DialogWindow window(hWnd);

	WindowDriver *p=window.get_driver();

	if(p)
	{

		g=p->respond(window,message);

		if(!g) return TRUE;	// processed

	}

	if( message.message==WM_DESTROY)
	{
		window.destroy();
	}

	// PASS TO TEMPLATE DEFINED DIALOG PROCEDURE

	return FALSE; 
}


	// WindowTemplateData

BOOL WindowTemplateData::set_from(const WindowTemplateId& id)
{
	WNDCLASSEX data;

	data.cbSize=sizeof(WNDCLASSEX);

	BOOL g=::GetClassInfoEx(id.app_instance,id.name,&data);

	if(!g) return FALSE;

    style=data.style; 
    procedure=data.lpfnWndProc; 
    icon=data.hIcon; 
    cursor=data.hCursor; 
    background=data.hbrBackground; 
    menu_name=data.lpszMenuName; 
    small_icon=data.hIconSm;

	return TRUE;
}


	// WindowTemplate


BOOL WindowTemplate::store(const WindowTemplateData& d,const WindowTemplateId& Id)
{
	id=Id;

	WNDCLASSEX data;

	data.cbSize=sizeof(WNDCLASSEX); 
    data.style=d.style; 
    data.lpfnWndProc=d.procedure; 
    data.cbClsExtra=0; 
    data.cbWndExtra=0;
    data.hInstance=Id.app_instance; 
    data.hIcon=d.icon; 
    data.hCursor=d.cursor; 
    data.hbrBackground=d.background; 
    data.lpszMenuName=d.menu_name; 
    data.lpszClassName=Id.name; 
    data.hIconSm=d.small_icon;

	handle=::RegisterClassEx(&data);

	return handle!=0;
}

BOOL WindowTemplate::remove()
{
	BOOL z = ::UnregisterClass(id.name,id.app_instance);

	if(z)
	{
		id.name=0;
		id.app_instance=0;
		handle=0;
	}

	return z;
};

BOOL WindowTemplate::get_stored(WindowTemplateData& d )
{
	return d.set_from(id);
}



	//  WindowData


BOOL WindowData::set_client_area(const Area& z)
{
	RECT r = z.get_rect();

	::AdjustWindowRectEx(&r,style,(menu != 0),style_ex);

	return 0;
}


	// WINDOW

UINT Window::change_base_style(UINT style)const
{ return (UINT)::SetClassLong(handle,GCL_STYLE,(LONG)style);}

HICON Window::change_icon(HICON icon)const
{ return (HICON)::SetClassLong(handle,GCL_HICON,(LONG)icon);}

HICON Window::change_small_icon(HICON small_icon)const
{ return (HICON)::SetClassLong(handle,GCL_HICONSM,(LONG)small_icon);}

HCURSOR Window::change_cursor(HCURSOR cursor)const
{ return (HCURSOR)::SetClassLong(handle,GCL_HCURSOR,(LONG)cursor);}

HBRUSH Window::change_background(HBRUSH brush)const
{ return (HBRUSH)::SetClassLong(handle,GCL_HBRBACKGROUND,(LONG)brush);}

LPCTSTR Window::change_base_menu(LPCTSTR name)const
{ return (LPCTSTR)::SetClassLong(handle,GCL_MENUNAME,(LONG)name);}


DWORD Window::change_style(DWORD style)const
{return ::SetWindowLong(handle,GWL_STYLE,(LONG)style);}

DWORD Window::change_style_ex(DWORD style)const
{return ::SetWindowLong(handle,GWL_EXSTYLE,(LONG)style);}


WindowDriver* Window::change_driver(WindowDriver &z)
{return (WindowDriver*)::SetWindowLong(handle,GWL_USERDATA,(LONG)(&z));}

BOOL Window::store_data( LPCTSTR data_name, HANDLE data)
{ if(handle) return ::SetProp(handle,data_name,data); return FALSE;}

VOID Window::remove_data( LPCTSTR data_name)
{ ::RemoveProp(handle,data_name);}

HANDLE Window::get_stored_data(LPCTSTR data_name)const
{ if(handle) return ::GetProp(handle,data_name); return 0;}


BOOL Window::destroy()
{
	if(!handle) return FALSE;

	// destroy window's "properties" 
	EnumPropsEx(handle,property_destruction_callback, NULL); 

	// remove window's driver
	::SetWindowLong(handle,GWL_USERDATA,0);

	BOOL z=::DestroyWindow(handle); 
	
	handle=0; 

	return z;
}



BOOL Window::client_area(Area& z)const
{
	if(!handle) return FALSE;

	RECT r;

	BOOL g=::GetClientRect(handle,&r);

	if(!g) return FALSE;

	z.set(r);

	return TRUE;
}

BOOL Window::selfsend_destroy_message()
{
	WindowMessageData m(WM_DESTROY);

	return send(m,handle);
}

BOOL Window::selfsend_end_thread_message()
{
	BOOL z=selfsend_destroy_message();

	if(z)
	{
		WindowMessageData m(WM_QUIT);

		z=send(m,handle);
	}

	return z;
}

		// GenericWindow

int GenericWindow::create(
		LPCTSTR name,
		const WindowData& d,
		const WindowId& parent,
		const WindowDriver &driver
						  )
{

	handle=::CreateWindowEx(
		d.style_ex,
		d.window_template.get_id().name,
		name,
		d.style,
		d.area.left,
		d.area.top,
		d.area.width,
		d.area.height,
		parent.window,
		d.menu,
		parent.app_instance,
		0);

	if(!handle) return 1;

	set(driver);

	return 0;
}


		// DialogWindow

int DialogWindow::create(
	LPCTSTR lpTemplateName,
	const WindowId& parent,
	const WindowDriver &driver
	)
{

	handle=::CreateDialogParam(
		parent.app_instance,
		lpTemplateName,
		parent.window,
		WindowDriver::dialog_procedure,
		0);

	if(!handle) return 1;

	set(driver);

	return 0;
}



	// FUNCTIONS


int GUI_step( WindowMessageFilter filter, HACCEL accelerator )
{	
	MSG msg;
	BOOL z=FALSE;        

	z=::get(&msg,filter);

	if(z==FALSE) return 1;
	if(z==-1) return 2; // error

	if(accelerator)
	{
		z=::process_accelerator(&msg,filter.window,accelerator);

		if(z) return 0; // message processed, window procedure called
	}

	z=::check_process_dialog(&msg,filter.window);

	if(z) return 0; // message processed, window procedure called

	// pass to window procedure untranslated value to ignore
	// and translated value place back in queue
	::translate( &msg );

	::dispatch(&msg);

	return 0;
}

VOID winsystem_thread_cleanup()
{
	EnumThreadWindows(GetCurrentThreadId(),window_destruction_callback,0);
}

VOID last_error_message_box(HWND handle)
{
	LPVOID lpMsgBuf;

	FormatMessage( 
	FORMAT_MESSAGE_ALLOCATE_BUFFER | 
	FORMAT_MESSAGE_FROM_SYSTEM | 
	FORMAT_MESSAGE_IGNORE_INSERTS,
	NULL,
	GetLastError(),
	MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
	(LPTSTR) &lpMsgBuf,
	0,
	NULL 
	);

	// Display the string.
	MessageBox( handle, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );

	// Free the buffer.
	LocalFree( lpMsgBuf );
}