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

#include "v/VTreeView.h"
#include "v/vobj_utils.h"
#include "StDisposer.h"

extern "C" {
#include "memory_debug.h"
#include "xl.h"
void gc_gb_sexp(XL_SEXP * p);

char vobj_col_type_from_name(L_CHAR *column);
int vobj_tree_view_attr_from_sf(XL_SYM_FIELD *sf);
int make_node_from_sexp(
		VTreeNode &node,
		XL_SEXP *key,
		XL_SEXP *data,
		VTreeView *obj,
		bool editable);
XL_SEXP * get_sexp_from_node(const VTreeNode *node, VTreeView *obj);
XL_SEXP * vobj_tree_view_row_edit_translator(VObject *obj, void *sys_arg);
XL_SEXP * vobj_VTreeView(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf);
XL_SEXP * vobj_AddRow(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf);
XL_SEXP * vobj_RemoveRow(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf);
XL_SEXP * vobj_GetRow(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf);
XL_SEXP * vobj_SetRow(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf);
XL_SEXP * vobj_GetSelectedRows(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf);

char
vobj_col_type_from_name(L_CHAR *column)
{
#define RET_TYPE(type) if ( l_strcmp(column, l_string(std_cm, #type)) == 0 ) return VTT_##type
	if ( column ) {
		RET_TYPE(BOOL);
		RET_TYPE(PERC);
		RET_TYPE(TEXT);
	}
	return 0;
}

int
vobj_tree_view_attr_from_sf(XL_SYM_FIELD *sf)
{
	L_CHAR *l_attr = get_sf_attribute(sf, l_string(std_cm, "selection"));
	int attr = 0;
	if ( l_attr ) {
		if ( l_strcmp(l_attr, l_string(std_cm, "none")) == 0 )
			attr = VTreeView::select_none;
		else if ( l_strcmp(l_attr, l_string(std_cm, "single")) == 0 )
			attr = VTreeView::select_single;
		else if ( l_strcmp(l_attr, l_string(std_cm, "multi")) == 0 )
			attr = VTreeView::select_multi;
	}

	l_attr = get_sf_attribute(sf, l_string(std_cm, "scrollbar_h"));
	if ( l_attr && atoi(n_string(std_cm, l_attr)) )
		attr |= VTreeView::scrollbar_h;

	l_attr = get_sf_attribute(sf, l_string(std_cm, "scrollbar_v"));
	if ( l_attr && atoi(n_string(std_cm, l_attr)) )
		attr |= VTreeView::scrollbar_v;

	l_attr = get_sf_attribute(sf, l_string(std_cm, "size_box_area"));
	if ( l_attr && atoi(n_string(std_cm, l_attr)) )
		attr |= VTreeView::size_box_area;

	return attr;
}

int
make_node_from_sexp(
		VTreeNode &node,
		XL_SEXP *key,
		XL_SEXP *data,
		VTreeView *obj,
		bool editable)
{
	node.key = (void*)(unsigned int)key->integer.data;
	node.editable = editable;
	for ( int i = 0 ; i < obj->get_n_cols() ; i++ ) {
		XL_SEXP *datum = get_el(data, i);
		switch ( obj->get_types()[i] & VTT_TYPE_MASK ) {
		  case VTT_TEXT:
			if ( get_type(datum) == XLT_NULL )
				node.data[i] = 0;
			else if ( get_type(datum) == XLT_STRING )
				node.data[i] = datum->string.data;
			else goto type_missmatch;
			break;

		  case VTT_PERC:
			if ( get_type(datum) == XLT_INTEGER )
				node.data[i] = (void*)
				  (unsigned int)datum->integer.data;
			else goto type_missmatch;
			break;

		  case VTT_BOOL:
			if ( get_type(datum) == XLT_NULL )
				node.data[i] = 0;
			else if ( get_type(datum) == XLT_INTEGER )
				node.data[i] = datum->integer.data ? VTF_TRUE : VTF_FALSE;
			else goto type_missmatch;
			break;

		  default:
			er_panic("unknown type");
		}
	}
	return 0;

type_missmatch:
	return -1;
}

XL_SEXP *
get_sexp_from_node(const VTreeNode *node, VTreeView *obj)
{
	XL_SEXP *list = 0;
	int i = obj->get_n_cols();
	while ( --i >= 0 ) {
		switch ( obj->get_types()[i] & VTT_TYPE_MASK ) {
		  case VTT_TEXT:
			list = cons(get_string((L_CHAR*)node->data[i]), list);
			break;

		  case VTT_PERC:
			list = cons(get_integer((int)node->data[i], 0), list);
			break;

		  case VTT_BOOL:
			list = cons(get_integer(node->data[i] != 0, 0), list);
			break;
		
		  default:
			er_panic("unknown type");
		}
	}
	return list;
}

XL_SEXP *
vobj_tree_view_row_edit_translator(VObject *obj, void *sys_arg)
{
	VTreeView::RowEdit *re = (VTreeView::RowEdit*)sys_arg;
	return List(get_integer((int)re->node->key, 0),
				get_integer(re->col, 0),
				get_sexp_from_node(re->node, (VTreeView*)obj),
				-1);
}

XL_SEXP *
vobj_VTreeView(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf)
{
	int cols = 0;
	XL_SEXP * _ref;
	VObjectStatus sts;
/*	
	VObject::FreeList *free_list;
	int flags = get_sts_from_sf(&sts,0, sf, &free_list);
	VExError err;

	XL_SEXP *p = eval(env, n_get_symbol("__parent"));
	if ( get_type(p) == XLT_ERROR )
		return p;
	if ( get_type(p) != XLT_INTEGER )
		return vobj_get_error(initial_VExError(V_ER_PARENT,0,0), arg);
	VObject *parent = VObject::get_object_by_id(p->integer.data);
*/	
	XLISP_ENV *nenv = 0;
	XL_SEXP *list, *col, *sym;
	XL_SEXP *cols_list = 0,  *args_list = 0;

	for ( list = cdr(arg) ; get_type(list) == XLT_PAIR ; list = cdr(list) ) {
		col = car(list);
		if ( get_type(col) == XLT_PAIR ) {
			sym = car(col);
			if ( get_type(sym) == XLT_SYMBOL &&
					 l_strcmp(sym->symbol.data, l_string(std_cm, "Column")) == 0 ) {
				cols++;
				cols_list = cons(sym, cols_list);
				continue;
			}
		}
		args_list = cons(col, args_list);
	}
	if ( get_type(list) )
		goto type_missmatch;
	cols_list = reverse(cols_list);
	args_list = reverse(args_list);
	
	VTreeView *obj;
	{
		char *types = new char[cols];
		const L_CHAR **titles = new const L_CHAR*[cols];
		short *widths = new short[cols];
		StDisposer<char> sd_types(types);
		StDisposer<const L_CHAR*> sd_titles(titles);
		StDisposer<short> sd_widths(widths);
		
		list = cols_list;
		for ( int i = 0 ; i < cols ; i++, list = cdr(list) ) {
			sym = car(list);
			L_CHAR *l_width = get_symbol_field(sym, l_string(std_cm, "width"));
			if ( ! l_width )
				return get_error(
					arg->h.file,
					arg->h.line,
					XLE_PROTO_INV_PARAM,
					l_string(std_cm,"VTreeView::Column"),
					n_get_string("width is not specified"));
			widths[i] = atoi(n_string(std_cm, l_width));
			L_CHAR *l_type = get_symbol_field(sym, l_string(std_cm, "type"));
			types[i] = vobj_col_type_from_name(l_type);
			if ( types[i] == 0 )
				return get_error(
					arg->h.file,
					arg->h.line,
					XLE_PROTO_INV_PARAM,
					l_string(std_cm,"VTreeView::Column"),
					n_get_string("unknown type"));
			L_CHAR *l_editable = get_symbol_field(sym, l_string(std_cm, "editable"));
			if ( l_editable && atoi(n_string(std_cm, l_editable)) )
				types[i] |= VTT_EDITABLE;
			titles[i] = get_symbol_field(sym, l_string(std_cm, "title"));
		}
		
		// sts.parent = parent;
		sts.attr = vobj_tree_view_attr_from_sf(sf);
		// flags |= VSF_PARENT | VSF_ATTR;
		
		// obj = VTreeView::create(&sts, flags, cols, types, titles, widths, &err);

		VTreeView::tree_set set;
		set.cols = cols;
		set.types = types;
		set.title = titles;
		set.widths = widths;
		_ref = get_refered_object<VTreeView>
				(&obj,env,arg,sf,VO_TREV,"VTreeView",&sts,VSF_ATTR,&set);
	}
	if ( get_type(_ref) == XLT_ERROR )
		return _ref;
	
	L_CHAR *attr;
	L_CHAR *func;
	if ( ( attr = get_sf_attribute(sf, l_string(std_cm, "select_event_handler")) ) ) {
		func = ll_copy_str(attr);
		obj->set_select_event_handler(
			vobj_callback_glue, func);
		obj->free_on_release(func);
	}
	if ( ( attr = get_sf_attribute(sf, l_string(std_cm, "row_edit_event_handler")) ) ) {
		VSysArgTranslator *vsat = (VSysArgTranslator*)
				d_alloc(sizeof(VSysArgTranslator));
		vsat->sig = 0;
		vsat->func = func = ll_copy_str(attr);
		vsat->translator = vobj_tree_view_row_edit_translator;
		
		obj->set_row_edit_event_handler(
			vobj_callback_glue, vsat);
		obj->free_on_release(vsat);
		obj->free_on_release(func);
	}
	
	gc_push(0,0,"vobj_VTreeView");
	nenv = new_env_pair(env, a);
	set_env(nenv, l_string(std_cm,"__target"), get_integer(sts.id, 0));
	for ( ; get_type(args_list) ; args_list = cdr(args_list) ) {
		XL_SEXP *res = eval(nenv, car(args_list));
		if ( get_type(res) == XLT_ERROR ) {
			gc_pop(res,(void(*)())gc_gb_sexp);
			return res;
		}
	}
	gc_pop(0,0);
	
	if ( get_type(_ref) == XLT_ERROR )
		return _ref;
	switch ( get_type(_ref) ) {
	case XLT_INTEGER:
		return vobj_get_id_list(_ref->integer.data,0,sf,0);
	case XLT_ERROR:
		return _ref;
	}
	return 0;
	
type_missmatch:
	return get_error(
		arg->h.file,
		arg->h.line,
		XLE_SEMANTICS_TYPE_MISSMATCH,
		l_string(std_cm, "VTreeView"),
		n_get_string("type missmatch"));
}


XL_SEXP *
vobj_AddRow(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf)
{
	VTreeView *obj;
	VExError err;
	void *pk, *nk;
	XL_SEXP *target = eval(env,n_get_symbol("__target"));
	XL_SEXP *key = get_el(arg, 1);
	XL_SEXP *data = get_el(arg, 2);
	XL_SEXP *parent_key = get_el(arg, 3);
	XL_SEXP *next_key = get_el(arg, 4);
	XL_SEXP *editable = get_el(arg, 5);
	if ( get_type(target) != XLT_INTEGER || get_type(key) != XLT_INTEGER ||
				get_type(data) != XLT_PAIR ||
				get_type(parent_key) != XLT_INTEGER && get_type(parent_key) != XLT_NULL ||
				get_type(next_key) != XLT_INTEGER && get_type(next_key) != XLT_NULL)
			goto type_missmatch;
	obj = dynamic_cast<VTreeView*>(
		VObject::get_object_by_id(target->integer.data));
	if ( obj == 0 )
		return vobj_get_error(initial_VExError(V_ER_NOT_FOUND,0,0), arg);
	
	pk = get_type(parent_key) ? (void*)
			(unsigned int)parent_key->integer.data : 0;
	nk = get_type(next_key) ? (void*)
			(unsigned int)next_key->integer.data : 0;
	
	{
		VTreeNode node(obj->get_n_cols());
		if ( make_node_from_sexp(node, key, data, obj, editable ? editable->integer.data : 0) )
			goto type_missmatch;
		err = obj->add_row(&node, pk, nk);
		return vobj_get_error(err, arg);
	}
	
type_missmatch:
		return get_error(
			arg->h.file,
			arg->h.line,
			XLE_SEMANTICS_TYPE_MISSMATCH,
			l_string(std_cm,"AddRow"),
			n_get_string("type missmatch"));
}

XL_SEXP *
vobj_RemoveRow(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf)
{
	VTreeView *obj;
	VExError err;
	XL_SEXP *target = eval(env,n_get_symbol("__target"));
	XL_SEXP *key = get_el(arg, 1);
	if ( get_type(target) != XLT_INTEGER || get_type(key) != XLT_INTEGER )
		return get_error(
			arg->h.file,
			arg->h.line,
			XLE_SEMANTICS_TYPE_MISSMATCH,
			l_string(std_cm,"RemoveRow"),
			n_get_string("type missmatch"));
	obj = dynamic_cast<VTreeView*>(
		VObject::get_object_by_id(target->integer.data));
	if ( obj == 0 )
		return vobj_get_error(initial_VExError(V_ER_NOT_FOUND,0,0), arg);
	
	err = obj->remove_row((void*)(unsigned int)key->integer.data);
	return vobj_get_error(err, arg);
}

XL_SEXP *
vobj_GetRow(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf)
{
	VTreeView *obj;
	const VTreeNode *node;
	XL_SEXP *target = eval(env,n_get_symbol("__target"));
	XL_SEXP *key = get_el(arg, 1);
	if ( get_type(target) != XLT_INTEGER || get_type(key) != XLT_INTEGER )
		return get_error(
			arg->h.file,
			arg->h.line,
			XLE_SEMANTICS_TYPE_MISSMATCH,
			l_string(std_cm,"GetRow"),
			n_get_string("type missmatch"));
	obj = dynamic_cast<VTreeView*>(
		VObject::get_object_by_id(target->integer.data));
	if ( obj == 0 )
		return vobj_get_error(initial_VExError(V_ER_NOT_FOUND,0,0), arg);
	
	node = obj->get_row((void*)(unsigned int)key->integer.data);
	if ( node == 0 )
		return vobj_get_error(initial_VExError(V_ER_PARAM, 0, 0), arg);
	
	return get_sexp_from_node(node, obj);
}

XL_SEXP *
vobj_SetRow(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf)
{
	VTreeView *obj;
	VExError err;
	XL_SEXP *target = eval(env,n_get_symbol("__target"));
	XL_SEXP *key = get_el(arg, 1);
	XL_SEXP *data = get_el(arg, 2);
	XL_SEXP *editable = get_el(arg, 3);
	if ( get_type(target) != XLT_INTEGER || get_type(key) != XLT_INTEGER ||
				get_type(data) != XLT_PAIR || editable && get_type(editable) != XLT_INTEGER )
		goto type_missmatch;
	obj = dynamic_cast<VTreeView*>(
		VObject::get_object_by_id(target->integer.data));
	if ( obj == 0 )
		return vobj_get_error(initial_VExError(V_ER_NOT_FOUND,0,0), arg);
	
	{
		VTreeNode node(obj->get_n_cols());
		if ( make_node_from_sexp(node, key, data, obj, editable ? editable->integer.data : 0) )
			goto type_missmatch;
		err = obj->set_row(&node);
		return vobj_get_error(err, arg);
	}

type_missmatch:
	return get_error(
		arg->h.file,
		arg->h.line,
		XLE_SEMANTICS_TYPE_MISSMATCH,
		l_string(std_cm,"AddRow"),
		n_get_string("type missmatch"));
}

XL_SEXP *
vobj_GetSelectedRows(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf)
{
	VTreeView *obj;
	XL_SEXP *target = get_el(arg, 1);
	if ( get_type(target) != XLT_INTEGER )
		return get_error(
			arg->h.file,
			arg->h.line,
			XLE_SEMANTICS_TYPE_MISSMATCH,
			l_string(std_cm,"GetSelectedRows"),
			n_get_string("type missmatch"));
	obj = dynamic_cast<VTreeView*>(
		VObject::get_object_by_id(target->integer.data));
	if ( obj == 0 )
		return vobj_get_error(initial_VExError(V_ER_NOT_FOUND,0,0), arg);
	
	VTreeView::Selection *sel = obj->get_selection();
	if ( sel == 0 )
		return vobj_get_error(initial_VExError(V_ER_DESTROYED,0,0), arg);
	
	XL_SEXP *ret = 0;
	for ( int i = sel->n-1 ; i >= 0 ; i-- )
		ret = cons(get_integer((int)sel->keys[i], 0), ret);
	return ret;
}

void
init_VTreeView(XLISP_ENV *env)
{
	XLISP_ENV *env_VTreeView = new_env(env);

	set_env(env,l_string(std_cm,"VTreeView"),
		get_func_prim((XL_SEXP*(*)())vobj_VTreeView,FO_NORMAL,env_VTreeView,2,-1));

	set_env(env_VTreeView,l_string(std_cm,"AddRow"),
		get_func_prim((XL_SEXP*(*)())vobj_AddRow,FO_APPLICATIVE,0,5,6));
	set_env(env_VTreeView,l_string(std_cm,"RemoveRow"),
		get_func_prim((XL_SEXP*(*)())vobj_RemoveRow,FO_APPLICATIVE,0,2,2));
	set_env(env_VTreeView,l_string(std_cm,"GetRow"),
		get_func_prim((XL_SEXP*(*)())vobj_GetRow,FO_APPLICATIVE,0,2,2));
	set_env(env_VTreeView,l_string(std_cm,"SetRow"),
		get_func_prim((XL_SEXP*(*)())vobj_SetRow,FO_APPLICATIVE,0,3,4));
	set_env(env_VTreeView,l_string(std_cm,"GetSelectedRows"),
		get_func_prim((XL_SEXP*(*)())vobj_GetSelectedRows,FO_APPLICATIVE,0,1,1));
}


} // extern "C"
