/**********************************************************************
 
	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 "v/VClipboard.h"
#include "machine/v_m.h"
extern "C" {
#include "memory_debug.h"
#include "utils.h"
#include "lock_level.h"
#include "jpeg.h"
}


// ============= VdataUtitities ===========

int
cmp_VIM(VIM * a,VIM * b)
{
VIM * p;
	if ( a == b )
		return 0;
	for ( p = a ; p ; p = p->parent )  {
		if ( p == b )
			return 1;
	}
	for ( p = b ; p ; p = p->parent ) {
		if ( p == a )
			return -1;
	}
	return VIM_INCOMP;
}

// ============= Indipendent Clipboard ===========



ClipBoardList * search_clipboard(VIM * v);
void purge_clipboard(ClipBoardList * c);
bool get_sys_clipboard(ClipBoardList * c);



ClipBoardList * cbl;
SEM cb_lock;

void
init_clipboard()
{
	cb_lock = new_lock(LL_CB);
}

ClipBoardList *
search_clipboard(VIM * v)
{
ClipBoardList * ret;
	for ( ret = cbl ; ret ; ret = ret->next )
		if ( v == ret->vim )
			return ret;
	if ( v->sys_type == 0 )
		return 0;
	ret = new ClipBoardList;
	memset(ret,0,sizeof(*ret));
	ret->vim = v;
	ret->next = cbl;
	cbl = ret;
	return ret;
}

void
purge_clipboard(ClipBoardList * c)
{
	if ( c->sys_data )
		d_f_ree(c->sys_data);
	c->sys_data = 0;
	c->sys_size = 0;
	if ( c->target )
		delete c->target;
	c->target = 0;
	clear_all_data(c);
}

bool
get_sys_clipboard(ClipBoardList * c)
{
	int len;
	if ( !VClipboard::is_data_type_available(c->vim->sys_type) )
		return false;
	if ( c->vim->sys_type == VDT_IMAGE )
		return true;
	char *data = (char*)VClipboard::get_data_type(c->vim->sys_type, len);
	if ( c->sys_data == 0 || c->sys_size != len )
		goto load;
	if ( memcmp(c->sys_data,data,len) == 0 ) {
		VClipboard::release_data();
		return false;
	}
load:
	if ( c->sys_data )
		d_f_ree(c->sys_data);
	c->sys_data = d_alloc(len);
	memcpy(c->sys_data,data,len);
	c->sys_size = len;
	VClipboard::release_data();
	return true;
}


void
insert_clipboard_variation(ClipBoardList* c,Vdata * d)
{
VdataList * lst;
	lst = new VdataList;
	lst->next = c->vdata_list;
	lst->d = d;
	c->vdata_list = lst;
}

Vdata *
delete_clipboard_variation(VIM * v)
{
VdataList * ret, ** p;
Vdata * rd;
ClipBoardList * c;
	for ( c = cbl ; c ; c = c->next ) {
		if ( cmp_VIM(v,c->vim) == VIM_INCOMP )
			continue;
		for ( p = &c->vdata_list ; *p ; p = &(*p)->next ) {
			ret = *p;
			if ( ret->d->get_type() == v ) {
				*p = ret->next;
				rd = ret->d;
				delete ret;
				return rd;
			}
		}
	}
	return 0;
}

Vdata *
check_clipboard_variation(VIM * v)
{
VdataList * p;
ClipBoardList * c;
	for ( c = cbl ; c ; c = c->next ) {
		if ( cmp_VIM(v,c->vim) == VIM_INCOMP )
			continue;
		for ( p = c->vdata_list ; p ; p = p->next )
			if ( p->d->get_type() == v )
				return p->d;
	}
	return 0;
}

void
clear_all_data(ClipBoardList *c)
{
VdataList * lst;
	for ( ; c->vdata_list ; ) {
		lst = c->vdata_list;
		c->vdata_list = lst->next;
		delete lst->d;
		delete lst;
	}
}


// ============= Vdata ==============

VIM VIM_Vdata = {0,VDT_NONE};

int
Vdata::get_size()
{
	return size;
}


void
Vdata::store_into_clipboard(Vdata * d)
{
}

ClipBoardList * 
Vdata::get_clipboard()
{
	return 0;
}


// ============= VdataString ==============


VIM VIM_VdataString = { &VIM_Vdata , VDT_STRING};

VdataString::VdataString(const char *str,int * erp,bool valid)
{
	if ( !valid )
		return;
	vdata_type = &VIM_VdataString;
	mData = str ? copy_str((char*)str) : 0;
	size = str ? strlen(str)+1 : 0;
	err = 0;
	if ( erp ) 
		*erp = err;
}


VdataString::VdataString(Vdata * d,int * erp,bool valid)
{
	if ( !valid )
		return;
	err = 0;
	vdata_type = &VIM_VdataString;
	switch ( cmp_VIM(d->get_type(),&VIM_VdataString) ) {
	case 0:
	case 1:
		mData = (void*)static_cast<VdataString*>(d)->get_VdataString(&err);
		break;
	default:
		err = -1;
		break;
	}
	if ( erp )
		*erp = err;
	if ( err < 0 )
		return;
	if ( mData == 0 )
		size = 0;
	else	size = strlen((char*)mData)+1;
	err = 0;
}

VdataString::~VdataString()
{
	if ( this->get_type() != &VIM_VdataString )
		return;
	if ( mData )
		d_f_ree(mData);
	mData = 0;
}

char*
VdataString::get_VdataString(int * erp)
{
	if ( err < 0 ) {
		if ( erp )
			*erp = err;
		return 0;
	}
	if ( mData == 0 )
		return 0;
	else	return copy_str((char*)mData);
}

ClipBoardList * 
VdataString::get_clipboard()
{
ClipBoardList * c;
char * tmp;

	c = search_clipboard(&VIM_VdataString);
	if ( get_sys_clipboard(c) ) {
		if ( c->target )
			delete c->target;
		if ( ((char*)c->sys_data)[c->sys_size-1] != 0 ) {
			tmp = (char*)d_alloc(c->sys_size+1);
			memcpy(tmp,c->sys_data,c->sys_size);
			tmp[c->sys_size] = 0;
			c->target = new VdataString(tmp);
			d_f_ree(tmp);
		}
		else {
			c->target = new VdataString((char*)c->sys_data);
		}
		clear_all_data(c);
	}
	return c;
}

bool
VdataString::clipboard_available(bool lock_flag)
{
Vdata * d;
VdataString * d2;
int er;
bool ret;
ClipBoardList* c;
	if ( lock_flag )
		lock_task(cb_lock);
	ret = false;
	d2 = static_cast<VdataString*>(check_clipboard_variation(&VIM_VdataString));
	if ( d2 ) {
		ret = true;
		goto end;
	}
	c = get_clipboard();
	if ( c == 0 )
		goto end;
	d = c->target;
	if ( d == 0 )
		goto end;
	d2 = new VdataString(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;
}

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

void
VdataString::store_into_clipboard(Vdata * d)
{
VdataString * str;
int er;
ClipBoardList * c;

	lock_task(cb_lock);
	
	if ( d == 0 ) {
		d = this;
		str = static_cast<VdataString*>(d);
		er = 0;
	}
	else	str = new VdataString(d,&er);
	if ( er < 0 ) 
		er_panic("store_into_clipboard");
	c = search_clipboard(&VIM_VdataString);
	purge_clipboard(c);
	c->target = d;
	c->sys_size = str->get_size();
	c->sys_data = str->get_VdataString(&er);
	if ( this != str )
		delete str;
	if ( er < 0 ) {
		purge_clipboard(c);
		goto end;
	}
	VClipboard::set_data_type(VDT_STRING, 
		c->sys_size, 
		c->sys_data, true);
end:
	unlock_task(cb_lock,"new_from_clipboard");
}


// ============= VdataLString ==============

VIM VIM_VdataLString = { &VIM_VdataString , VDT_NONE };

VdataLString::VdataLString(const L_CHAR *lstr,int * erp,bool valid)
	: VdataString((char*)0,0,0)
{
	if ( !valid )
		return;
	vdata_type = &VIM_VdataLString;
	mData = lstr ? ll_copy_str((L_CHAR*)lstr) : 0;
	size = lstr ? (l_strlen((L_CHAR*)lstr)+1)*sizeof(L_CHAR) : 0;
	err = 0;
	if ( erp )
		*erp = err;
}


VdataLString::VdataLString(const char *str,int * erp,bool valid)
	: VdataString((char*)0,0,0)
{
	if ( !valid )
		return;
	vdata_type = &VIM_VdataLString;
	mData = str ? nl_copy_str(std_cm, (char*)str) : 0;
	size = mData ? (l_strlen((L_CHAR*)mData)+1)*sizeof(L_CHAR) : 0;
	err = 0;
	if ( erp )
		*erp = err;
}

VdataLString::VdataLString(Vdata * d,int * erp,bool valid)
	: VdataString((char*)0,0)
{
VIM * v;
	if ( !valid )
		return;
	err = 0;
	vdata_type = &VIM_VdataLString;
	v = d->get_type();
	switch ( cmp_VIM(v,&VIM_VdataLString) ) {
	case 0:
	case 1:
		mData = (void*)static_cast<VdataLString*>(d)->get_VdataString(&err);
		break;
	case -1:
		if ( v == &VIM_VdataString ) {
		char * tmp;
			tmp = (char*)static_cast<VdataString*>(d)->get_VdataString(&err);
			if ( err == 0 ) {
				mData = nl_copy_str(std_cm,tmp);
				d_f_ree(tmp);
			}
		}
		else {
			err = -1;
		}
		break;
	default:
		err = -1;
		break;
	}
	if ( erp )
		*erp = err;
	if ( err < 0 )
		return;
	if ( mData == 0 )
		size = 0;
	else	size = l_strlen((L_CHAR*)mData)+1;
	err = 0;
}

VdataLString::~VdataLString()
{
	if ( this->get_type() != &VIM_VdataLString )
		return;
	if ( mData )
		d_f_ree(mData);
	mData = 0;
}

char*
VdataLString::get_VdataString(int * erp)
{
	if ( err < 0 ) {
		if ( erp )
			*erp = err;
		return 0;
	}
	if ( mData == 0 )
		return 0;
	else	return ln_copy_str(std_cm,(L_CHAR*)mData);
}

L_CHAR*
VdataLString::get_VdataLString(int * erp)
{
	if ( err < 0 ) {
		if ( erp )
			*erp = err;
		return 0;
	}
	if ( mData == 0 )
		return 0;
	else	return ll_copy_str((L_CHAR*)mData);
}

bool
VdataLString::clipboard_available(bool lock_flag)
{
Vdata * d;
VdataLString * d2;
int er;
bool ret;
ClipBoardList * c;
	if ( lock_flag )
		lock_task(cb_lock);
	ret = false;
	d2 = static_cast<VdataLString*>(check_clipboard_variation(&VIM_VdataLString));
	if ( d2 ) {
		ret = true;
		goto end;
	}
	c = get_clipboard();
	if ( c == 0 )
		goto end;
	d = c->target;
	if ( d == 0 )
		goto end;
	d2 = new VdataLString(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;
}

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

void
VdataLString::store_into_clipboard(Vdata * d)
{
VdataString * str;
int err;
	if ( d == 0 )
		d = this;
	str = new VdataString(d,&err);
	if ( err < 0 ) {
		delete str;
		return;
	}
	str->store_into_clipboard(d);
}


// ============= VdataImage ==============


VIM VIM_VdataImage = { &VIM_Vdata , VDT_IMAGE };

VdataImage::VdataImage(const VImage * img,int * erp,bool valid)
{
	if ( !valid )
		return;
	vdata_type = &VIM_VdataImage;
	v_image_ref((VImage*)img);
	mData = (void*)img;
	size = img->size.h * img->size.w * sizeof(int);
	err = 0;
	if ( erp ) 
		*erp = err;
}


VdataImage::VdataImage(Vdata * d,int * erp,bool valid)
{
VImage * img;
	if ( !valid )
		return;
	err = 0;
	vdata_type = &VIM_VdataImage;
	switch ( cmp_VIM(d->get_type(),&VIM_VdataImage) ) {
	case 0:
	case 1:
		mData = (void*)static_cast<VdataImage*>(d)->get_VdataImage(&err);
		break;
	default:
		err = -1;
		break;
	}
	if ( erp )
		*erp = err;
	if ( err < 0 )
		return;
	img = (VImage*)mData;
	size = img->size.h * img->size.w * sizeof(int);
	err = 0;
}

VdataImage::~VdataImage()
{
VImage * img;
	if ( this->get_type() != &VIM_VdataImage )
		return;
	img = (VImage*)mData;
	v_image_unref(img);
	mData = 0;
}

VImage *
VdataImage::get_VdataImage(int * erp)
{
VImage * ret;
	if ( err < 0 ) {
		if ( erp )
			*erp = err;
		return 0;
	}
	ret = (VImage*)mData;
	return ret;
}

bool
VdataImage::clipboard_available(bool lock_flag)
{
Vdata * d;
VdataImage * d2;
int er;
bool ret;
ClipBoardList* c;
	if ( lock_flag )
		lock_task(cb_lock);
	ret = false;
	d2 = static_cast<VdataImage*>(check_clipboard_variation(&VIM_VdataImage));
	if ( d2 ) {
		ret = true;
		goto end;
	}
	c = get_clipboard();
	if ( c == 0 )
		goto end;
	d = c->target;
	if ( d == 0 )
		goto end;
	d2 = new VdataImage(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;
}

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



void
VdataImage::store_into_clipboard(Vdata * d)
{
VdataImage * im;
int er;
ClipBoardList * c;
	lock_task(cb_lock);
	
	if ( d == 0 ) {
		d = this;
		im = static_cast<VdataImage*>(d);
		er = 0;
	}
	else	im = new VdataImage(d,&er);
	if ( er < 0 ) 
		er_panic("store_into_clipboard");
	c = search_clipboard(&VIM_VdataImage);
	purge_clipboard(c);
	c->target = d;

	c->sys_data = 0;

	if ( this != im )
		delete im;
	if ( er < 0 ) {
		purge_clipboard(c);
		goto end;
	}
	VClipboard::set_image((VImage*)mData,&c->sys_size);
end:
	unlock_task(cb_lock,"new_from_clipboard");
}

ClipBoardList * 
VdataImage::get_clipboard()
{
ClipBoardList * c;
VImage * img;

	c = search_clipboard(&VIM_VdataImage);
	if ( get_sys_clipboard(c) ) {
		if ( c->target )
			delete c->target;
		c->target = 0;
		clear_all_data(c);

		img = VClipboard::get_image();
		c->target = new VdataImage(img);
		v_image_unref(img);
	}
	return c;
}

