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

#ifndef __WINSYSTEM_H__
#define __WINSYSTEM_H__

#include <tchar.h>
#include "win0.h"
#include "area.h"


struct AppInstanceData;
struct WindowMessageData;
class Window;

// global structure, has to be created at the start of WinMain
extern AppInstanceData *G_app_instance_data;


		// process data

struct AppInstanceData
{
	HINSTANCE current,previous;

AppInstanceData(HINSTANCE c,HINSTANCE p):
	current(c),previous(p){}
};

		// ids

struct WindowTemplateId
{ 
	LPCTSTR name;
	HINSTANCE app_instance;

WindowTemplateId(LPCTSTR n,HINSTANCE i):
	app_instance(i),name(n){}
WindowTemplateId(LPCTSTR n=0, const AppInstanceData *z=0):
	app_instance(0),name(n){if(z) app_instance=z->current;}
};

struct WindowId
{ 
	HINSTANCE app_instance; 
	HWND window;

WindowId(const AppInstanceData *z=0,HWND w=0):
	app_instance(0),window(w){if(z) app_instance=z->current;}
};


		// FunctionPaint

class FunctionPaint
{
public:

	HDC dc;
	PAINTSTRUCT ps;

FunctionPaint(HWND h){ dc=BeginPaint( handle=h, &ps ); }
~FunctionPaint(){ EndPaint( handle, &ps ); }

private:
	HWND handle;
};



		// Window driver

// a base class for window procedure functions
struct WindowDriver
{
virtual ~WindowDriver(){}

// returns: 0 -> processed, 1 -> pass to next, -1 -> error
virtual int respond(Window& ,WindowMessageData&){return 1;}

// for internal use
static LRESULT CALLBACK generic_procedure(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
static BOOL CALLBACK dialog_procedure(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
};

		// window template (window class)
 
struct WindowTemplateData
{
	LPCTSTR menu_name;
    UINT    style;  
    HICON   icon, small_icon; 
    HCURSOR cursor; 
    HBRUSH  background; 
	WNDPROC	procedure;

WindowTemplateData():
	menu_name(0),
	style(CS_VREDRAW | CS_HREDRAW),
	icon(0),
	small_icon(0),
	cursor(0),
	background((HBRUSH)( COLOR_BACKGROUND+1 )),
	procedure(&WindowDriver::generic_procedure){}

int set_from(const WindowTemplateId& id);
};

/*
struct WindowTemplateDataVariant:public WindowTemplateData
{

WindowTemplateDataVariant()
{
	icon=(HICON)LoadImage(0,IDI_APPLICATION, IMAGE_ICON,0,0,LR_DEFAULTSIZE);
	small_icon=(HICON)LoadImage(0,IDI_APPLICATION, IMAGE_ICON,GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),0);
	cursor=(HCURSOR)LoadImage(0,IDC_ARROW, IMAGE_CURSOR,0,0,LR_DEFAULTSIZE);
}

~WindowTemplateDataVariant()
{
	DestroyIcon(icon); 
	DestroyIcon(small_icon);
	DestroyCursor(cursor);
}
};
*/



class WindowTemplate
{
WindowTemplate(const WindowTemplate&);
WindowTemplate& operator=(const WindowTemplate&);
public:

WindowTemplate():handle(0){}
WindowTemplate(const WindowTemplateData& data,const WindowTemplateId& Id)
{store(data,Id);}
//~WindowTemplate(){}

WindowTemplateId get_id(){return id;}
ATOM get_handle()const{return handle;}

BOOL store(const WindowTemplateData& data,const WindowTemplateId& id);
BOOL remove();
BOOL get_stored(WindowTemplateData& );


private:
WindowTemplateId id;
ATOM handle; 
};

		// WINDOW

struct WindowData
{
  WindowTemplate& window_template;
  Area area;
  DWORD style,style_ex;			
  HMENU menu;			// menu handle or child identifier	

WindowData(WindowTemplate& z):
	window_template(z),
	style(WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CS_OWNDC),
	style_ex(WS_EX_APPWINDOW | WS_EX_OVERLAPPEDWINDOW),
	menu(0)
	{}

// interprets menu==0 as absence of menu,
// hence, set menu before calling this
BOOL set_client_area(const Area& z);
};

// does not use extra bytes
class Window
{
Window(const Window&);
Window& operator=(const Window&);
public:

Window(HWND h=0):handle(0){if(h) set(h);}
virtual ~Window(){}

int set(HWND h){BOOL z=::IsWindow(h); if(z) handle=h;return !z;}
HWND get_handle()const{return handle;}

int set(const WindowDriver& z)
{ ::SetWindowLong(handle,GWL_USERDATA,(LONG)(&z)); return 0;}

WindowDriver *get_driver()const
{ return (WindowDriver *)::GetWindowLong(handle,GWL_USERDATA);}

// should not call directly - send messages
BOOL destroy();

BOOL minimize()const{return CloseWindow(handle);}

UINT change_base_style(UINT style)const;
HICON change_icon(HICON icon)const;
HICON change_small_icon(HICON small_icon)const;
HCURSOR change_cursor(HCURSOR cursor)const;
HBRUSH change_background(HBRUSH brush)const;
LPCTSTR change_base_menu(LPCTSTR name)const;
DWORD change_style(DWORD style)const;
DWORD change_style_ex(DWORD style)const;
WindowDriver* change_driver(WindowDriver &z);
BOOL store_data( LPCTSTR data_name, HANDLE data);
VOID remove_data( LPCTSTR data_name);
HANDLE get_stored_data(LPCTSTR data_name)const;

BOOL show(int nCmdShow )const{return ::ShowWindow(handle,nCmdShow);}
// send show command into another thread's queue
BOOL show_async(int nCmdShow )const{return ::ShowWindowAsync(handle,nCmdShow);}

// repainting, direct call 
BOOL update(){return UpdateWindow(handle);}

BOOL client_area(Area &)const;

BOOL selfsend_destroy_message();
BOOL selfsend_end_thread_message();

protected:
	HWND handle;
};

// provides one private Device Context
class GenericWindow:public Window
{
GenericWindow(const GenericWindow&);
GenericWindow& operator=(const GenericWindow&);
public:

GenericWindow(HWND h=0):Window(h){}

// binds window to a calling thread
// passes creation messages to window procedure
// fails if window is already created
int create(LPCTSTR name,
		   const WindowData&,
		   const WindowId& parent, 
		   const WindowDriver &driver);

// window's private device content
HDC dc(){return ::GetDC(handle);} 
};


class DialogWindow:public Window
{
DialogWindow(const DialogWindow&);
DialogWindow& operator=(const DialogWindow&);
public:

DialogWindow(HWND h=0):Window(h){}

// binds window to a calling thread
// fails if window is already created
int create(
  LPCTSTR lpTemplateName,  // dialog box template
  const WindowId& parent, 
  const WindowDriver &driver);

};


		// WINDOW MESSAGE

struct WindowMessageData
{
	UINT   message; 
	WPARAM wparam; 
	LPARAM lparam; 
	WindowMessageData(UINT m=0,WPARAM w=0,LPARAM l=0):
	message(m),wparam(w),lparam(l){}
};

struct WindowMessageFilter
{
	HWND window;   
	UINT min_id,max_id;		
	WindowMessageFilter(HWND w=0,UINT mi=0,UINT ma=0):
	window(w),min_id(mi),max_id(ma){}
};


// sends to a queue of a thread linked to a window
inline
BOOL send(WindowMessageData& m,HWND window)
{ return ::PostMessage(window,m.message,m.wparam,m.lparam);}

inline
LRESULT send_priority(WindowMessageData& m,HWND window)
{ return ::SendMessage(window,m.message,m.wparam,m.lparam);}

inline
BOOL send(WindowMessageData& m,HWND dialog_window,int item)
{ return ::SendDlgItemMessage(dialog_window,item,m.message,m.wparam,m.lparam);}

inline
BOOL send_to_thread(WindowMessageData& m,DWORD thread_id)
{return ::PostThreadMessage(thread_id,m.message,m.wparam,m.lparam);}

// from a queue of calling thread
inline
BOOL get(LPMSG lpMsg,WindowMessageFilter& filter)
{ return GetMessage(lpMsg,filter.window,filter.min_id,filter.max_id);} 

// from a queue of calling thread
inline
BOOL peek(LPMSG lpMsg,WindowMessageFilter& filter,UINT wRemoveMsg=PM_NOREMOVE | PM_NOYIELD)
{ return PeekMessage(lpMsg,filter.window,filter.min_id,filter.max_id,wRemoveMsg);}

// passes to a window procedure
inline
LRESULT dispatch(LPMSG lpMsg)
{return ::DispatchMessage(lpMsg);}

// for some messages, creates a new message and places it in front of queue
inline 
BOOL translate(CONST MSG *lpMsg){return ::TranslateMessage(lpMsg);}

// direct calls of a window procedure for some messages
inline
BOOL process_accelerator(LPMSG lpMsg,HWND hWnd,HACCEL hAccTable)
{return (BOOL)::TranslateAccelerator(hWnd,hAccTable,lpMsg);}

// checks if it is a message to a dialog and only then processes it
inline
BOOL check_process_dialog(LPMSG lpMsg,HWND hWnd)
{return ::IsDialogMessage(hWnd,lpMsg);}

// message to a calling thread
inline
VOID send_quit_gui_thread(int exit_code=0){::PostQuitMessage(exit_code);}


int GUI_step( WindowMessageFilter filter,HACCEL accelerator=0 );

// sends destroy message to all top windows of calling thread
VOID winsystem_thread_cleanup();

inline
int message_box(
  HWND handle,         // window-owner, if zero then has no owner
  LPCTSTR lpText,  
  LPCTSTR lpCaption=_T("Error Info"), 
  UINT uType=MB_OK | MB_ICONINFORMATION | MB_SETFOREGROUND,
  WORD wLanguageId=MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)  // default language
)
{return ::MessageBoxEx(handle,lpText,lpCaption,uType,wLanguageId);}

VOID last_error_message_box(HWND handle);


#endif