
#ifndef ___V_M_H___
#define ___V_M_H___

#include "gb_windows.h"
#include <commctrl.h>
#include "v/v_errors.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "msequence.h"
#ifdef __cplusplus
}
#endif

// MACHINE COMMON CONSTANTS & MACROS
#ifdef __cplusplus

#pragma warning(disable : 4786 )
#include <wchar.h>

class VWindow;
class VObject;
class VMenuItem;
class VMenuInfoM;
typedef LOGFONTW VFONT;

class VRadioGroupInfo_{
public:
	VRadioGroupInfo_(HWND radio):radio(radio),next(0),prev(0){remove();}
	~VRadioGroupInfo_();
	void	insert(VRadioGroupInfo_ *grp);
	void	remove();
	void	select();
private:
	HWND	radio;
	VRadioGroupInfo_	*next, *prev, *selected;
};
typedef VRadioGroupInfo_* VRadioGroupInfo;
class VContainerInfo;

/* base class of message_handler classes */
class MessageHandler{
	UINT msg;
public:
	MessageHandler(UINT message):msg(message){}
	UINT get_messageid()const{return msg;}
	virtual void on_event(VObject *obj,MSG *msg)=0;
};

/* WM_COMMAND handler. WM_COMMAND event is sent on click button or click menu item */
typedef void (*OnCommandFunc)(VObject *sender, MSG *msg);
class OnCommand : public MessageHandler{
	OnCommandFunc func;
	void on_event(VObject *arg,MSG *msg){func(arg,msg);}
public:
	OnCommand(OnCommandFunc f):MessageHandler(WM_COMMAND),func(f){}
};

/* WM_NOTIFY handler. WM_NOTIFY event is sent on click common control */
class OnNotify : public MessageHandler{
	OnCommandFunc func;
	void on_event(VObject *arg,MSG *msg){func(arg,msg);}
public:
	OnNotify(OnCommandFunc f):MessageHandler(WM_NOTIFY),func(f){}
};

/* windows dependent VObject information */
class VInfo {
private:
	HWND hwnd;
	VObject *obj;
	VFONT *font;
	HFONT hfont;

protected:
	int control_id;

#define MAX_MESSAGE_HANDLER 5
	MessageHandler* message_handler[MAX_MESSAGE_HANDLER];

public:
	void set_font(VFONT *font);
	HWND get_hwnd(){return hwnd;}
	VFONT *get_font();
	static VFONT *get_default_font();
	int get_control_id(){return control_id;}
	VObject *get_obj(){return obj;}
	
	/* add a message handler which responds to WM_XXX message dispatched by VIindowInfo::dispatch_message */
	void add_message_handler(MessageHandler *handler);

	/* return whether this VObject is container window. container window means the window who has window procedure */
	virtual bool is_container_window() const {return false;}
	
	/* get VInfo of machine dependent parent window who has window procedure */
	static VContainerInfo *get_container_info(VObject *find_from);
	
	/* get VInfo from associated window */
	static VInfo *get_from_hwnd(HWND hWnd);
	
	VInfo(VObject *o, HWND h, int id);
	virtual ~VInfo();

	friend class VContainerInfo;
};

class VContainerInfo : public VInfo {
public:
	VContainerInfo(VObject *o, HWND h, int id,
		bool bkgnd_parent, bool is_dialog,
		bool draw_default=false, bool no_click=false,
		WNDPROC defWndProc=NULL);
	virtual ~VContainerInfo();

	int	get_next_control_id(){ return ++max_control_id; }

	static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
	static bool	dispatch_message_to_object(VInfo *info, MSG *msg);
	
	bool	is_container_window() const {return true;}
	bool	is_dialog_window() const {return is_dialog;}
	
	void	set_min_size(short w, short h){ min_size.w = w; min_size.h = h; }

	void	set_internal_size(short w, short h){
		internal_size.w = w;
		internal_size.h = h;
		setup_scroll();
	}
	void	setup_scroll();
	void	set_scrollable(bool flag) { scrollable = flag; }
	bool	get_bkgnd_parent() { return bkgnd_parent != 0; }
	bool	get_scrollable() { return scrollable != 0; }
	void	get_scroll_pos(int &x, int &y) {
		x = scrollable ? sih.nPos : 0;
		y = scrollable ? siv.nPos : 0;
	}

	
protected:
	struct { short w, h; }	size, min_size, internal_size;

	virtual LRESULT dispatch_message(MSG *msg);

	HWND		hAncestorDialog;

private:
	int			max_control_id;
	char		scrollable:1, bkgnd_parent:1, is_dialog:1, draw_default:1, no_click:1;
	SCROLLINFO	sih, siv;
	WNDPROC		defaultWndProc;
};

class VTrackedInfo : public VContainerInfo {
public:
	VTrackedInfo(VObject *o, HWND h, int id,
		bool bkgnd_white, bool is_dialog, bool draw_default, WNDPROC defWndProc=NULL)
		: VContainerInfo(o,h,id,bkgnd_white,is_dialog,draw_default,defWndProc) {}
	~VTrackedInfo();

	static void			track_mouse(MSG *msg);

protected:
	static VTrackedInfo	*check_wnd(MSG *msg);

	virtual void		mouse_enter(MSG *msg) = 0;
	virtual void		mouse_leave(MSG *msg) = 0;
	virtual void		mouse_move(MSG *msg) = 0;
	virtual void		mouse_down(MSG *msg) = 0;
	virtual void		mouse_up(MSG *msg) = 0;

private:
	static VTrackedInfo	*tracked, *last;
	static HWND			hWndTracked, hWndLastPoint;
	static int			button_cnt;
};

#else /* ifndef cplusplus */
typedef void* VInfo;
typedef void* VRadioGroupInfo;
#endif /* ifdef cplusplus */

typedef HBITMAP VImageInfo;
typedef HMENU VMenuItemInfo;
typedef void* VTreeNodeInfo;

#define V_DEFAULT_SPACING	5
#define V_DEFAULT_PADDING	1
#define V_DEFAULT_FSIZE		130
#define V_WINDOW_MIN_WIDTH	80
#define V_WINDOW_MIN_HEIGHT	40

#define USE_V_LAYOUT
//#define V_LAYOUT_REDRAW_AFTER_LAYOUT

#define SPLIT_BAR_WIDTH 6

#define _V_OP_START(err) \
{	lock(this,__FILE__,__LINE__);	\
	if ( destroy_time != V_OBJECT_ALIVE ) {	\
		unlock(this);	\
		return err;		\
	}\
}

#define _V_OP_START_VOID \
{	lock(this,__FILE__,__LINE__);	\
	if ( destroy_time != V_OBJECT_ALIVE ) {	\
		unlock(this);	\
		return ;		\
	} \
}

#define V_OP_START _V_OP_START(V_ER_DESTROYED)



#define _V_OP_START_EX(c) \
	lock(this,__FILE__,__LINE__);	\
	if ( destroy_time != V_OBJECT_ALIVE ) {	\
	VExError er;				\
		er.code = c;			\
		er.subcode1 = 0;		\
		er.subcode2 = 0;		\
		unlock(this);	\
		return er;		\
	}

#define V_OP_START_EX _V_OP_START_EX(V_ER_DESTROYED)

#define V_OP_END	\
	unlock(this);

#define V_OP_END_IN_CALLBACK	\
	unlock(obj);

#define _V_OP_START_EX_IN_CALLBACK(c) \
	obj->lock(obj,__FILE__,__LINE__);	\
	if ( destroy_time != V_OBJECT_ALIVE ) {	\
	VExError er;				\
		er.code = c;			\
		er.subcode1 = 0;		\
		er.subcode2 = 0;		\
		unlock(obj);	\
		return er;		\
	}

#define V_OP_START_EX_IN_CALLBACK _V_OP_START_EX_IN_CALLBACK(V_ER_DESTROYED)


#define SET_RGB8_32(pix,r,g,b,a) do {     \
		((unsigned char*)(&pix))[0] = (b); \
		((unsigned char*)(&pix))[1] = (g); \
		((unsigned char*)(&pix))[2] = (r); \
		((unsigned char*)(&pix))[3] = (a); \
} while(0)
#define GET_RGB8_32(pix,r,g,b,a) do {	  \
		b = ((unsigned char*)(&pix))[0]; \
		g = ((unsigned char*)(&pix))[1]; \
		r = ((unsigned char*)(&pix))[2]; \
		a = ((unsigned char*)(&pix))[3]; \
} while(0)
#define IS_RGB8_32(pix,r,g,b,a)                \
		(((unsigned char*)(&pix))[0] == (b) && \
		 ((unsigned char*)(&pix))[1] == (g) && \
		 ((unsigned char*)(&pix))[2] == (r) && \
		 ((unsigned char*)(&pix))[3] == (a) )

#define RGB8_MAX	255

#define V_SERIALIZED_EXEC(func, arg)  ms_do((int(*)(void*))func, (void*)arg, (char*)"V_SERIALIZED_EXEC")

enum VdataType {
	VDT_NONE	= 0,
	VDT_STRING		= CF_TEXT,
	VDT_L_STRING	= 'LTXT',
	VDT_IMAGE		= CF_BITMAP
};

// MACHINE DEPENDENT CONSTANTS

extern struct VWindowsList {
	HWND				hwnd;
	struct VWindowsList	*next;
} *windows_list;


#ifdef __cplusplus
extern "C"
#endif
int _SendMessage(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);


#if 0

#include <stdio.h>

#if defined(_DEBUG) || defined(DEBUG)

#define printf MyOutputDebugString
#ifndef __cplusplus
#define inline static
#endif

inline void MyOutputDebugString(LPCSTR pszFormat, ...)
{
    va_list	argp;
    char pszBuf[1024];
    va_start(argp, pszFormat);
    vsprintf(pszBuf, pszFormat, argp);
    va_end(argp);
    OutputDebugString(pszBuf);
}

#endif

#endif


#endif
