/**********************************************************************

	Copyright (C) 2003-2004
	Hirohisa MORI <joshua@nichibun.ac.jp>
	Tomohito Nakajima <nakajima@zeta.co.jp>
	Tomoki Sekiyama <sekiyama@yahoo.co.jp>

	This program is free software; you can redistribute it
	and/or modify it under the terms of the GLOBALBASE
	Library General Public License (G-LGPL) as published by

	http://www.globalbase.org/

	This program is distributed in the hope that it will be
	useful, but WITHOUT ANY WARRANTY; without even the
	implied warranty of MERCHANTABILITY or FITNESS FOR A
	PARTICULAR PURPOSE.

**********************************************************************/


#include "v/VBackColorView.h"
#include "VApplication.h"
#include "vobject_main.h"
#include "vwin_control.h"


class VBackColorViewInfo : public VContainerInfo
{
public:
	VBackColorViewInfo(VObject *o, HWND h, int id)
		: VContainerInfo(o,h,id,0,1,1), brush(0)
	{
		set_color(0xffffffff);
	}
	virtual ~VBackColorViewInfo()
	{
		if ( brush )
			DeleteObject(brush);
	}

	COLORREF get_color() { return color; }

	void set_color(COLORREF c) {
		color = c;
		if ( brush )
			DeleteObject(brush);
		if ( c != 0 )
			brush = CreateSolidBrush(c);
		::InvalidateRect(get_hwnd(), NULL, TRUE);
		::UpdateWindow(get_hwnd());
	}

	static BOOL CALLBACK BackColorViewProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
protected:
	virtual LRESULT dispatch_message(MSG *msg);

private:
	HBRUSH brush;
	COLORREF color;
};


BOOL CALLBACK
VBackColorViewInfo::BackColorViewProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	extern DWORD current_time;
	VBackColorViewInfo *info = dynamic_cast<VBackColorViewInfo *>(VInfo::get_from_hwnd(hWnd));
	if(!info)
		return FALSE;
	MSG msg;
	memset(&msg, 0, sizeof(msg));
	msg.hwnd = hWnd;
	msg.message = message;
	msg.lParam = lParam;
	msg.wParam = wParam;
	msg.time = current_time;

	return info->dispatch_message(&msg);
}

LRESULT
VBackColorViewInfo::dispatch_message(MSG *msg)
{
	VBackColorViewInfo *info;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (msg->message) {
		case WM_CTLCOLORSTATIC:
			if ( color == 0 ) {
				VContainerInfo *parent;
				parent = dynamic_cast<VContainerInfo*>(
					get_from_hwnd(GetParent(get_hwnd())));
				while ( parent && parent->get_bkgnd_parent() )
					parent = dynamic_cast<VContainerInfo*>(
						get_from_hwnd(GetParent(parent->get_hwnd())));
				if ( parent ) {
					msg->hwnd = parent->get_hwnd();
					return SendMessage(msg->hwnd, msg->message, msg->wParam, msg->lParam);
				}
				break;
			}
			info = (VBackColorViewInfo*)VBackColorViewInfo::get_from_hwnd(msg->hwnd);
			SetBkColor((HDC)msg->wParam, info->color);
			return (LRESULT)info->brush;
		case WM_PAINT:
			info = (VBackColorViewInfo*)VBackColorViewInfo::get_from_hwnd(msg->hwnd);
			hdc = BeginPaint(msg->hwnd, &ps);
			if ( color ) {
				FillRect(hdc, &ps.rcPaint, info->brush);
			}
			else {
				HBRUSH bg = (HBRUSH)SendMessage(hAncestorDialog,
					WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)msg->hwnd);
				FillRect(hdc, &ps.rcPaint, bg);
			}
			EndPaint(msg->hwnd, &ps);
			return 1;
		case WM_LBUTTONDOWN:
		case WM_LBUTTONDBLCLK:
			get_obj()->value_changed();
			break;
	}
	return VContainerInfo::dispatch_message(msg);
}


static HWND v_make_back_color_dialog(DWORD style, HWND parent)
{
	struct {
		DLGTEMPLATE	h;
		USHORT		menu;
		USHORT		cls;
		USHORT		title;
	} tmp;
	tmp.h.style = style;
	tmp.h.dwExtendedStyle = 0;
	tmp.h.cdit = 0;
	tmp.h.x = 0;
	tmp.h.y = 0;
	tmp.h.cx = 10;
	tmp.h.cy = 10;
	tmp.menu = 0;
	tmp.cls = 0;
	tmp.title = 0;
	return CreateDialogIndirect(GetModuleHandle(NULL),
		(DLGTEMPLATE*)&tmp, parent, VBackColorViewInfo::BackColorViewProc);
}

VExError 
VBackColorView::create_do(const VObjectStatus* s, int flags, VObject *parent, void * arg)
{
	DWORD style=WS_CHILD|DS_CONTROL;
	if(!s->enabled){
		style |= WS_DISABLED;
	}
	if(s->visible){
		style |= WS_VISIBLE;
	}
	
	/* find container window that will own this button.*/
	VContainerInfo *container_info = VInfo::get_container_info(parent);
	int id = container_info->get_next_control_id();
	HWND hwnd;
	hwnd = v_serialized_exec_func(v_make_back_color_dialog,
		style, container_info->get_hwnd());
	info = new VBackColorViewInfo(this, hwnd, id);
	
	return parent->add_child_do(this);
}

void
VBackColorView::destroy_do(VObject *parent)
{
	parent->remove_child_do(this);
	if(info){
		v_serialized_exec_sub(::DestroyWindow, info->get_hwnd());
		delete info;
		info = NULL;
	}
	sts.parent->redraw();
}

VBackColorView::~VBackColorView()
{
}

VExError
VBackColorView::get_status(VObjectStatus *s, int flags) const
{
	V_OP_START_EX
	VExError err = VObject::get_status(s,flags);
	win_control_default_get_status(info->get_hwnd(), s, flags, &err);
	V_OP_END
	return err;
};

static void
set_color(VBackColorViewInfo *info, COLORREF color)
{
	info->set_color(color);
}

VExError
VBackColorView::set_status(const VObjectStatus *s, int flags)
{
	V_OP_START_EX
	VSize last_size = sts.min_size;
	VExError err = VObject::set_status(s,flags);
	HWND hwnd = info->get_hwnd();
	win_control_default_set_status(hwnd, s, flags&~VSF_DESC, &err);

	if ( flags & VSF_ATTR ) {
		unsigned char r, g, b, a;
		GET_RGB8_32(s->attr,r,g,b,a);
		v_serialized_exec_sub(set_color, (VBackColorViewInfo*)info, a==0?0:RGB(r,g,b));
	}
	
	if ( flags & VSF_CALC_MIN ) {
		if (sts.children)
			sts.children->object->set_status(0, VSF_CALC_MIN);
		VLayout layout;
		layout.layout_in_frame(this, true);
		sts.min_size = layout.parent_min_size();
		err.subcode1 &= ~ VSF_CALC_MIN;
	}
	if ( flags & VSF_LAYOUT ) {
		VLayout layout;
		layout.layout_in_frame(this);
		layout.do_layout(this);
		err.subcode1 &= ~ VSF_LAYOUT;
	}

	if ( flags & VSF_VISIBLE ) {
		if ( s->visible != (::IsWindowVisible(info->get_hwnd())==TRUE) ){
			v_serialized_exec_sub(::ShowWindow, hwnd, s->visible ? SW_SHOW : SW_HIDE);
		}
		err.subcode1 &= ~VSF_VISIBLE;
	}

	V_OP_END
	
	if ( flags & (VSF_ALIGN | VSF_PADDING | VSF_VISIBLE) )
		VLayout::mark(this);
	return err;
}


VExError VBackColorView::add_child_do(VObject* child)
{
	return initial_VExError(V_ER_NO_ERR,0,0);
}

void
VBackColorView::remove_child_do(VObject* child)
{

}

void
VBackColorView::child_status_changed(VObject* child, VInfo* info)
{
	// do nothing - object is marked when status is changed
}

void
VBackColorView::redraw(VRect *rect) const
{
	if ( ! info )
		return;
	_V_OP_START_VOID
	win_redraw(info->get_hwnd(), rect);
	V_OP_END
}
