/**********************************************************************
 
	Copyright (C) 2003-2004
	Hirohisa MORI <joshua@nichibun.ac.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 <string.h>

#include <CDataBrowser.h>
#include <CDataBrowserImp.h>

#include "v/VTreeView.h"
#include "CClickCheckAttachment.h"
extern "C"{
LTabGroup* GetTabGroupForWindow(LView* view);
#include "memory_debug.h"
}


OSStatus
VTreeViewImp::ItemDataCallback(
	CDataBrowser *			browser,
	DataBrowserItemID		item,
	DataBrowserPropertyID	property,
	DataBrowserItemDataRef	itemData,
	Boolean					setValue)
{
	int i = property - v_base_col_id;
	OSStatus status = noErr;
	CFStringRef str;

	VTreeView *vobj = (VTreeView*)browser->GetUserCon();
	VTreeNode *node = (VTreeNode*)item;

	if ( setValue ) {
		if ( i < 0 )
			return errDataBrowserPropertyNotSupported;
		if ( (vobj->types[i] & VTT_TYPE_MASK) == VTT_TEXT ) {
			UInt32 de = CFStringGetSystemEncoding();
			status = GetDataBrowserItemDataText(itemData, &str);
			char *buffer = new char[1024];
			CFStringGetCString(str, buffer, 1024, de);
			CFRelease(str);
			VTreeView::RowEdit arg = {node, i, nl_copy_str(std_cm, buffer)};
			delete[] buffer;
			VTreeView::row_edit_event(vobj, &arg);
		}
		else if ( (vobj->types[i] & VTT_TYPE_MASK) == VTT_BOOL ) {
			UInt16 flag;
			status = GetDataBrowserItemDataButtonValue(itemData, &flag);
			if ( ! node->editable )
				status = SetDataBrowserItemDataButtonValue(itemData, !flag);
			else {
				VTreeView::RowEdit arg = {node, i, flag ? VTF_TRUE : VTF_FALSE};
				VTreeView::row_edit_event(vobj, &arg);
			}
		}
	}
	else if ( i >= 0 ) {
		if ( (vobj->types[i] & VTT_TYPE_MASK) == VTT_TEXT ) {
			UInt32 de = CFStringGetSystemEncoding();
			str = CFStringCreateWithCString(kCFAllocatorDefault, 
				n_string(std_cm, (L_CHAR*)node->data[i]), de);
			status = SetDataBrowserItemDataText(itemData, str);
			if ( str )
				CFRelease(str);
		}
		else if ( (vobj->types[i] & VTT_TYPE_MASK) == VTT_BOOL )
			status = SetDataBrowserItemDataButtonValue(itemData, node->data[i] ? 1 : 0);
		else if ( (vobj->types[i] & VTT_TYPE_MASK) == VTT_PERC )
			status = SetDataBrowserItemDataValue(itemData, (long)node->data[i]);
	}
	else switch (property) {
	  case kDataBrowserItemIsContainerProperty:
		status = SetDataBrowserItemDataBooleanValue(itemData, node->children ? TRUE : FALSE);
		break;

	  case kDataBrowserItemIsEditableProperty:
		status = SetDataBrowserItemDataBooleanValue(itemData, node->editable);
		break;
	
	  default:
		status = errDataBrowserPropertyNotSupported;
	}
	
	return status;
}

void
VTreeViewImp::ItemNotifCallback(
	CDataBrowser *				pane,
	DataBrowserItemID			item,
	DataBrowserItemNotification message)
{
	UInt32 num;
	VTreeNode* node = (VTreeNode*)item;
	switch ( message ) {
	  case kDataBrowserContainerOpened:
		{
			VTreeNode *n = node->children;
			num = 0;
			do {
				num++;
				n = n->next;
			} while ( n != node->children );
			DataBrowserItemID *items = new DataBrowserItemID[num];
			int i = 0;
			do {
				items[i++] = (DataBrowserItemID)n;
				n = n->next;
			} while ( n != node->children );
			pane->AddItem(num, items, item);
			delete[] items;
			break;
		}
	  case kDataBrowserItemSelected:
	  case kDataBrowserItemDeselected:
		vq_insert_callback((VTreeView*)pane->GetUserCon(),
			VTreeViewImp::ItemSelected, pane, 0, 0);
		break;
	}
}

V_CALLBACK_D(VTreeViewImp::ItemSelected)
{
	CDataBrowser *pane = (CDataBrowser*)user_arg;

	UInt32 num;
	Handle items;
	pane->GetSelectedItems(items, num);

	VTreeView::Selection *sel = new VTreeView::Selection;
	sel->n = num;
	if ( num ) {
		sel->keys = new void*[num];
		for ( int i = 0 ; i < num ; i++ )
			sel->keys[i] = ((VTreeNode**)*items)[i]->key;
	}

	DisposeHandle(items);
	VTreeView::select_event((VTreeView*)pane->GetUserCon(), sel);
}


VExError
VTreeView::create_do(const VObjectStatus* s, int flags, 
			VObject * nmp,void * arg)
{
	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;
	
	SPaneInfo paneInfo;
	paneInfo.superView = dynamic_cast<LView*>(nmp->get_info_this());
	if ( paneInfo.superView == 0 )
		er_panic("non-LView object cannot be parent");
	paneInfo.paneID = sts.id;
	paneInfo.width = 0;
	paneInfo.height = 0;
	paneInfo.visible = true;
	paneInfo.enabled = true;
	paneInfo.left = 0;
	paneInfo.top = 0;
	paneInfo.bindings = (SBooleanRect){false,false,false,false};
	paneInfo.userCon = (long)this;
	
	LCommander *com = GetTabGroupForWindow(paneInfo.superView);
	CDataBrowser *v = new CDataBrowser(paneInfo, com,
		sts.attr & scrollbar_h, sts.attr & scrollbar_v,
		(sts.attr & size_box_area) ? &(Rect){0,0,15,15} : 0);
	v->AddAttachment(new CClickCheckAttachment);
	v->SetUserCon((SInt32)this);
	
	switch ( sts.attr & selection_mask ) {
	  case select_none:
		v->SetSelectionFlag(kDataBrowserResetSelection);
		break;
	  case select_single:
		v->SetSelectionFlag(kDataBrowserDragSelect | kDataBrowserSelectOnlyOne
							| kDataBrowserResetSelection);
		break;
	  case select_multi:
		v->SetSelectionFlag(kDataBrowserDragSelect | kDataBrowserCmdTogglesSelection);
//							| kDataBrowserResetSelection);
		break;
	}

	for ( int i = 0 ; i < cols ; i++ ) {
		const char *title = set->title[i] ? n_string(std_cm, (L_CHAR*)set->title[i]) : "";
		UInt32 flag = (set->types[i] & VTT_EDITABLE) ?
				kDataBrowserPropertyIsMutable : 0;
		flag |= kDataBrowserListViewSelectionColumn;
		if ( (set->types[i] & VTT_TYPE_MASK) == VTT_TEXT )
			v->AddColumn(v_base_col_id+i, kDataBrowserTextType,
				set->widths[i], set->widths[i], flag, title);
		else if ( (set->types[i] & VTT_TYPE_MASK) == VTT_BOOL )
			v->AddColumn(v_base_col_id+i, kDataBrowserCheckboxType,
				set->widths[i], set->widths[i], flag, title);
		else if ( (set->types[i] & VTT_TYPE_MASK) == VTT_PERC )
			v->AddColumn(v_base_col_id+i, kDataBrowserProgressBarType,
				set->widths[i], set->widths[i], flag, title);
		else
			er_panic("VTreeView::unknown type");
	}
	
	
	v->SetDisclosureColumn(v_base_col_id);
	v->mItemDataFunc = VTreeViewImp::ItemDataCallback;
	v->mItemNotifFunc = VTreeViewImp::ItemNotifCallback;
	v->FinishCreate();

	info = v;
	return nmp->add_child_do(this);
}

void
VTreeView::destroy_do(VObject * nmp)
{
	nmp->remove_child_do(this);
	delete info;
	nmp->redraw();
	
	for ( int key = 0 ; key < v_tree_view_hash_size ; key++ )
		while( hash[key] )
			remove_node(hash[key]);
	delete hash;
	delete types;
	delete selection;
}

VTreeView::~VTreeView()
{
}

VExError
VTreeView::get_status(VObjectStatus *s, int flags) const
{
	V_OP_START_EX
	VExError err = v_get_status_standard(s, &flags, info);
	VExError err2 = VObject::get_status(s,flags);
	if ( err2.code )
		err = merge_VExError_vstatus_type(err,err2);
	V_OP_END
	return err;
};

VExError
VTreeView::set_status(const VObjectStatus *s, int flags)
{
	V_OP_START_EX
	VExError err = v_set_status_standard(s, flags, &sts, info);
	VExError err2 = VObject::set_status(s,flags);
	if ( err2.code )
		err = merge_VExError_vstatus_type(err,err2);
	V_OP_END

	if ( flags & VSF_VISIBLE )
		VLayout::mark(this);

	return err;
}

void
VTreeView::redraw(VRect* rect) const
{
	_V_OP_START()
	if ( rect )
		info->RefreshRect((Rect){rect->t, rect->l, rect->b, rect->r});
	else
		info->Refresh();
	V_OP_END
}

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

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
	
	if ( add_node(&node, parent_key, next_key) )
		if ( node->parent )
			((CDataBrowser*)info)->UpdateItem((UInt32)node->parent);
		else
			((CDataBrowser*)info)->AddItem((UInt32)node, (UInt32)node->parent);
	else
		err.code = V_ER_PARAM;
	
	V_OP_END
	return err;
}

VExError
VTreeView::remove_row(void *key)
{
	VExError err = initial_VExError(V_ER_NO_ERR,0,0);
	V_OP_START_EX
	
	VTreeNode *node = get_node(key);
	if ( node ) {
		((CDataBrowser*)info)->RemoveItem((UInt32)node, (UInt32)node->parent);
		remove_node(node);
	}
	else
		err.code = V_ER_PARAM;
	
	V_OP_END
	return err;
}

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

VExError
VTreeView::set_row(VTreeNode *node)
{
	VExError err = initial_VExError(V_ER_NO_ERR,0,0);
	V_OP_START_EX
	
	if ( set_node(&node) )
		((CDataBrowser*)info)->UpdateItem((UInt32)node->parent, (UInt32)node);
	else
		err.code = V_ER_PARAM;
	
	V_OP_END
	return err;
}

void
VTreeView::row_edit_event_done(VTreeView::RowEdit *re)
{
	((CDataBrowser*)info)->UpdateItem(
		(UInt32)re->node->parent, (UInt32)re->node,
		re->col + v_base_col_id);
}
