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

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


extern "C" {
#include	"memory_debug.h"
#include	"lock_level.h"
}	
#include	"v/VMarshaler.h"

typedef struct m_user_arg {
	VMarshaler *		m_obj;
	int			click;
} M_USER_ARG;
typedef struct m_list {
	struct m_list *	next;
	VMarshaler * 	m;
} M_LIST;


V_CALLBACK_D(_vm_value_handler);
V_CALLBACK_D(_vm_destroy_handler);
int insert_m_list(VMarshaler * mm);
void delete_m_list(VMarshaler * mm);

M_LIST * marshaler_list;
SEM marshaler_lock;
int m_id;


void
init_marshaler()
{

	marshaler_lock = new_lock(LL_MARSHALER);
}

int
insert_m_list(VMarshaler * mm)
{
M_LIST * m;

	lock_task(marshaler_lock);
retry:
	if ( m_id == 0 )
		m_id = 1;
	else {
		m_id ++;
		if ( m_id <= 0 )
			m_id = 1;
	}
	for ( m = marshaler_list ; m ; m = m->next ) {
		if ( m->m->get_id() == m_id )
			goto retry;
	}
	m = (M_LIST *)d_alloc(sizeof(*m));
	m->m = mm;
	m->next = marshaler_list;
	marshaler_list = m;
	mm->__set_id(m_id);
	unlock_task(marshaler_lock,"__get_new_m_id");
	return m_id;
}


void
delete_m_list(VMarshaler * mm)
{
M_LIST * m,** mp;
	lock_task(marshaler_lock);
	for ( mp = &marshaler_list ; *mp ;  mp = &(*mp)->next ) {
		m = *mp;
		if ( m->m == mm ) {
			*mp = m->next;
			d_f_ree(m);
			break;
		}
	}
	unlock_task(marshaler_lock,"delte_m_list");
}


VMarshaler::VMarshaler(int t_nos,int mv_nos)
{
int i;
	targets_nos = t_nos;
	m_values_nos = mv_nos;

	obj_list = (VObject**)d_alloc(sizeof(VObject*)*targets_nos);
	target_values = (int*)d_alloc(sizeof(int)*targets_nos);
	action_tables = 0;
	actions_nos = 0;
	m_values = (int*)d_alloc(sizeof(int)*m_values_nos);
	event_handlers = (V_CALLBACK_T*)
		d_alloc(sizeof(V_CALLBACK_T)*m_values_nos);
	c_objects = (VObject**)d_alloc(sizeof(VObject*)*m_values_nos);
	eh_args = (void**)d_alloc(sizeof(void*)*m_values_nos);
	for ( i = 0  ; i < targets_nos ; i ++ ) {
		obj_list[i] = 0;
		target_values[i] = AF_DONTCARE;
	}
	for ( i = 0 ; i < m_values_nos ; i ++ ) {
		m_values[i] = AF_DONTCARE;
		event_handlers[i] = 0;
		c_objects[i] = 0;
		eh_args[i] = 0;
	}
	insert_m_list(this);
}

VMarshaler::~VMarshaler()
{
int i;
VObjectStatus sts;
	memset(&sts,0,sizeof(sts));
	for ( i = 0 ; i < targets_nos ; i ++ ) {
		if ( obj_list[i] == 0 )
			continue;
		obj_list[i]->set_status(&sts,VSF_VALUE_EH|VSF_DESTROY_H);
	}
	d_f_ree(obj_list);
	for ( i = 0 ; i < actions_nos ; i ++ ) {
		if ( action_tables[i] == 0 )
			continue;
		d_f_ree(action_tables[i]);
	}
	d_f_ree(target_values);
	if ( action_tables )
		d_f_ree(action_tables);
	d_f_ree(m_values);
	d_f_ree(event_handlers);
	d_f_ree(c_objects);
	d_f_ree(eh_args);
	delete_m_list(this);
}

int
VMarshaler::get_id()
{
	return id;
}

void
VMarshaler::__set_id(int m_id)
{
	id = m_id;
}

VMarshaler *
id2marshaler(int id)
{
M_LIST * m;
	for ( m = marshaler_list ; m ; m = m->next )
		if ( m->m->get_id() == id )
			return m->m;
	return 0;
}

int
VMarshaler::set_target(int no,VObject * obj)
{
VObjectStatus sts;
	if ( no >= targets_nos )
		return -1;
	obj_list[no] = obj;
	if ( obj ) {
		sts.value_event_handler = _vm_value_handler;
		sts.value_eh_arg = (void*)this;
		sts.destroy_handler = _vm_destroy_handler;
		sts.destroy_h_arg = (void*)this;
		if ( obj->set_status(&sts,VSF_VALUE_EH|VSF_DESTROY_H).code  )
			return -1;
		if ( obj->get_status(&sts,VSF_VALUE).code ) {
			memset(&sts,0,sizeof(sts));
			obj->set_status(&sts,VSF_VALUE_EH|VSF_DESTROY_H);
			return -1;
		}
		__vm_value_handler(no,sts.value,0);
	}
	else {
		__vm_value_handler(no,AF_DONTCARE,0);
	}
	return 0;
}

void
VMarshaler::set_action(int a_no,int * values,int * mm_values,int * a_values)
{
int i;
int * tbl;
int v;
int flags;
VObjectStatus sts;
	if ( actions_nos <= a_no ) {
		action_tables = (int**)d_re_alloc(action_tables,sizeof(int*)*(a_no+1));
		for ( i = actions_nos ; i < a_no+1 ; i ++ )
			action_tables[i] = 0;
		actions_nos = a_no+1;
	}
	if ( action_tables[a_no] )
		d_f_ree(action_tables[a_no]);
	if ( values == 0 && m_values == 0 && a_values == 0 ) {
		action_tables[a_no] = 0;
		return;
	}
	action_tables[a_no] = tbl = (int*)d_alloc(sizeof(int)*(2*targets_nos + m_values_nos));
	if ( values ) {
		for ( i = 0 ; i < targets_nos ; i ++ )
			tbl[i] = values[i];
	}
	else {
		for ( i = 0 ; i < targets_nos ; i ++ )
			tbl[i] = AF_DONTCARE;
	}
	if ( m_values ) {
		for ( i = 0 ; i < m_values_nos ; i ++ )
			tbl[i+targets_nos] = mm_values[i];
	}
	else {
		for ( i = 0 ; i < m_values_nos ; i ++ )
			tbl[i+targets_nos] = AF_DONTCARE;
	}
	if ( a_values ) {
		for ( i = 0 ; i < targets_nos ; i ++ )
			tbl[i+targets_nos+m_values_nos] = a_values[i];
	}
	else {
		for ( i = 0 ; i < targets_nos ; i ++ )
			tbl[i+targets_nos+m_values_nos] = AF_DONTCARE;
	}
	for ( i = 0 ; i < targets_nos ; i ++ )
		if ( tbl[i] & AF_INITIALIZE )
			goto need_initialize;
	return;
need_initialize:
	tbl[0] |= AF_INITIALIZE;
	for ( i = 0 ; i < m_values_nos ; i ++ ) {
		if ( tbl[targets_nos+i] == AF_DONTCARE )
			continue;
		m_values[i] = tbl[targets_nos+i] & AF_VALUE_M;
	}
	for ( i = 0 ; i < targets_nos ; i ++ ) {
		memset(&sts,0,sizeof(sts));
		v = tbl[targets_nos+m_values_nos+i];
		flags = 0;
		if ( (v & AF_VALUE_M) != AF_DONTCARE) {
			target_values[i] = v & AF_VALUE_M;
			flags = VSF_VALUE;
			sts.value = v&AF_VALUE_M;
		}
		switch ( v & AF_VISIBLE_M ) {
		case AF_VISIBLE_OFF:
			flags |= VSF_VISIBLE;
			sts.visible = 0;
			break;
		case AF_VISIBLE_ON:
			flags |= VSF_VISIBLE;
			sts.visible = 1;
		case AF_VISIBLE_DC:
			break;
		}
		switch ( v & AF_ENABLE_M ) {
		case AF_VISIBLE_OFF:
			flags |= VSF_ENABLED;
			sts.enabled = 0;
			break;
		case AF_VISIBLE_ON:
			flags |= VSF_ENABLED;
			sts.enabled = 1;
		case AF_VISIBLE_DC:
			break;
		}
		if ( flags ) {
			obj_list[i]->set_status(&sts,VSF_VALUE_EH);

			obj_list[i]->set_status(&sts,flags);

			sts.value_event_handler = _vm_value_handler;
			sts.value_eh_arg = (void*)this;
			obj_list[i]->set_status(&sts,VSF_VALUE_EH);

		}
	}
}

int
VMarshaler::set_event_handler(int no,VObject * obj,V_CALLBACK(eh),void * arg)
{
	if ( no >= m_values_nos )
		return -1;
	event_handlers[no] = eh;
	c_objects[no] = obj;
	eh_args[no] = arg;
	return 0;
}


int
VMarshaler::get_value(int no)
{
	if ( no >= m_values_nos )
		return AF_DONTCARE;
	return m_values[no];
}




void
VMarshaler::__vm_value_handler(int target,int value,int click)
{
int i,j;
int * tbl;
char * c_flags;
VObjectStatus sts;
int flags;
int v;
	if ( target >= targets_nos )
		return;
	if ( target_values[target] == (value&AF_VALUE_M) && click == 0 )
		return;
	c_flags = (char*)d_alloc(m_values_nos);
	target_values[target] = value&AF_VALUE_M;
	for ( i = 0 ; i < m_values_nos ; i ++ )
		c_flags[i] = 0;
	for ( i = 0 ; i < actions_nos ; i ++ ) {
		tbl = action_tables[i];
		if ( tbl == 0 )
			continue;
		if ( tbl[0] & AF_INITIALIZE )
			continue;
		if ( click == 0 && (tbl[target] & AF_CLICK) )
			continue;
		for ( j = 0 ; j < targets_nos ; j ++ ) {
			if ( (tbl[j]&AF_CLICK) && (target != j || click == 0) )
				goto no_match;
			if ( (tbl[j]&AF_VALUE_M) == AF_DONTCARE )
				continue;
			if ( (tbl[j]&AF_VALUE_M) != target_values[j] )
				goto no_match;
		}
		for ( j = 0 ; j < m_values_nos ; j ++ ) {
			if ( tbl[targets_nos+j] == AF_DONTCARE )
				continue;
			if ( tbl[targets_nos+j] != m_values[j] )
				c_flags[j] = 1;
			if ( tbl[targets_nos+j] & AF_CLICK )
				c_flags[j] = 1;
			m_values[j] = tbl[targets_nos+j] & AF_VALUE_M;
		}
		for ( j = 0 ; j < targets_nos ; j ++ ) {
			if ( obj_list[j] == 0 )
				continue;
			v = tbl[targets_nos + m_values_nos + j];
			flags = 0;
			memset(&sts,0,sizeof(sts));
			if ( (v&AF_VALUE_M) != AF_DONTCARE && 
					target_values[j] != 
						(v & AF_VALUE_M) ) {
				flags = VSF_VALUE;
				sts.value = v&AF_VALUE_M;
			}
			switch ( v & AF_VISIBLE_M ) {
			case AF_VISIBLE_OFF:
				flags |= VSF_VISIBLE;
				sts.visible = 0;
				break;
			case AF_VISIBLE_ON:
				flags |= VSF_VISIBLE;
				sts.visible = 1;
				break;
			case AF_VISIBLE_DC:
				break;
			}
			switch ( v & AF_ENABLE_M ) {
			case AF_ENABLE_OFF:
				flags |= VSF_ENABLED;
				sts.enabled = 0;
				break;
			case AF_ENABLE_ON:
				flags |= VSF_ENABLED;
				sts.enabled = 1;
				break;
			case AF_ENABLE_DC:
				break;
			}
			if ( flags ) {
				obj_list[j]->set_status(&sts,VSF_VALUE_EH);

				obj_list[j]->set_status(&sts,flags);

				sts.value_event_handler = _vm_value_handler;
				sts.value_eh_arg = (void*)this;
				obj_list[j]->set_status(&sts,VSF_VALUE_EH);

				__vm_value_handler(obj_list[j],sts.value,0);
			}
		}
		for ( j = 0 ; j < m_values_nos ; j ++ ) {
		VM_ARG vm;
			if ( c_flags[j] == 0 )
				continue;
			if ( c_objects[j] && event_handlers[j] ) {
				vm.id = j;
				vm.value = m_values[j];
				vq_insert_callback(c_objects[j],event_handlers[j],
						eh_args[j],&vm,sizeof(vm));
			}
		}
		break;
	no_match:
		{}
	}
	d_f_ree(c_flags);
}

void
VMarshaler::__vm_value_handler(VObject * obj,int value,int click)
{
int i;
	for ( i = 0 ; i < targets_nos ; i ++ )
		if ( obj_list[i] == obj ) {
			__vm_value_handler(i,value,click);
			break;
		}
}

V_CALLBACK_D(_vm_value_handler)
{
VMarshaler * m;
VObjectStatus sts;
	m = (VMarshaler*)user_arg;
	object->get_status(&sts,VSF_VALUE);
	m->__vm_value_handler(object,sts.value,AF_CLICK);
}

void
VMarshaler::__vm_destroy_handler(VObject * obj)
{
int no;
int i;
	for ( no = 0 ; no < targets_nos ; no ++ )
		if ( obj_list[no] == obj ) {
			set_target(no,0);
		}
	for ( i = 0 ; i < targets_nos ; i ++ )
		if ( obj_list[i] )
			break;
	if ( i == targets_nos )
		this->~VMarshaler();
}


V_CALLBACK_D(_vm_destroy_handler)
{
VMarshaler * m;
	m = (VMarshaler*)user_arg;
	m->__vm_destroy_handler(object);
}


