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

	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 "machine/gb_windows.h"
#include <winuser.h>
#include "VApplication.h"
#include "v/VButton.h"
#include "vobject_main.h"
#include "machine/lc_util.h"

extern "C" {
#include "machine/v_types.h"
#include "memory_debug.h"
VFONT* _v_set_descriptor_get_font(const L_CHAR * descriptor,
			const LC_WRITING_STYLE * ws,
			int fsize, int *size);
}


static HWND CreateComboBoxWindow(int style, HWND hwnd, int id)
{
	return ::CreateWindowEx(
		0, 
		"COMBOBOX",
		"COMBOBOX",
		style,
		0,
		0,
		0,
		0,
		hwnd,
		(HMENU)id,
		theApp->get_instance(),
		NULL);
}

class VComboBoxInfo : public VInfo
{
  public:
	VComboBoxInfo(VObject *o, HWND h, int id) : VInfo(o,h,id),cur_sel(0) {}
	int cur_sel;
};

static void v_popup_button_on_command(VObject *arg, MSG *msg)
{
	switch (HIWORD(msg->wParam)) {
	  case CBN_SELENDOK:
		int cursel = SendMessage((HWND)msg->lParam, CB_GETCURSEL, 0, 0);
		VComboBoxInfo *info = dynamic_cast<VComboBoxInfo*>(
			VInfo::get_from_hwnd((HWND)msg->lParam));
		if ( SendMessage((HWND)msg->lParam, CB_GETITEMDATA, cursel, 0) == 0 ) {
			arg->value_changed();
			info->cur_sel = cursel;
		}
		else
			SendMessage((HWND)msg->lParam, CB_SETCURSEL, info->cur_sel, 0);
		break;
	}
}

VExError 
VPopupButton::create_do(const VObjectStatus* s, int flags, VObject *parent, void * arg)
{
	list_num = 0;
	list_lc = 0;
	list_ws = 0;
	
	DWORD style=WS_CHILD | CBS_DROPDOWNLIST | WS_TABSTOP;
	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 = v_serialized_exec_func(CreateComboBoxWindow, style, container_info->get_hwnd(), id);
	info = new VComboBoxInfo(this, hwnd, s->id);
	
	/* set event handlers */
	info->add_message_handler(new OnCommand(v_popup_button_on_command));
	
	return parent->add_child_do(this);
}

void
VPopupButton::destroy_do(VObject *parent)
{
	VButton::destroy_do(parent);
}

VPopupButton::~VPopupButton()
{
}


VExError
VPopupButton::get_status(VObjectStatus *s, int flags) const
{
	V_OP_START_EX

	if ( flags & VSF_VALUE ) {
		s->value = v_serialized_exec_func(_SendMessage,
			info->get_hwnd(), CB_GETCURSEL, 0, 0);
		flags &= ~VSF_VALUE;
	}

	VExError err = VButton::get_status(s,flags);

	V_OP_END
	return err;
};

VExError
VPopupButton::set_status(const VObjectStatus *s, int flags)
{
	V_OP_START_EX
	VExError err;
	flags &= ~VSF_DESC;	// Descriptor is not used

	if ( flags & (VSF_POSITION | VSF_SIZE) ) {
		VObjectStatus s2 = *s;
		if ( flags & VSF_SIZE ) {
			s2.size.h = sts.min_size.h;
			if ( flags & VSF_POSITION )
				s2.position.y += (s->size.h - sts.min_size.h) / 2;
		}
		else if ( flags & VSF_POSITION )
			s2.position.y += (sts.size.h - sts.min_size.h) / 2;
		err = VButton::set_status(&s2,flags);
	}
	else
		err = VButton::set_status(s,flags);

	if ( flags & VSF_VALUE ) {
		v_serialized_exec_sub(SendMessage,
			info->get_hwnd(), CB_SETCURSEL, 1, s->value);
		err.subcode1 &= ~(VSF_DESC | VSF_VERTD);
	}
	
	V_OP_END
	
	if ( flags & (VSF_ALIGN | VSF_PADDING | VSF_VISIBLE) )
		VLayout::mark(this);
	return err;
}

static void
v_popup_button_calc_min_size(VSize *min_size, HWND hwnd, char *cstr, wchar_t *wstr, VFONT *font)
{
	HDC hdc = ::GetDC(hwnd);
	
	HFONT temp_hfont;
	HFONT old_font;
	if(font){
		temp_hfont = ::CreateFontIndirectW(font);
		old_font = (HFONT)::SelectObject(hdc, temp_hfont);
	}

	SIZE s={0,0};
	if ( cstr )
		GetTextExtentPoint32A(hdc, cstr, strlen(cstr), &s);
	else if ( wstr )
		GetTextExtentPoint32W(hdc, wstr, wcslen(wstr), &s);
	min_size->w = s.cx;
	min_size->h = s.cy;

	if(font){
		::SelectObject(hdc, old_font);
		::DeleteObject(temp_hfont);
	}
	::ReleaseDC(hwnd,hdc);
}

static void
v_popup_button_set_list(
		HWND hwnd, int num, const L_CHAR **list,
		const LC_WRITING_STYLE **ws, VObjectStatus *sts)
{
	sts->min_size.w = sts->min_size.h = 0;
	SendMessage(hwnd, CB_RESETCONTENT, 0, 0);
	int size;
	for ( int i = 0 ; i < num ; i++ ) {
		if ( list[i] ) {
			/* Writing Style for each item is not supported */
			const LC_WRITING_STYLE *wsi = /*(ws && ws[i]) ? ws[i] :*/ sts->ws;
			VFONT *vf = wsi ? _v_set_descriptor_get_font(list[i], 
				wsi, sts->fsize, &size) : 0;

			char *cstr=0;
			wchar_t *wstr=0;
			l2native(&cstr, &wstr, list[i]);
			
			if ( cstr )
				SendMessageA(hwnd, CB_INSERTSTRING, -1, (LPARAM)cstr);
			else if ( wstr )
				SendMessageW(hwnd, CB_INSERTSTRING, -1, (LPARAM)wstr);
			SendMessage(hwnd, CB_SETITEMDATA, i, 0);
			
			VSize s;
			v_popup_button_calc_min_size(&s, hwnd, cstr, wstr, vf);
			if ( s.w > sts->min_size.w ) sts->min_size.w = s.w;
			if ( s.h > sts->min_size.h ) sts->min_size.h = s.h;

			if ( cstr )
				d_f_ree(cstr);
			if ( wstr )
				d_f_ree(wstr);
		}
		else {
			SendMessage(hwnd, CB_INSERTSTRING, -1, (LPARAM)"");
			SendMessage(hwnd, CB_SETITEMDATA, i, -1);
		}
	}
	SendMessage(hwnd, CB_SETCURSEL, 1, sts->value);
	SetWindowPos(hwnd, NULL, 0, 0, sts->min_size.w, 300, SWP_NOZORDER | SWP_NOMOVE); // set drop down list rect
	sts->min_size.w += 20;
	sts->min_size.h += 8;
}

VExError
VPopupButton::set_list(int num, const L_CHAR **list,
		const LC_WRITING_STYLE **ws)
{
	V_OP_START_EX
	VSize last_size = sts.min_size;
	VExError err = initial_VExError(V_ER_NO_ERR,0,0);
	
	// copy data into object
	list_num = num;
	list_lc = new L_CHAR*[num];
	if ( ws )
		list_ws = new const LC_WRITING_STYLE*[num];
	for ( int i = 0 ; i < num ; i++ ) {
		list_lc[i] = list[i] ? ll_copy_str(const_cast<L_CHAR*>(list[i])) : 0;
		if ( ws )
			list_ws[i] = ws[i];
	}

	// set to the popup button
	HWND hwnd = info->get_hwnd();
		v_serialized_exec_sub(v_popup_button_set_list, hwnd, num, list, ws, &sts);

	V_OP_END
	
	if ( sts.min_size.w != last_size.w ||
		 sts.min_size.h != last_size.h )
		VLayout::mark(this);
	return err;
};


VExError
VPopupButton::get_list(int *num, L_CHAR ***list, const LC_WRITING_STYLE ***ws)
{
	V_OP_START_EX
	VExError err = initial_VExError(V_ER_NO_ERR,0,0);
	
	*num = list_num;
	*list = (L_CHAR**)d_alloc(sizeof(L_CHAR*)*list_num);
	set_buffer(*list);
	if ( ws ) {
		if ( list_ws == 0 )
			*ws = 0;
		else {
			*ws = (const LC_WRITING_STYLE**)d_alloc(sizeof(LC_WRITING_STYLE*)*list_num);
			set_buffer(*ws);
		}
	}
	for ( int i = 0 ; i < list_num ; i++ ) {
		if ( list_lc[i] ) {
			(*list)[i] = ll_copy_str(const_cast<L_CHAR*>(list_lc[i]));
			set_buffer((*list)[i]);
		}
		else
			(*list)[i] = 0;
		if ( ws && *ws )
			(*ws)[i] = list_ws[i];
	}
	
	V_OP_END
	return err;
}
