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

#include "v/VWindow.h"
#include "v/vobj_utils.h"
#include "v/VMarshaler.h"

extern "C" {

void mem_test(char*);
#include "memory_debug.h"
#include "xl.h"

XL_SEXP * vobj_VMarshaler(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf);
XL_SEXP * get_m_objects(VObject *** o_list,XLISP_ENV * env,L_CHAR * data,XL_SEXP * arg);
L_CHAR * delete_term(L_CHAR * str);
XL_SEXP * _get_m_object(VObject *** o_list,int * nos,XLISP_ENV * env,DIV_STR * ds,XL_SEXP * arg);
int ** get_rule(int * ret_nos,int target_nos,int value_nos,L_CHAR * data);
int * get_code_table(int * _nos,DIV_STR * ds);

L_CHAR *
delete_term(L_CHAR * str)
{
L_CHAR * ret;
L_CHAR * p,* q;
	ret = ll_copy_str(str);
	for ( p = q = ret ; *q ; q ++ )
		if ( (*q) < 0 || (*q) > ' ' )
			*p++ = *q;
	*p = 0;
	return ret;
}

XL_SEXP *
_get_m_object(VObject *** o_list,int * nos,XLISP_ENV * env,DIV_STR * ds,XL_SEXP * arg)
{
int id;
VObject * v;
XL_SEXP * p;
	for ( ; ds ; ds ++ ) {
		if ( ds->type == DST_STRING ) {
			id = atoi(n_string(std_cm,ds->d.str));
			if ( id == 0 ) {
				p = eval(env,n_get_symbol("__object_list"));
				if ( get_type(p) == XLT_ERROR )
					return p;
				p = vobj_get_object_by_name(p,ds->d.str,arg);
				if ( get_type(p) != XLT_INTEGER )
					return p;
				v = VObject::get_object_by_id(p->integer.data);
			}
			else {
				v = VObject::get_object_by_id(id);
			}
			if ( v == 0 )
				return get_error(
					arg->h.file,
					arg->h.line,
					XLE_PROTO_INV_OBJECT,
					l_string(std_cm,"_get_m_object"),
					List(n_get_string("undefined object"),
						get_string(ds->d.str),
						-1));
			*o_list = (VObject**)d_re_alloc((void*)*o_list,sizeof(VObject**)*((*nos)+1));
			(*o_list)[*nos] = v;
			(*nos)++;
		}
		else	_get_m_object(o_list,nos,env,ds->d.ds,arg);
		if ( ds->term == DST_TERMINATE )
			break;
	}
	return 0;
}

XL_SEXP *
get_m_objects(VObject *** o_list,XLISP_ENV * env,L_CHAR * data,XL_SEXP * arg)
{
DIV_STR ds;
int nos;
XL_SEXP * ret;
	ds.d.str = delete_term(data);
	ds.term = DST_TERMINATE;
	ds.type = DST_STRING;
	ds.flags = DSF_FREE;
	divide_string(&ds,l_string(std_cm,","));
	*o_list = 0;
	nos = 0;
	ret = _get_m_object(o_list,&nos,env,&ds,arg);
	free_div_str(ds);
	if ( get_type(ret) == XLT_ERROR )
		return ret;
	return get_integer(nos,0);
}


int *
get_code_table(int * _nos,DIV_STR * ds)
{
int * ret;
int nos;
DIV_STR * dsp;
int i;
char * str;
char * p;
int d,_d;
	for ( nos = 1 , dsp = ds ; dsp->term != DST_TERMINATE ; dsp ++ , nos ++);
	ret = (int*)d_alloc(sizeof(int)*nos);
	for ( i = 0 , dsp = ds ; i < nos ; dsp ++ , i ++ ) {
		str = n_string(std_cm,dsp->d.str);
		d = AF_DONTCARE;
		for ( p = str ; *p ; p ++ ) {
			switch ( *p ) {
			case 'I':
				d |= AF_INITIALIZE;
				continue;
			case 'V':
				d |= AF_VISIBLE_ON;
				continue;
			case 'v':
				d |= AF_VISIBLE_OFF;
				continue;
			case 'E':
				d |= AF_ENABLE_ON;
				continue;
			case 'e':
				d |= AF_ENABLE_OFF;
				continue;
			case 'C':
				d |= AF_CLICK;
				continue;
			case 'X':
				d |= AF_DONTCARE;
				break;
			case '#':
				sscanf(p+1,"%x",&_d);
				d &= ~AF_VALUE_M;
				d |= _d & AF_VALUE_M;
				break;
			default:
				sscanf(p,"%i",&_d);
				d &= ~AF_VALUE_M;
				d |= _d & AF_VALUE_M;
				break;
			}
			break;
		}
		ret[i] = d;
	}
	*_nos = nos;
	return ret;
}

int **
get_rule(int * ret_nos,int target_nos,int value_nos,L_CHAR * data)
{
DIV_STR ds,*a_list;
int * inp;
int nos;
int i;
int * tbl;
int ** ret;
int _ret_nos;
	ds.d.str = delete_term(data);
ss_printf("DELETE TERM %ls\n",ds.d.str);
	ds.term = DST_TERMINATE;
	ds.type = DST_STRING;
	ds.flags = DSF_FREE;
	divide_string(&ds,l_string(std_cm,","));
	divide_string(&ds,l_string(std_cm,"->"));
	divide_string(&ds,l_string(std_cm,"/"));
	divide_string(&ds,l_string(std_cm,":"));
	ret = 0;
	_ret_nos = 0;
	for ( a_list = ds.d.ds ; ; a_list ++ ) {
		if ( a_list->type != DST_LIST )
			goto next;
		if ( a_list->d.ds[0].type != DST_LIST )
			goto next;
		if ( a_list->d.ds[0].term == DST_TERMINATE )
			goto next;
		if ( a_list->d.ds[0].d.ds[0].type != DST_LIST )
			goto next;
		if ( a_list->d.ds[1].d.ds[0].term == DST_TERMINATE )
			goto next;
		inp = get_code_table(&nos,a_list->d.ds[0].d.ds[0].d.ds);
		if ( inp == 0 )
			goto err;
		tbl = (int*)d_alloc(sizeof(int)*(2*target_nos + value_nos));
		for ( i = 0 ; i < nos ; i ++ )
			tbl[i] = inp[i];
		for ( ; i < target_nos ; i ++ )
			tbl[i] = AF_DONTCARE;

		inp = get_code_table(&nos,a_list->d.ds[1].d.ds[0].d.ds);
		if ( inp == 0 ) {
			d_f_ree(tbl);
			goto err;
		}
		for ( i = 0 ; i < nos ; i ++ )
			tbl[target_nos + value_nos + i] = inp[i];
		for ( ; i < target_nos ; i ++ )
			tbl[target_nos + value_nos + i] = AF_DONTCARE;

		inp = get_code_table(&nos,a_list->d.ds[1].d.ds[1].d.ds);
		if ( inp == 0 ) {
			d_f_ree(tbl);
			goto err;
		}
		for ( i = 0 ; i < nos ; i ++ )
			tbl[target_nos + i] = inp[i];
		for ( ; i < value_nos ; i ++ )
			tbl[target_nos + i] = AF_DONTCARE;


		ret = (int**)d_re_alloc((void*)ret,sizeof(int*)*(_ret_nos+1));
		ret[_ret_nos] = tbl;
		_ret_nos ++;
	next:
		if ( a_list->term == DST_TERMINATE )
			break;
	}
	free_div_str(ds);
	*ret_nos  = _ret_nos;
	return ret;
err:
	free_div_str(ds);
	for ( i  = 0 ; i < _ret_nos ; i ++ )
		d_f_ree(ret[i]);
	d_f_ree(ret);
	return 0;
}

XL_SEXP *
vobj_VMarshaler(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf)
{
L_CHAR * rule;
L_CHAR * obj_list;
XL_SEXP * ret;
VObject ** o_list;
int ** _rule;
int r_nos;
L_CHAR * d;
int value_nos,target_nos;
VMarshaler * m;
int i;
	rule = get_sf_attribute(sf,l_string(std_cm,"rule"));
	if ( rule == 0 ) {
		return get_error(
			arg->h.file,
			arg->h.line,
			XLE_PROTO_INV_PARAM,
			l_string(std_cm,"VMarshaler"),
			List(n_get_string("attribute (rule)"),
				-1));
	}
	obj_list = get_sf_attribute(sf,l_string(std_cm,"objects"));
	if ( rule == 0 ) {
		return get_error(
			arg->h.file,
			arg->h.line,
			XLE_PROTO_INV_PARAM,
			l_string(std_cm,"VMarshaler"),
			List(n_get_string("attribute (objects)"),
				-1));
	}
	d = get_sf_attribute(sf,l_string(std_cm,"values"));
	if ( d == 0 )
		value_nos = 1;
	else	value_nos = atoi(n_string(std_cm,d));
	ret = get_m_objects(&o_list,env,obj_list,arg);
	if ( get_type(ret) == XLT_ERROR )
		return ret;
	target_nos = ret->integer.data;
	_rule = get_rule(&r_nos,target_nos,value_nos,rule);
	if ( _rule == 0 ) {
		if ( o_list )
			d_f_ree(o_list);
		return get_error(
			arg->h.file,
			arg->h.line,
			XLE_PROTO_INV_PARAM,
			l_string(std_cm,"VMarshaler"),
			List(n_get_string("rule format error"),
				-1));
	}

	m = new VMarshaler(target_nos,value_nos);
	for ( i = 0 ; i < target_nos ; i ++ )
		m->set_target(i,o_list[i]);
	for ( i = 0 ; i < r_nos ; i ++ )
		m->set_action(i,
			&_rule[i][0],
			&_rule[i][target_nos],
			&_rule[i][target_nos+value_nos]);
	for ( i = 0 ; i < r_nos ; i ++ )
		d_f_ree(_rule[i]);
	d_f_ree(_rule);
	if ( o_list )
		d_f_ree(o_list);
	return get_integer(m->get_id(),0);
}


void
init_VMarshaler(XLISP_ENV *env)
{
	set_env(env,l_string(std_cm,"VMarshaler"),
		get_func_prim((XL_SEXP*(*)())vobj_VMarshaler,FO_APPLICATIVE,0,1,1));
	
}



} // extern "C"
