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

	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/v_m.h"
#include "v/VObject.h"
#include "v/VWindow.h"
#include "v/VMenu.h"

const int LINESIZE = 10;

extern DWORD current_time;

VContainerInfo::VContainerInfo(VObject *o, HWND h, int id,
		bool bkgnd_parent, bool is_dialog, bool draw_default, bool no_click,
		WNDPROC defWndProc)
	:VInfo(o,h,id),max_control_id(100),scrollable(0),
	 bkgnd_parent(bkgnd_parent),is_dialog(is_dialog),
	 draw_default(draw_default),no_click(no_click),
	 defaultWndProc(defWndProc)
{
	size.w = size.h = 0;
	min_size.w = min_size.h = 0;
	internal_size.w = internal_size.h = 0;
	memset(&sih, 0, sizeof(sih));
	memset(&siv, 0, sizeof(siv));

	hAncestorDialog = h;
	while ( hAncestorDialog && ! dynamic_cast<VContainerInfo*>(
				VInfo::get_from_hwnd(hAncestorDialog))->is_dialog )
		hAncestorDialog = GetParent(hAncestorDialog);
	if ( hAncestorDialog == NULL )
		er_panic("VContainerInfo without ancestor dialog");
}


VContainerInfo::~VContainerInfo(){
}

bool
VContainerInfo::dispatch_message_to_object(VInfo *info, MSG *msg)
{
	for(int i=0;i<MAX_MESSAGE_HANDLER;++i){
		MessageHandler *mh = info->message_handler[i];
		if(mh==0)
			return false;
		if(mh->get_messageid()==msg->message){
			mh->on_event(info->get_obj(),msg);
			return true;
		}
	}
	return false;
}

void VContainerInfo::setup_scroll()
{
	if ( scrollable ) {
		RECT r;
		v_serialized_exec_sub(GetClientRect,hwnd, &r);

		sih.cbSize = sizeof(SCROLLINFO);
		sih.fMask = SIF_POS | SIF_RANGE | SIF_PAGE | SIF_TRACKPOS;
		sih.nMin = 0;
		sih.nMax = max(internal_size.w, r.right-r.left);
		sih.nPage = r.right-r.left+1;
		sih.nPos = min(sih.nPos, sih.nMax-(sih.nPage-1));
		v_serialized_exec_sub(SetScrollInfo, hwnd, SB_HORZ, &sih, TRUE);

		siv.cbSize = sizeof(SCROLLINFO);
		siv.fMask = SIF_POS | SIF_RANGE | SIF_PAGE | SIF_TRACKPOS;
		siv.nMin = 0;
		siv.nMax = max(internal_size.h, r.bottom-r.top);
		siv.nPage = r.bottom-r.top+1;
		siv.nPos = min(siv.nPos, siv.nMax-(siv.nPage-1));
		v_serialized_exec_sub(SetScrollInfo, hwnd, SB_VERT, &siv, TRUE);
printf("setup_scroll %s %d/%d/%d %d/%d/%d\n", get_obj()->describe_self(), sih.nPos, sih.nPage, sih.nMax, siv.nPos, siv.nPage, siv.nMax);
	}
}

static int scroll_pos(WPARAM wParam, SCROLLINFO &si)
{
	int d = 0;
	switch(LOWORD(wParam)){
	case SB_LINELEFT:
	//case SB_LINEUP:
		d = -LINESIZE;
		break;
	case SB_LINERIGHT:
	//case SB_LINEDOWN:
		d  = LINESIZE;
		break;
	case SB_PAGELEFT:
	//case SB_PAGEUP:
		d = -si.nPage;
		break;
	case SB_PAGERIGHT:
	//case SB_PAGEDOWN:
		d = si.nPage;
		break;
	case SB_THUMBTRACK:
		d = HIWORD(wParam)-si.nPos;
		break;
	}
	int m = si.nMax - si.nPage - si.nPos;
	d = (-si.nPos)>(m<d?m:d)?(-si.nPos):(m<d?m:d);
	return d;
}

LRESULT VContainerInfo::dispatch_message(MSG *msg){
	VInfo* info;
	RECT r;
	HDC hdc;
	PAINTSTRUCT ps;
	HRESULT res;

	switch(msg->message){
	case WM_CTLCOLORSTATIC:
		if ( ! bkgnd_parent )
			break;
		VContainerInfo *parent;
		parent = this;
		while ( parent && parent->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;
	case WM_NCHITTEST:
		if ( no_click )
			return HTTRANSPARENT;
		break;
	case WM_PAINT:
		if ( draw_default )
			break;
		hdc = BeginPaint(msg->hwnd, &ps);
		if ( ps.fErase ) {
			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_SIZE:
		setup_scroll();
		break;
	case WM_COMMAND:
		if ( msg->lParam ) {	// from control
			info = VInfo::get_from_hwnd((HWND)msg->lParam);
			if (info)
				dispatch_message_to_object(info, msg);
		}
		break;
	case WM_NOTIFY:
		LPNMHDR nm;
		nm = (LPNMHDR)msg->lParam;
		info = VInfo::get_from_hwnd(nm->hwndFrom);
		if (info)
			dispatch_message_to_object(info, msg);
		break;
	case WM_HSCROLL:
		if ( msg->lParam ) {
			info = VInfo::get_from_hwnd((HWND)msg->lParam);
			if (info)
				dispatch_message_to_object(info, msg);
		}
		else if ( scrollable )  {	// message is sent to this window
			int dx = scroll_pos(msg->wParam,sih);
			if ( dx ) {
				sih.nPos += dx;
				SetScrollInfo(hwnd, SB_HORZ, &sih, TRUE);
				ScrollWindowEx(hwnd, -dx, 0, NULL, NULL,
					NULL, NULL, SW_ERASE | SW_INVALIDATE | SW_SCROLLCHILDREN);
				UpdateWindow(hwnd);
			}
			return 0;
		}
		break;
	case WM_VSCROLL:
		if ( msg->lParam ) {
			info = VInfo::get_from_hwnd((HWND)msg->lParam);
			if (info)
				dispatch_message_to_object(info, msg);
		}
		else if ( scrollable )  {	// message is sent to this window
			int dy = scroll_pos(msg->wParam,siv);
			if ( dy ) {
				siv.nPos += dy;
				SetScrollInfo(hwnd, SB_VERT, &siv, TRUE);
				ScrollWindowEx(hwnd, 0, -dy, NULL, NULL,
					NULL, NULL, SW_ERASE | SW_INVALIDATE | SW_SCROLLCHILDREN);
				UpdateWindow(hwnd);
			}
			return 0;
		}
		break;
	}

	if ( defaultWndProc ) {
		LRESULT ret = CallWindowProc(defaultWndProc, msg->hwnd, msg->message, msg->wParam, msg->lParam);
		return ret;
	}
	if ( is_dialog )
		return FALSE;
	return DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
}


LRESULT CALLBACK VContainerInfo::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	VContainerInfo *info = (VContainerInfo *)(VInfo::get_from_hwnd(hWnd));
	if(!info)
		return DefWindowProc( hWnd, message, wParam, lParam );
	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);
}
