/**********************************************************************
 
	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/VTreeView.h"
#include "VApplication.h"
#include "TreeListView.h"
#include "vobject_main.h"
#include "vwin_control.h"


void
VTreeViewImp::TLVDataFunc(
	TreeListView *		tlview,
	TreeListViewData *	data,
	bool				set)
{
	VTreeNode *node = (VTreeNode*)data->id;
	VTreeView* vobj = (VTreeView*)tlview->GetUserData();

	if ( set ) {
		if ( data->col >= 0 ) {
			if ( data->type == 's' ) {
				VTreeView::RowEdit arg = {node, data->col, nl_copy_str(std_cm, data->data.str)};
				vobj->row_edit_event(vobj, &arg);
			}
			else if ( data->type == 'b' ) {
				VTreeView::RowEdit arg = {node, data->col, data->data.flag ? VTF_TRUE : VTF_FALSE};
				vobj->row_edit_event(vobj, &arg);
			}
		}
		node->info = (void*)data->info;
		return;
	}

	if ( data->col >= 0 ) {
		if ( data->type == 's' )
			data->data.str = n_string(std_cm, (L_CHAR*)node->data[data->col]);
		else if ( data->type == 'b' )
			data->data.flag = node->data[data->col] != 0;
		else if ( data->type == 'p' )
			data->data.perc = (long)node->data[data->col];
		
		data->editable = (vobj->types[data->col] & VTT_EDITABLE) ? 1 : 0;
	}
	data->child = node->children ? 1 : 0;
	data->info = (TreeListViewNodeInfo*)node->info;
}

void
VTreeViewImp::TLVNotifFunc(
	TreeListView *		tlview,
	TreeListViewNotif *	notif)
{
	VTreeNode* node = (VTreeNode*)notif->id;
	VTreeView* vobj = (VTreeView*)tlview->GetUserData();

	switch ( notif->message ) {
	  case TLVN_EXPAND:
		VTreeNode *n;
		n = node->children;
		do {
			tlview->Insert((int)n, 0, (int)node);
			n = n->next;
		} while ( n != node->children );
		break;

	  case TLVN_REDRAW:
		int num, index, diff;
		num = tlview->SelectedItemStart();

		VTreeView::Selection *sel = new VTreeView::Selection;
		sel->n = num;
		diff = (num == vobj->selection->n) ? 0 : 1;
		if ( num ) {
			sel->keys = new void*[num];
			for ( int i = 0 ; i < num ; i++ ) {
				sel->keys[i] = ((VTreeNode*)tlview->SelectedItemIterate())->key;
				if ( !diff && num == vobj->selection->n )
					if ( sel->keys[i] != vobj->selection->keys[i] )
						diff = 1;
			}
		}

		if ( diff == 0 ) {
			delete sel;
			break;
		}
		vobj->select_event(vobj, sel);
		break;
	}
}


class VTreeViewInfo : public VInfo {
public:
	VTreeViewInfo(VObject *o, HWND h, int id, TreeListView *tl)
		: VInfo(o,h,id), tlview(tl) {}
	TreeListView *tlview;
	static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
};

LRESULT CALLBACK
VTreeViewInfo::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	VTreeViewInfo *info;
	TreeListView *tlview;
	HWND hList;

	switch ( message ) {
	case WM_DESTROY:
		tlview = ((VTreeViewInfo*)VInfo::get_from_hwnd(hWnd))->tlview;
		delete tlview;
		return	0;

	case WM_SIZE:
		info = ((VTreeViewInfo*)VInfo::get_from_hwnd(hWnd));
		if ( info == 0 )
			break;
		tlview = info->tlview;
		hList = tlview->GetHWnd();
		MoveWindow(hList, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
		return	0;

	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		EndPaint (hWnd, &ps);
		return	0;

	case WM_DRAWITEM:
		tlview = ((VTreeViewInfo*)VInfo::get_from_hwnd(hWnd))->tlview;
		tlview->DrawItems((LPDRAWITEMSTRUCT)lParam);
        return TRUE;
	}
	return ::DefWindowProc(hWnd, message, wParam, lParam);
}

class TreeViewRegister{
public:
	
	TreeViewRegister(){
		WNDCLASSEX wc;
		wc.cbSize = sizeof(WNDCLASSEX);
		wc.style = CS_PARENTDC|CS_DBLCLKS;
		wc.lpfnWndProc = VTreeViewInfo::WndProc;
		wc.cbClsExtra = 0;
		wc.cbWndExtra = sizeof(DWORD);
		wc.hInstance = ::GetModuleHandle(NULL);
		wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
		wc.hCursor = LoadCursor(NULL,IDC_ARROW);
		wc.hbrBackground = 0;
		wc.lpszMenuName = NULL;
		wc.lpszClassName = "vobj_tree_view";
		wc.hIconSm = NULL;
		
		if(!::RegisterClassEx(&wc)){
			VWinError();
			er_panic("TreeViewRegister");
		}
	}
}tree_view_register;



struct CreateTreeView {
	TreeListView *tlview;
	int n;
	const char **titles;
	short *widths;
	char *types;
};

static HWND
CreateTreeViewWindow(int style, VContainerInfo *info, int id, CreateTreeView *s)
{
	RECT r = {0,0,0,0};
	HWND ret = ::CreateWindowEx(
		WS_EX_CONTROLPARENT, 
		"vobj_tree_view",
		"tree_view",
		style,
		0,
		0,
		0,
		0,
		info->get_hwnd(),
		(HMENU)id,
		theApp->get_instance(),
		NULL);
	s->tlview = new TreeListView(&r, ret, 0, s->n, (char**)s->titles, s->widths, s->types);
	s->tlview->SetDataFunc(VTreeViewImp::TLVDataFunc);
	s->tlview->SetNotifFunc(VTreeViewImp::TLVNotifFunc);
	return ret;
}

VExError
VTreeView::create_do(const VObjectStatus* s, int flags, VObject * parent ,void * arg)
{
	int i;

	selection = new Selection;
	select_event_handler = 0;
	row_edit_event_handler = 0;
	tree_set *set = (tree_set*)arg;
	cols = set->cols;
	types = new char[cols];
	memcpy(types, set->types, cols);
	sts.min_size.w = sts.min_size.h = 40;
	
	ring = 0;
	hash = new VTreeNode*[v_tree_view_hash_size];
	for ( int key = 0 ; key < v_tree_view_hash_size ; key++ )
		hash[key] = 0;
	
	if ( flags & VSF_ATTR )
		sts.attr = s->attr;
	else
		sts.attr = 0;

	/* find container window that will own this button.*/
	VContainerInfo *container_info = VInfo::get_container_info(parent);
	int id = container_info->get_next_control_id();

	CreateTreeView ctv;
	ctv.n = cols;
	ctv.titles = new const char*[cols];
	for ( i = 0 ; i < cols ; i++ )
		ctv.titles[i] = set->title[i] ? n_string(std_cm, (L_CHAR*)set->title[i]) : "";
	ctv.widths = set->widths;
	ctv.types = new char[cols];
	for ( i = 0 ; i < cols ; i++ )
		ctv.types[i] = (set->types[i] & ~VTT_EDITABLE) == VTT_TEXT ? 's' :
					   (set->types[i] & ~VTT_EDITABLE) == VTT_BOOL ? 'b' :
					   (set->types[i] & ~VTT_EDITABLE) == VTT_PERC ? 'p' : 0;

	DWORD style=WS_CHILD|DS_CONTROL;
	if(!s->enabled)
		style |= WS_DISABLED;
	if(s->visible)
		style |= WS_VISIBLE;

	HWND hwnd = v_serialized_exec_func(CreateTreeViewWindow, style, container_info, id, &ctv);
	ctv.tlview->SetUserData(this);

	delete ctv.titles;

	info = new VTreeViewInfo(this, hwnd, id, ctv.tlview);
	
	/* set event handlers */
	info->add_message_handler(new OnCommand(::value_changed));

	return parent->add_child_do(this);
}

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

VTreeView::~VTreeView()
{
}


VExError
VTreeView::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;
};

VExError
VTreeView::set_status(const VObjectStatus *s, int flags)
{
	V_OP_START_EX
	VExError err = VObject::set_status(s,flags);
	HWND hwnd = info->get_hwnd();
	win_control_default_set_status(hwnd, s, flags, &err);

	if ( flags & VSF_ATTR ) {
		int style = ::GetWindowLong(hwnd, GWL_STYLE);
		
		switch ( s->attr & selection_mask ) {
		case select_none:
		case select_single:
			style |= LVS_SINGLESEL;
			break;
		case select_multi:
			style &= ~LVS_SINGLESEL;
			break;
		}

		if ( s->attr & (scrollbar_h | scrollbar_v) )
			style |= LVS_NOSCROLL;
		else
			style &= ~LVS_NOSCROLL;
			
		SetWindowLong(hwnd, GWL_STYLE, style);
		dynamic_cast<VTreeViewInfo*>(info)->tlview->
			SetAutoACWMode(s->attr & scrollbar_v);
	}
	
	if ( flags & VSF_CALC_MIN ) {
		// min must be specified by user
		err.subcode1 &= ~VSF_CALC_MIN;
	}

	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;
}

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

VTreeView::Selection *
VTreeView::get_selection() const
{
	Selection *ret;
	_V_OP_START(0)
	
	ret = copy_selection(this);
	
	V_OP_END
	return ret;
}


VError
VTreeViewImp::add_row(
	VTreeView *view,
	VTreeNode *node,
	void *parent_key,
	void *next_key)
{
	if ( view->add_node(&node, parent_key, next_key) ) {
		node->info = 0;
		((VTreeViewInfo*)view->info)->tlview->Insert(
			(int)node,
			node->parent ? (node->next == node->parent->children ? 0 : (int)node->next)
						 : (node->next == view->ring ? 0 : (int)node->next),
			(int)node->parent);
		return V_ER_NO_ERR;
	}
	return V_ER_PARAM;
}

VExError
VTreeView::add_row(VTreeNode *node, void *parent_key, void *next_key)
{
	VExError err = initial_VExError(V_ER_NO_ERR,0,0);
	V_OP_START_EX
	err.code = v_serialized_exec_func(VTreeViewImp::add_row,
		this, node, parent_key, next_key);
	V_OP_END
	return err;
}


VError
VTreeViewImp::remove_row(VTreeView *view, void *key)
{
	VTreeNode *node = view->get_node(key);
	if ( node ) {
		((VTreeViewInfo*)view->info)->tlview->Remove((int)node);
		view->remove_node(node);
		return V_ER_NO_ERR;
	}
	return V_ER_PARAM;
}

VExError
VTreeView::remove_row(void *key)
{
	VExError err = initial_VExError(V_ER_NO_ERR,0,0);
	V_OP_START_EX
	err.code = v_serialized_exec_func(VTreeViewImp::remove_row, this, key);
	V_OP_END
	return err;
}


VTreeNode *
VTreeViewImp::get_row(const VTreeView *view, void *key)
{
	int n = view->cols;
	VTreeNode *_node = view->get_node(key), *node = 0;
	if ( _node ) {
		node = new VTreeNode(n, *_node);
		for ( int i = 0 ; i < n ; i++ ) {
			if ( (view->types[i] & VTT_TYPE_MASK) == VTT_TEXT ) {
				node->data[i] = ll_copy_str((L_CHAR*)node->data[i]);
				set_buffer((void*)node->data[i]);
			}
		}
	}
	return node;
}

VTreeNode*
VTreeView::get_row(void *key) const
{
	VTreeNode* node;
	_V_OP_START(0)
	node = v_serialized_exec_func(VTreeViewImp::get_row, this, key);
	V_OP_END
	return node;
}


VError
VTreeViewImp::set_row(VTreeView *view, VTreeNode *node)
{
	if ( view->set_node(&node) ) {
		((VTreeViewInfo*)view->info)->tlview->Set((int)node);
		return V_ER_NO_ERR;
	}
	return V_ER_PARAM;
}

VExError
VTreeView::set_row(VTreeNode *node)
{
	VError e;
	V_OP_START_EX
	e = v_serialized_exec_func(VTreeViewImp::set_row, this, node);
	V_OP_END
	return initial_VExError(e,0,0);
}


void
VTreeView::row_edit_event_done(VTreeView::RowEdit *re)
{
}
