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

	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 <stdlib.h>

#include "v/VEditText.h"
#include "VApplication.h"
#include "vobject_main.h"
#include "vwin_control.h"
#include "machine/lc_util.h"

extern "C" {
#include "machine/v_types.h"
#include "memory_debug.h"
}


static HWND CreateEditWindow(int style, HWND hwnd, int id, short max_len){
	HWND ret = ::CreateWindowEx(
		0, 
		"EDIT",
		"EDIT",
		style,
		0,
		0,
		0,
		0,
		hwnd,
		(HMENU)id,
		theApp->get_instance(),
		NULL);
	SendMessage(ret,EM_LIMITTEXT,max_len,0);
	return ret;
}

static void
v_edit_text_on_command(VObject *obj, MSG *msg)
{
	VEditText *edit = dynamic_cast<VEditText*>(obj);
	if ( edit == 0 )
		er_panic("v_edit_text_on_command");
	switch(HIWORD(msg->wParam)) {
	case EN_UPDATE:
		edit->descriptor_changed();
		break;
	case EN_SETFOCUS:
		edit->focus_event(true);
		break;
	case EN_KILLFOCUS:
		edit->focus_event(false);
		break;
	}
}

static void
v_edit_text_calc_min_size(VSize *min_size, short min_len, VInfo *info)
{
	HDC hdc = ::GetDC(info->get_hwnd());
	
	HFONT temp_hfont;
	HFONT old_font;
	VFONT *font;
	font = info->get_font();
	if(font){
		temp_hfont = ::CreateFontIndirectW(font);
		old_font = (HFONT)::SelectObject(hdc, temp_hfont);
	}

	SIZE s={0,0};
	GetTextExtentPoint32(hdc, TEXT("M"), 1, &s);
	min_size->w = s.cx * min_len;
	min_size->h = s.cy + 4;

	if(font){
		::SelectObject(hdc, old_font);
		::DeleteObject(temp_hfont);
	}
	::ReleaseDC(info->get_hwnd(),hdc);
}

VExError 
VEditText::create_do(const VObjectStatus* s, int flags, VObject *parent, void * arg)
{
	edit_text_set *set = (edit_text_set*)arg;
	min_len = set->min_len;

	DWORD style= WS_CHILD | WS_BORDER | ES_LEFT | ES_AUTOHSCROLL | 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;
	hwnd = v_serialized_exec_func(CreateEditWindow,
		style, container_info->get_hwnd(), id, set->max_len);
	info = new VInfo(this, hwnd, id);
	
	sts.min_size.w = 100;
	sts.min_size.h = 24;

	info->add_message_handler(new OnCommand(v_edit_text_on_command));
	return parent->add_child_do(this);
}

void
VEditText::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();
}

VEditText::~VEditText()
{
}

VExError
VEditText::get_status(VObjectStatus *s, int flags) const
{
	V_OP_START_EX
	
	if ( flags & (VSF_VALUE | VSF_DESC) ) {
		int len = GetWindowTextLength(info->get_hwnd());
		char *buffer = new char[len+1];
		GetWindowText(info->get_hwnd(), buffer, len+1);

		if ( (flags & VSF_VALUE) && (sts.attr & integer_only) )
			s->value = atoi(buffer);

		if ( flags & VSF_DESC )
			s->descriptor = l_string(std_cm, buffer);

		delete buffer;
		flags &= ~(VSF_VALUE | VSF_DESC);
	}
	VExError err = VObject::get_status(s,flags);
	win_control_default_get_status(info->get_hwnd(), s, flags, &err);

	V_OP_END
	return err;
};

VExError
VEditText::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();
	if ( s ) {
		VObjectStatus s2 = *s;
		if ( flags & VSF_SIZE )
			s2.size.h = sts.min_size.h;
		if ( flags & VSF_POSITION )
			s2.position.y += (sts.size.h - sts.min_size.h) / 2;
		win_control_default_set_status(hwnd, &s2, flags&~VSF_DESC, &err);
	}

	if ( flags & VSF_CALC_MIN ) {
		// min is calculated when desc is set
		err.subcode1 &= ~VSF_CALC_MIN;
	}
	
	if ( flags & VSF_ATTR ) {
		int style = GetWindowLong(info->get_hwnd(), GWL_STYLE);
		if ( s->attr & integer_only )
			SetWindowLong(info->get_hwnd(), GWL_STYLE, style | ES_NUMBER);
		else
			SetWindowLong(info->get_hwnd(), GWL_STYLE, style & ~ES_NUMBER);
	}

	if ( (flags & VSF_VALUE) && (sts.attr & integer_only) ) {
		char buf[32];
		sprintf(buf, "%d", s->value);
		SetWindowTextA(info->get_hwnd(),buf);
	}

	if ( flags & VSF_DESC ) {
		char *cstr=0;
		wchar_t *wstr=0;
		if ( s->descriptor ) {
			l2native(&cstr, &wstr, s->descriptor);
			if ( cstr )
				v_serialized_exec_sub(SetWindowTextA, info->get_hwnd(), cstr);
			else if ( wstr )
				v_serialized_exec_sub(SetWindowTextW, info->get_hwnd(), wstr);
			if ( cstr )
				d_f_ree(cstr);
			if ( wstr )
				d_f_ree(wstr);
		} else
			v_serialized_exec_sub(SetWindowTextA, info->get_hwnd(), "");
		err.subcode1 &= ~VSF_DESC;
	}

	if ( flags & (VSF_FSIZE | VSF_WS) ) {
		v_set_descriptor(info, 0, sts.ws, sts.fsize, 0, 0);
		v_serialized_exec_sub(v_edit_text_calc_min_size, &sts.min_size, min_len, info);
		err.subcode1 &= ~(VSF_DESC | VSF_VERTD | VSF_FSIZE | VSF_WS);
	}

	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)) ||
		 (flags & (VSF_DESC | VSF_WS | VSF_FSIZE)) &&
			(sts.min_size.w != last_size.w ||
			 sts.min_size.h != last_size.h) )
		VLayout::mark(this);
	return err;
}

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


bool
VEditText::command_status(int type) const
{
	unsigned sel;
	switch ( type ) {
	  case VMT_UNDO:
		return SendMessage(info->get_hwnd(),EM_CANUNDO,0,0);
	  case VMT_REDO:
		return false;
	  case VMT_CUT:
	  case VMT_COPY:
	  case VMT_CLEAR:
		sel = SendMessage(info->get_hwnd(),EM_GETSEL,0,0);
		return HIWORD(sel)-LOWORD(sel) != 0;
	  case VMT_PASTE:
		return IsClipboardFormatAvailable(CF_TEXT);
	  case VMT_SEL_ALL:
		TCHAR c0[2];
		GetWindowText(info->get_hwnd(), c0, 2);
		return c0[0] != 0;
	}
	return false;
}

bool
VEditText::obey_command(int type)
{
	switch ( type ) {
	  case VMT_UNDO:
		SendMessage(info->get_hwnd(),WM_UNDO,0,0);
		return true;
	  case VMT_CUT:
		SendMessage(info->get_hwnd(),WM_CUT,0,0);
		return true;
	  case VMT_COPY:
		SendMessage(info->get_hwnd(),WM_COPY,0,0);
		return true;
	  case VMT_PASTE:
		SendMessage(info->get_hwnd(),WM_PASTE,0,0);
		return true;
	  case VMT_CLEAR:
		SendMessage(info->get_hwnd(),WM_CLEAR,0,0);
		return true;
	  case VMT_SEL_ALL:
		SendMessage(info->get_hwnd(),EM_SETSEL,0,32767);
		return true;
	}
	return false;
}

void
VEditText::focus()
{
	v_serialized_exec_sub(SetFocus, info->get_hwnd());
}
