/**********************************************************************
 
	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	<gdk/gdk.h>
#include	<gdk/gdkx.h>
#include	"v/VDisplay.h"

extern "C" {

#include "task.h"
#include "pri_level.h"
#include "lock_level.h"
#include "v_pango.h"
#include "memory_debug.h"
#include "utils.h"
#include "machine/v_object.h"
#include "v/v_types.h"
#include "machine/msequence.h"

int get_tid();
int (*vobject_quit_callback)() = 0;

int running_state = 1;
bool suspend_gtk_main = false;
int gtk_main_thread_id = 0;
extern int loop_task_id;
extern int mseq_flag2;
SEM suspend_gtk_main_lock;

// ------------------------------
// vobject_init
// ------------------------------


LC_FONT_ENGINE_TYPE * vobj_fet[] = {&pango_font_engine_type,0};


Display *
get_gdk_display(char * dis)
{
Display * ret;
GdkPixmap * pm;
	pm = gdk_pixmap_new(NULL,1,1,1);
	ret = GDK_PIXMAP_XDISPLAY(pm);
	gdk_pixmap_unref(pm);
	return ret;
}


int
vobject_init_m1(int *argc, char ***argv)
{
	suspend_gtk_main_lock = new_lock(LL_VOBJECT);
	
	gtk_init(argc, argv);
	gdk_rgb_init();
	gtk_widget_set_default_colormap(gdk_rgb_get_cmap());
	gtk_widget_set_default_visual(gdk_rgb_get_visual()); 
	v_init_pango();
	return 0;
}

int
vobject_init_m2(int *argc, char ***argv)
{
	return 0;
}

// ------------------------------
// vobject_main
// ------------------------------


int
call_gtk_main(void *)
{
// called via mseq
	gtk_main_thread_id = get_tid();
	if ( ! suspend_gtk_main )
		gtk_main();
	return 0;
}


int
_set_suspend_gtk_main(void *k)
{
// called via mseq
	suspend_gtk_main = (int)k;
	if ( k == 0 ) {
		wakeup_task((int)&suspend_gtk_main);
	}
	return 0;
}

void
set_suspend_gtk_main(int suspend)
{
	static int count_locked_object = 0;
	if ( get_tid() != loop_task_id ) {
		lock_task(suspend_gtk_main_lock);
		if ( suspend ) {
			if ( count_locked_object++ == 0 )
				ms_do(_set_suspend_gtk_main, (void*)1,1, "set_suspend_gtk_main");
		}
		else {
			if ( --count_locked_object == 0 )
				ms_do(_set_suspend_gtk_main, 0,1, "set_suspend_gtk_main");
		}
		unlock_task(suspend_gtk_main_lock, "set_suspend_gtk_main");
	}
}


int gtk_notif_pipe[2];
int notif_waiting = 0;
SEM notif_lock;

void
call_gtk_main_task()
// thread calling gtk_main via msequence
{
	while( mseq_flag2 == 0 )
		sleep(1);
	while ( running_state ) {
		lock_task(notif_lock);
		if ( notif_waiting )
			sleep_task((int)&notif_lock, notif_lock);
		else
			unlock_task(notif_lock, "call_gtk_main_task");
		ms_do(call_gtk_main,0,0,"call_gtk_main");
		lock_task(suspend_gtk_main_lock);
		for ( ; suspend_gtk_main ; ) {
			sleep_task((int)&suspend_gtk_main,
				suspend_gtk_main_lock);
			lock_task(suspend_gtk_main_lock);
		}
		unlock_task(suspend_gtk_main_lock,"call_gtk_main_task");
	}
/* test *
	printf(" * test * sleep for 15 sec before quit...\n");
	sleep_sec(15);
	printf("exit...\n");
// end */
	gtk_exit(0);
	exit(0);
}

void
exec_in_gtk_main(void(*func)(void*), void *arg)
{
	if ( gtk_main_thread_id == 0 ) {
	  ms_do((int(*)(void*))func, arg, 1, "from exec_in_gtk_main");
	}
	else if ( get_tid() != gtk_main_thread_id ) {
		lock_task(notif_lock);
		write(gtk_notif_pipe[1],"e",1);
		write(gtk_notif_pipe[1],&func,sizeof(func));
		write(gtk_notif_pipe[1],&arg,sizeof(arg));
		sleep_task((unsigned int)arg,notif_lock);
	}
	else {
		(*func)(arg);
	}
}

void
recv_pipe_notif(gpointer data, gint src, GdkInputCondition cond)
// called from g_main_loop
{
	char s;
	void(*func)(void*);
	void *arg;
	if ( read(src,&s,1) == 1 ) {
		if ( s == 'q' ) {
			gtk_main_quit();
		}
		else if ( s == 'e' ) {
			lock_task(notif_lock);
			read(src,&func,sizeof(func));
			read(src,&arg,sizeof(arg));
			unlock_task(notif_lock, "recv_pipe_notif");
			(*func)(arg);
			wakeup_task((unsigned int)arg);
		}
	}
}

void
mseq_gtk_notif(int (*func)(void*), void * arg, char str[])
// called in ms_do when request incoming
{
	if ( func != call_gtk_main ) {
		lock_task(notif_lock);
		if ( notif_waiting == 0 ) {
			write(gtk_notif_pipe[1],"q",1);
		}
		notif_waiting++;
		unlock_task(notif_lock, "mseq_gtk_notif");
	}
}

void
mseq_gtk_notif_end(int (*func)(void*), void * arg, char str[])
// called in ms_do when request processed
{
	if ( func != call_gtk_main ) {
		lock_task(notif_lock);
		if ( --notif_waiting == 0 )
			wakeup_task((int)&notif_lock);
		unlock_task(notif_lock, "mseq_gtk_notif_end");
	}
}


int
vobject_main()
{
	extern void (*ms_put_call_back)(int (*func)(void*), void * arg, char str[]);
	extern void (*ms_done_call_back)(int (*func)(void*), void * arg, char str[]);
	
	if ( pipe(gtk_notif_pipe) != 0 )
		er_panic("gtk_notif_pipe");
	gdk_input_add(gtk_notif_pipe[0],GDK_INPUT_READ,recv_pipe_notif,0);
	ms_put_call_back = mseq_gtk_notif;
	ms_done_call_back = mseq_gtk_notif_end;
	notif_lock = new_lock(LL_VOBJECT);
	
	create_task(call_gtk_main_task,0,PRI_USER_INTERFACE);
	ms_loop();
	return 0;
}


// ------------------------------
// vobject_quit
// ------------------------------

void
_vobject_flush_event()
{
	while (g_main_iteration(FALSE));
}

void
vobject_flush_event()
{
	v_serialized_exec_sub(_vobject_flush_event);
}

int
_vobject_quit()
{
// called via ms
	if ( gtk_main_level() ) {
		while (g_main_iteration(FALSE));
		gtk_main_quit();
	}
	running_state = 0;
	return 1;
}

int
vobject_quit()
{
	if ( vobject_quit_callback && !(*vobject_quit_callback)() )
		return 0;
	VDisplay::get_main_display()->destroy();
	return v_serialized_exec_func(_vobject_quit);
}


// ------------------------------
// vobject_layout
// ------------------------------

void
_vobject_layout()
{
	while (g_main_iteration(FALSE));
}

void
vobject_layout()
{
	v_serialized_exec_sub(_vobject_layout);
}


// ------------------------------
// vobject_beep
// ------------------------------

void
vobject_beep()
{
	gdk_beep();
}


unsigned int
vobject_get_hilite_color()
{
	static unsigned int hilite_color = 0;
	if ( hilite_color )
		return hilite_color;
	GdkColor *c;
	c = &v_serialized_exec_func(gtk_widget_get_default_style)->base[GTK_STATE_SELECTED];
	SET_RGB8_32(hilite_color, c->red>>8, c->green>>8, c->blue>>8, 0xff);
	return hilite_color;
}

// ------------------------------
// changed call_back
// ------------------------------


void
value_changed(GtkWidget *w, gpointer c)
{
	((VObject*)c)->value_changed();
}

void
descriptor_changed(GtkWidget *w, gpointer c)
{
	((VObject*)c)->descriptor_changed();
}


// ------------------------------
// set_styled_descriptor
// ------------------------------

char *
ucd_n_string(const L_CHAR *lc)
{
	char *ret = code_convert_lc2str(
		const_cast<L_CHAR*>(lc), &utf8_cm, 0);
	set_buffer(ret);
	return ret;
}

void
_set_styled_descriptor_style(VInfo * info, char * font, int is_bin)
{
	if ( is_bin )
		info = GTK_BIN(info)->child;
	if ( info ) {
		if ( font ) {
			gtk_widget_modify_font(info,
				pango_font_description_from_string(font));
		}
	}
}

void
_set_styled_descriptor_desc(VInfo * info, char * desc, int is_bin)
{
	if ( is_bin )
		info = GTK_BIN(info)->child;
	if ( info ) {
		if ( desc )
			gtk_label_set_text(GTK_LABEL(info), desc);
		else
			gtk_label_set_text(GTK_LABEL(info), "");
	}
}

void
_set_styled_descriptor(VInfo * info, char * desc, char * font, int is_bin)
{
	_set_styled_descriptor_style(info, font, is_bin);
	_set_styled_descriptor_desc(info, desc, is_bin);
}

char *
_set_styled_descriptor_get_font(
	const L_CHAR * descriptor,
	const LC_WRITING_STYLE * ws,
	int fsize)
{
V_PANGO_FONT * vf;
char *font = 0;
LC_FONT * f;
int	size;
LC_WS_COND cond;
int i;
	
	if ( fsize <= 0 )
		fsize = V_DEFAULT_FSIZE;
	
	if ( ws == 0 ) {
		font = (char*)d_alloc(20);
		sprintf(font,"%i", fsize/10);
		return  font;
	}
	
	cond.cond = WSC_FET_LIST;
	cond.fet = &vobj_fet[0];
	f = lc_select_font(&size,
		const_cast<L_CHAR*>(descriptor),
		l_strlen(const_cast<L_CHAR*>(descriptor)),
		const_cast<LC_WRITING_STYLE*>(ws),
		fsize, &cond,0);
	if ( f ) {
		for ( i = 0 ; i < f->fw_len ; i ++ )
			if ( f->fw_list[i].fe->type ==
			&pango_font_engine_type ) {
			vf = (V_PANGO_FONT*)f->fw_list[i].work;
			font = (char*)d_alloc(
				strlen(vf->fontname) + 20);
			sprintf(font,"%s %i",
				vf->fontname,
				size/10);
			break;
		}
	}
	else	return 0;
	return font;
}

void
set_styled_descriptor(
	const L_CHAR * descriptor,
	const LC_WRITING_STYLE * ws,
	int fsize,
	int vert_desc,
	VInfo * info,
	int is_bin)
{
char *ucd, *font = 0;

	if ( descriptor == 0 ) {
		v_serialized_exec_sub(_set_styled_descriptor, info, (char*)0, font, is_bin);
		return;
	}
	
	font = _set_styled_descriptor_get_font(descriptor, ws, fsize);
	if ( vert_desc )
		ucd = ucd_n_string(v_make_vert_desc(descriptor));
	else
		ucd = ucd_n_string(descriptor);
	
	v_serialized_exec_sub(_set_styled_descriptor, info, ucd, font, is_bin);
	
	if ( font )
		d_f_ree(font);
}

// ------------------------------
// standard set/get status
// ------------------------------

VExError
v_get_status_standard(VObjectStatus *s, int *flags, const VInfo *info)
{
	if ( *flags & VSF_VISIBLE ) {
		s->visible = GTK_WIDGET_VISIBLE(info);
	}
	if ( *flags & VSF_ENABLED ) {
		s->enabled = GTK_WIDGET_SENSITIVE(info);
	}
	if ( *flags & VSF_SIZE ) {
		s->size = (VSize){info->allocation.width, info->allocation.height};
	}
	*flags &= ~(VSF_VISIBLE|VSF_ENABLED|VSF_SIZE);
	return initial_VExError(V_ER_NO_ERR,*flags,0);
}

VExError
v_set_status_standard(const VObjectStatus *s, int flags, VInfo *info)
{
	if ( flags & VSF_ENABLED ) {
		v_serialized_exec_sub(gtk_widget_set_sensitive, info, s->enabled);
	}
	if ( flags & VSF_VISIBLE ) {
		if ( s->visible )
			v_serialized_exec_sub(gtk_widget_show, info);
		else
			v_serialized_exec_sub(gtk_widget_hide, info);
	}
	return initial_VExError(V_ER_NO_ERR,flags&~(VSF_ENABLED|VSF_VISIBLE),0);
}

// ------------------------------
// create_do utility
// ------------------------------

VExError
return_create_do(
	VObject * this_obj,
	VObject * nmp,
	VObjectStatus * this_sts,
	const VObjectStatus * s,
	int flags,
	int already_flags)
{
VExError er,er2;

	if ( flags & VSF_PADDING )
		this_sts->padding = s->padding;
	else 	this_sts->padding = v_default_sts.padding;
	if ( flags & VSF_ALIGN ) {
		this_sts->alignh = s->alignh;
		this_sts->alignv = s->alignv;
	}
	else {
		this_sts->alignh = v_default_sts.alignh;
		this_sts->alignv = v_default_sts.alignv;
	}

	this_obj->set_status(&v_default_sts,VSF_DEF_FLAGS &
			(~(flags|already_flags)));
	er = this_obj->set_status(s,flags & VSF_DEF_FLAGS &
			(~already_flags));
	if ( er.code ) {
		printf("return_create_do error : %s %x %x %x\n",
			this_obj->describe_self(),
			er.code, er.subcode1, er.subcode2);
		return er;
	}
	er.subcode1 = already_flags&(~VSF_ALREADY_FLAGS);
	er2 = nmp->add_child_do(this_obj);
	if ( er2.code ) {
		printf("return_create_do error 2 : %s %x %x %x\n",
			this_obj->describe_self(),
			er2.code, er2.subcode1, er2.subcode2);
		return er2;
	}
	return er;
}

} // extern "C"

