/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.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	"pri_level.h"
#include	"lock_level.h"
#include	"memory_debug.h"
#include	"long_char.h"

}

#include	"v/VMacro.h"

typedef struct VMacroGCNode {
	struct VMacroGCNode *	next;
	struct VMacroGCNode * 	prev;
	VMacro *		object;
} VMacroGCNode;


extern "C" {

SEM VMacro_lock;
VMacroGCNode gc_header;
void
gc_VdataXLdata();
void
VMacro_gc_lock();
void
VMacro_gc_unlock();
void
VMacro_gc();


void
VMacro_gc_lock()
{
	lock_task(VMacro_lock);
}

void
VMacro_gc_unlock()
{
	unlock_task(VMacro_lock,"VMAcro_gc_unlock");
}

void
VMacro_gc()
{
VMacroGCNode * n;
	for ( n = gc_header.next ; n != &gc_header ; n = n->next )
		n->object->gc();
	gc_VdataXLdata();
}

void
init_VMacro()
{
	VMacro_lock = new_lock(LL_VMACRO);
}

} // extern "C";



VExError
VMacro::get_status(VObjectStatus * s, int flags) const
{
VExError err;
	V_OP_START_EX
	if ( flags & VSF_PARENT ) {
		s->window = sts.window;
		s->parent = sts.parent;
		flags &= ~VSF_PARENT;	
	}
	if ( flags & VSF_ID ) {
		s->id = sts.id;
		flags &= ~VSF_ID;
	}
	if ( sts.children )
		err = sts.children->object->get_status(s,flags);
	else	err = VObject::get_status(s,flags);
	err = merge_VExError_vstatus_type(
		initial_VExError(V_ER_NO_ERR,flags,0),err);
	V_OP_END
	return err;
}

VExError
VMacro::set_status(const VObjectStatus * s,int flags)
{
VExError err;
	V_OP_START_EX
	VObject::set_status(s,flags);
	if ( sts.children )
		err = sts.children->object->set_status(s,flags);
	V_OP_END
	return err;
}


VMacro::VMacro()
{
	vmacro_lock_cnt = 0;
	vmacro_task = 0;
}

VMacro::~VMacro()
{
}

/*
VInfo *
VMacro::get_info_ex(bool inner) const
{
	if ( inner )
		return sts.children ? get_info(sts.children->object) : 0;
	return get_info(sts.parent);
}
*/

void
VMacro::gc()
{
}


void
VMacro::_vm_lock(char* f,int ln)
{
int t;
	t = get_tid();
	vm_lock_pri = push_pri(PRI_USER_INTERFACE);
	_i_lock_task(VMacro_lock,f,ln);
	for ( ; vmacro_task && vmacro_task != t ; ) {
		sleep_task((int)&vmacro_lock_cnt,VMacro_lock);
		_i_lock_task(VMacro_lock,f,ln);
	}
	vmacro_lock_file = f;
	vmacro_lock_line = ln;
	vmacro_task = t;
	vmacro_lock_cnt ++;
	unlock_task(VMacro_lock,"vm_lock");
}

void
VMacro::_vm_unlock()
{
int t;
	t = get_tid();
	_i_lock_task(VMacro_lock,__FILE__,__LINE__);
	if ( vmacro_task != t )
		er_panic("vm_lock");
	vmacro_lock_cnt --;
	if ( vmacro_lock_cnt == 0 ) {
		vmacro_task = 0;
		wakeup_task((int)&vmacro_lock_cnt);
	}
	unlock_task(VMacro_lock,"vm_lock");
	change_pri(0,vm_lock_pri);
}


void
VMacro::insert_gc()
{
VMacroGCNode * n;
	VMacro_gc_lock();
	if ( gc_node == 0 ) {
		n = new VMacroGCNode;
		n->object = this;
		n->prev = &gc_header;
		n->next = gc_header.next;
		n->prev->next = n;
		n->next->prev = n;

		gc_node = n;
	}
	VMacro_gc_unlock();
}


void
VMacro::delete_gc()
{
	VMacro_gc_lock();
	if ( gc_node ) {
		gc_node->next->prev = gc_node->prev;
		gc_node->prev->next = gc_node->next;
		delete gc_node;
		gc_node = 0;
	}
	VMacro_gc_unlock();
}



VObject *
VMacro::get_backcolorview(VObjectStatus * _sts,int flags)
{
	return 0;
}



// ============= VdataXL ==============

typedef struct VdataXLdata {
	struct VdataXLdata *	next;
	XL_SEXP *		d;
} VdataXLdata;

VdataXLdata * VdataXLdata_list;

void
insert_VdataXLdata(VdataXLdata*dd);
void
delete_VdataXLdata(VdataXLdata*dd);

void
insert_VdataXLdata(VdataXLdata*dd)
{
	lock_task(VMacro_lock);
	dd->next = VdataXLdata_list;
	VdataXLdata_list = dd;
	unlock_task(VMacro_lock,"insert");
}

void
delete_VdataXLdata(VdataXLdata*dd)
{
VdataXLdata ** p;
	lock_task(VMacro_lock);
	for ( p = &VdataXLdata_list ; *p ; p = &(*p)->next ) {
		if ( *p != dd )
			continue;
		*p = dd->next;
		break;
	}
	unlock_task(VMacro_lock,"insert");
}

void
gc_VdataXLdata()
{
VdataXLdata * p;
	for ( p = 0 ; p ; p = p->next )
		gc_gb_sexp(p->d);
}

VIM VIM_VdataXL = { &VIM_VdataLString , VDT_NONE };

VdataXL::VdataXL(const L_CHAR *lstr,int * erp,bool valid)
	: VdataLString((char*)0,0,0)
{
STREAM * st;
XL_SEXP * ret;
VdataXLdata * _ret;
	if ( !valid )
		return;
	if ( lstr == 0 ) {
		err = -1;
		if ( erp )
			*erp = err;
		return;
	}
	vdata_type = &VIM_VdataXL;
	gc_push(0,0,"VdataXL");
	_ret = (VdataXLdata*)d_alloc(sizeof(*_ret));
	st = s_open_string_read((L_CHAR*)lstr,&int_cm,l_strlen((L_CHAR*)lstr)*sizeof(L_CHAR),0);
	_ret->d = ret = init_parse(st,l_string(std_cm,"VdataXL"),l_string(std_cm,"VdataXL"));
	for ( ; get_type_sexp(ret) == XLT_PAIR ; ret = cdr(ret) );
	mData = (void*)_ret;
	s_close(st);
	err = 0;
	if ( get_type_sexp(_ret->d) == XLT_ERROR ) {
		err = -1;
		if ( erp )
			*erp = err;
		d_f_ree(_ret);
		goto end;
	}

	insert_VdataXLdata(_ret);
	if ( erp )
		*erp = err;
end:
	gc_pop(0,0);
}


VdataXL::VdataXL(const char *str,int * erp,bool valid)
	: VdataLString((char*)0,0,0)
{
STREAM * st;
XL_SEXP * ret;
VdataXLdata * _ret;
	if ( !valid )
		return;
	vdata_type = &VIM_VdataXL;
	if ( str == 0 ) {
		err = -1;
		if ( erp )
			*erp = err;
		return;
	}
	gc_push(0,0,"VdataXL");
	_ret = (VdataXLdata*)d_alloc(sizeof(*_ret));
ss_printf("VdataXL CLIP- %s\n",str);
	st = s_open_string_read((char*)str,std_cm,strlen((char*)str),0);
	_ret->d = ret = init_parse(st,l_string(std_cm,"VdataXL"),l_string(std_cm,"VdataXL"));
	for ( ; get_type_sexp(ret) == XLT_PAIR ; ret = cdr(ret) );
	mData = (void*)_ret;
	s_close(st);
	err = 0;
	if ( get_type_sexp(_ret->d) == XLT_ERROR ) {
		err = -1;
		if ( erp )
			*erp = err;
		d_f_ree(_ret);
		goto end;
	}

	insert_VdataXLdata(_ret);
	if ( erp )
		*erp = err;
end:
	gc_pop(0,0);
}


VdataXL::VdataXL(const XL_SEXP * s,int * erp,bool valid)
	: VdataLString((char*)0,0,0)
{
VdataXLdata * _ret;
	if ( !valid )
		return;
	vdata_type = &VIM_VdataXL;
	_ret = (VdataXLdata*)d_alloc(sizeof(*_ret));
	_ret->d = (XL_SEXP*)s;
	mData = (void*)_ret;
	err = 0;
	if ( erp )
		*erp = err;

	insert_VdataXLdata(_ret);
}

VdataXL::VdataXL(Vdata * d,int * erp,bool valid)
	: VdataLString((char*)0,0,0)
{
VIM * v;
VdataLString * str;
L_CHAR * lstr;
VdataXLdata * _ret;
VdataXL * xl_ret;
	if ( !valid )
		return;
	vdata_type = &VIM_VdataXL;
	err = 0;
	v = d->get_type();
	switch ( cmp_VIM(v,&VIM_VdataXL) ) {
	case 0:
	case 1:
		mData = (void*)static_cast<VdataXL*>(d)->get_VdataXL(&err);
		break;
	case -1:
		str = new VdataLString(d,&err);
		if ( err < 0 ) {
			if ( erp )
				*erp = err;
			delete str;
			return;
		}
		lstr = str->get_VdataLString(&err);
		if ( err < 0 ) {
			delete str;
			if ( erp )
				*erp = err;
			return;
		}
		delete str;
		xl_ret = new VdataXL(lstr,&err);
		if ( lstr )
			d_f_ree(lstr);
		if ( err < 0 ) {
			if ( erp )
				*erp = err;
			delete xl_ret;
			return;
		}
		_ret = (VdataXLdata*)d_alloc(sizeof(*_ret));
		_ret->d = xl_ret->get_VdataXL();
		mData = (void*)_ret;
		insert_VdataXLdata(_ret);
		break;
	default:
		err = -1;
		break;
	}
	if ( erp )
		*erp = err;
	if ( err < 0 )
		return;
	err = 0;
}

VdataXL::~VdataXL()
{
	if ( this->get_type() != &VIM_VdataXL )
		return;
	if ( mData ) {
		delete_VdataXLdata((VdataXLdata*)mData);
		d_f_ree(mData);
	}
}

char*
VdataXL::get_VdataString(int * erp)
{
STREAM * st;
VdataXLdata * dd;
char * ret;
	if ( err < 0 ) {
		if ( erp )
			*erp = err;
		return 0;
	}
	dd = (VdataXLdata*)mData;
	st = s_open_string_write(std_cm);
	print_sexp(st,dd->d,PF_XML);
	ret = copy_str(s_get_string(st));
	s_close(st);
	return ret;
}

L_CHAR*
VdataXL::get_VdataLString(int * erp)
{
STREAM * st;
VdataXLdata * dd;
L_CHAR * ret;
	if ( err < 0 ) {
		if ( erp )
			*erp = err;
		return 0;
	}
	dd = (VdataXLdata*)mData;
	st = s_open_string_write(&int_cm);
	print_sexp(st,dd->d,PF_XML);
	ret = ll_copy_str(s_get_l_string(st));
	s_close(st);
	return ret;
}

XL_SEXP*
VdataXL::get_VdataXL(int * erp)
{
VdataXLdata * dd;
	if ( err < 0 ) {
		if ( erp )
			*erp = err;
		return 0;
	}
	dd = (VdataXLdata*)mData;
	return dd->d;
}

bool
VdataXL::clipboard_available(bool lock_flag)
{
Vdata * d;
VdataXL * d2;
int er;
bool ret;
ClipBoardList * c;
	if ( lock_flag )
		lock_task(cb_lock);
	ret = false;
	d2 = static_cast<VdataXL*>(check_clipboard_variation(&VIM_VdataXL));
	if ( d2 ) {
		ret = true;
		goto end;
	}
	c = get_clipboard();
	if ( c == 0 )
		goto end;
	d = c->target;
	if ( d == 0 )
		goto end;
	d2 = new VdataXL(d,&er);
	if ( er < 0 )
		goto end;
	insert_clipboard_variation(c,d2);
	ret = true;
end:
	if ( lock_flag )
		unlock_task(cb_lock,"new_from_clipboard");
	return ret;
}

VdataXL * 
VdataXL::new_from_clipboard()
{
VdataXL * ret;
	lock_task(cb_lock);
	if ( VdataXL::clipboard_available(false) )
		ret = static_cast<VdataXL*>(delete_clipboard_variation(&VIM_VdataXL));
	else	ret = 0;
	unlock_task(cb_lock,"new_from_clipboard");
	return ret;
}
