/**********************************************************************
 
	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/VObject.h"
#include "v/vobj_utils.h"

extern "C" {
#include "xl.h"
#include "xlerror.h"



XL_SEXP *
_vobj_GetStatus(VObject ** po,XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf,
		VStatusFlagsFromFS * vsffs)
{
	XL_SEXP *el;
	L_CHAR * refer,*refer_id;
	VObject * obj;
	XL_SEXP * ref;
	int flags;
	int err;

	*po = 0;
	err = vobj_status_flag_from_fs(arg, sf, &flags,vsffs);
	if ( err == -2 )
		return 0;
	else if ( err )
		return get_error(
			arg->h.file,
			arg->h.line,
			XLE_PROTO_INV_PARAM,
			l_string(std_cm,"GetStatus"),
			n_get_string("unknown flag"));

	refer = get_sf_attribute(sf,l_string(std_cm,"refer"));
	refer_id = get_sf_attribute(sf,l_string(std_cm,"refer.id"));
	if ( refer ) {
		ref = eval(env,n_get_symbol("__object_list"));
		if ( get_type(ref) == XLT_ERROR )
			return ref;
		ref = vobj_get_object_by_name(ref,refer,arg);
		if ( get_type(ref) == XLT_ERROR )
			return ref;
		obj = VObject::get_object_by_id(ref->integer.data);
	}
	else if ( refer_id ) {
		obj = VObject::get_object_by_id(atoi(n_string(std_cm,refer_id)));
	}
	else {
		el = get_el(arg, 1);
		if ( get_type(el) != XLT_INTEGER )
			return get_error(
				arg->h.file,
				arg->h.line,
				XLE_SEMANTICS_TYPE_MISSMATCH,
				l_string(std_cm,"GetStatus"),
				n_get_string("type missmatch"));
		obj = VObject::get_object_by_id(el->integer.data);
	}
	if ( obj == 0 )
		return vobj_get_error(initial_VExError(V_ER_NOT_FOUND,0,0), arg);


	VObjectStatus sts;
	sts.children = 0;
	VExError err2 = obj->get_status(&sts, flags);
	if ( err2.code ) {
		free_vobject_status(&sts);
		return vobj_get_error(err2, arg);
	}
	
	XL_SEXP *ret = vobj_get_status_list(&sts, flags);
	free_vobject_status(&sts);
	*po = obj;
	return ret;
}

XL_SEXP *
vobj_GetStatus(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf)
{
VObject * obj;
XL_SEXP * ret;
	ret = _vobj_GetStatus(&obj,env,arg,a,sf,0);
	if ( ret == 0 && obj == 0 )
		return get_error(
			arg->h.file,
			arg->h.line,
			XLE_PROTO_INV_FIELD_NAME,
			l_string(std_cm,"GetStatus"),
			n_get_string("flags must be specified"));
	return ret;
}

XL_SEXP *
vobj_SetStatus(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf)
{
	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,"SetStatus"),
			n_get_string("type missmatch"));
	VObject *obj = VObject::get_object_by_id(target->integer.data);
	if ( obj == 0 )
		return vobj_get_error(initial_VExError(V_ER_NOT_FOUND,0,0), arg);
	
	VObjectStatus sts;
	VObject::FreeList *free_list=0;
	int flags = get_sts_from_sf(env,&sts,0, sf, &free_list);
	obj->free_on_release_list(free_list);

	VExError err = obj->set_status(&sts, flags);
	
	return vobj_get_error(err, arg);
}


XL_SEXP *
vobj_vobjTarget(XLISP_ENV *env, XL_SEXP *arg, XLISP_ENV *a, XL_SYM_FIELD *sf)
{
XL_SEXP * prev, * t, * sym;
XLISP_ENV * e;
XL_SEXP * olist, * prev_olist, * p;
int _p;
XL_SEXP * ret;
L_CHAR * _append;
	prev = arg;
	e = 0;
	_p = 0;
	for ( arg = cdr(arg) ; get_type(arg) == XLT_PAIR ;
			prev = arg, 
			arg = cdr(arg) ) {
		t = car(arg);
		if ( get_type(t) != XLT_PAIR )
			break;
		sym = car(t);
		if ( get_type(sym) != XLT_SYMBOL )
			break;
		if ( l_strcmp(sym->symbol.data,l_string(std_cm,"ObjectList"))
				== 0 ) {
			_append = get_sf_attribute(sym->symbol.field,
					l_string(std_cm,"append"));
			if ( _append && l_strcmp(_append,l_string(std_cm,"off")) == 0 )
				prev_olist = 0;
			else	prev_olist = eval(env,
					n_get_symbol("__object_list"));
			olist = eval(env,get_el(t,1));
			if ( get_type(olist) == XLT_ERROR )
				return olist;
			if ( get_type(prev_olist) == XLT_PAIR ) {
				if ( e == 0 )
					e = new_env(env);
				set_env(e,l_string(std_cm,"__object_list"),
					append(olist,prev_olist));
			}
			else {
				if ( e == 0 )
					e = new_env(env);
				set_env(e,l_string(std_cm,"__object_list"),
					olist);
			}
			continue;
		}
		else if ( l_strcmp(sym->symbol.data,l_string(std_cm,"Parent"))
				== 0 ) {
			p = eval(env,get_el(t,1));
			if ( get_type(p) == XLT_ERROR )
				return p;
			if ( get_type(p) != XLT_INTEGER )
				break;
			_p = p->integer.data;
			continue;
		}
		break;
	}
	if ( e )
		ret = vobj_eval_child(_p, e, prev);
	else	ret = vobj_eval_child(_p, env, prev);
	return ret;
}

XL_SEXP *
vobj_vobjWaitSync(XLISP_ENV * env,XL_SEXP * s,XLISP_ENV * a,XL_SYM_FIELD * sf)
{
WAITSYNC_CLIENT c;
XL_SEXP * id,* p,* _id;
VObject * obj;
char * err;
L_CHAR * _ws_type;
int ws_type;

	_ws_type = get_sf_attribute(sf,l_string(std_cm,"type"));
	if ( _ws_type ) {
		if ( l_strcmp(_ws_type,l_string(std_cm,"wait")) == 0 )
			ws_type = WST_WAIT;
		else	ws_type = WST_FORCE_STOP;
	}
	else	ws_type = WST_WAIT;

retry:
	memset(&c,0,sizeof(c));
	if ( list_length(s) == 1 ) {
		id = eval(env,n_get_symbol("__object_list"));
		for ( ; get_type(id) == XLT_PAIR ; id = cdr(id) ) {
			p = car(id);
			if ( get_type(p) != XLT_PAIR ) {
				waitsync_clear(&c);
				err = "__object_list(1)";
				goto inv_param;
			}
			_id = get_el(p,1);
			if ( get_type(_id) != XLT_INTEGER ) {
				waitsync_clear(&c);
				err = "__object_list(2)";
				goto inv_param;
			}
			obj = VObject::get_object_by_id(_id->integer.data);
			if ( obj == 0 )
				continue;
			obj->waitsync(&c,ws_type);
		}
	}
	else {
		id = get_el(s,1);
		switch ( get_type(id) ) {
		case XLT_INTEGER:
			obj = VObject::get_object_by_id(id->integer.data);
			if ( obj )
				obj->waitsync(&c,ws_type);
			break;
		case XLT_STRING:
			obj = get_object_from_name(env,id->string.data);
			if ( obj )
				obj->waitsync(&c,ws_type);
			break;
		default:
			err = "id";
			goto inv_param;
		}
	}
	waitsync_sleep(&c);
	if ( c.count )
		goto retry;
	return 0;
inv_param:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_PROTO_INV_PARAM,
		l_string(std_cm,"vobjStable"),
		n_get_string(err));
}

void
init_vobj_status(XLISP_ENV *env)
{
	set_env(env,l_string(std_cm,"SetStatus"),
		get_func_prim((XL_SEXP*(*)())vobj_SetStatus,FO_APPLICATIVE,0,2,2));
	set_env(env,l_string(std_cm,"GetStatus"),
		get_func_prim((XL_SEXP*(*)())vobj_GetStatus,FO_APPLICATIVE,0,1,2));
	set_env(env,l_string(std_cm,"vobjTarget"),
		get_func_prim((XL_SEXP*(*)())vobj_vobjTarget,FO_NORMAL,0,1,-1));
	set_env(env,l_string(std_cm,"vobjWaitSync"),
		get_func_prim((XL_SEXP*(*)())vobj_vobjWaitSync,FO_APPLICATIVE,0,1,2));
}


} // extern "C"
