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

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


#include	<stdio.h>
#include	<stdlib.h>
#include	<X11/Xatom.h>
#include	"memory_debug.h"
#include	"utils.h"
#include	"machine/v_x11.h"
#include	"long_char.h"

int v_get_font(LC_FONT *,LC_FONT_ENGINE *);

VDISPLAY * disp_list;

LC_FONT_ENGINE_TYPE X_font_engine_type = {
	"X",
	v_get_font,
	v_get_string_pic,
	0,
	{0,0},
	0
};

char * display_key = 0;

Display * get_x11_display(char *);

Display * (*get_display)(char *) = get_x11_display;

int _err_handler(
    Display*		/* display */,
    XErrorEvent*	/* error_event */
);



void
v_set_display_key(char * key)
{
	display_key = copy_str(key);
}

void
get_mask_info(int * mul,int * max,int * shift,unsigned int mask)
{
int msb,lsb;
	if ( mask == 0 ) {
		*mul = 0;
		*max = 0;
		*shift = 0;
		return;
	}
	for ( lsb = 0 ; lsb < 32 && (mask&(1<<lsb)) == 0 ; lsb ++ );
	for ( msb = lsb ; msb < 32 && (mask&(1<<msb)) != 0 ; msb ++ );
	*mul = 1<<lsb;
	*max = 1<<(msb-lsb);
	*shift = 0xffff / ((1<<(msb-lsb))-1);
}

int
set_gb2m(unsigned int * table,unsigned int mask)
{
int msb,lsb;
int shift;
int i;

	if ( mask == 0 )
		return -1;
	for ( lsb = 0 ; lsb < 32 && (mask&(1<<lsb)) == 0 ; lsb ++ );
	for ( msb = lsb ; msb < 32 && (mask&(1<<msb)) != 0 ; msb ++ );
	if ( msb < COL_BIT ) {
		shift = COL_BIT - msb;
		for ( i = 0 ; i < (1<<COL_BIT) ; i ++ )
			table[i] = (i>>shift)&mask;
	}
	else if ( msb == COL_BIT ) {
		for ( i = 0 ; i < (1<<COL_BIT) ; i ++ )
			table[i] = i&mask;
	}
	else {
		shift = msb - COL_BIT;
		for ( i = 0 ; i < (1<<COL_BIT) ; i ++ )
			table[i] = (i<<shift)&mask;
	}
	return 0;
}

void
get_gb2m_mul_shift(unsigned long *mul,char * shift,int mask)
{
int lsb,msb;
	for ( lsb = 0 ; lsb < 32 && (mask&(1<<lsb)) == 0 ; lsb ++ );
	for ( msb = lsb ; msb < 32 && (mask&(1<<msb)) != 0 ; msb ++ );
	*shift = lsb;
	*mul = COL_MASK / ((1<<(msb-lsb))-1);
}


Display *
get_x11_display(char * dis)
{
	return XOpenDisplay(dis);
}


typedef struct v_open_disp_s {
	VDISPLAY *		ret;
	VERROR *		err;
} V_OPEN_DISP_S;

int
_v_open_disp(V_OPEN_DISP_S * buf)
{
VDISPLAY * d;
XVisualInfo tmp, * v;
int match;
XColor * acolor;
int i;
char * dis;
int max,mul,shift;
V_OPEN_WIN_S ow;

	acolor = d_alloc(sizeof(XColor)*0x10000); 
	d = d_alloc(sizeof(*d));
	d->next = disp_list;
	disp_list = d;
/*
	(void*)XSetErrorHandler(_err_handler);
*/
	if ( display_key == 0 )
		dis = getenv("DISPLAY");
	else	dis = display_key;
	if ( dis == 0 ) {
		fprintf(stderr,"undefined env variable DISPLAY\n");
		exit(1);
	}
	d->d = (*get_display)(dis);
	if ( d->d == 0 ) {
		fprintf(stderr,"cannot open the display \"%s\"\n",
			dis);
		exit(1);
	}

	d->depth = 24;

	tmp.screen = 0;
	tmp.depth = d->depth;
	tmp.class = DirectColor;
	v = XGetVisualInfo(
		d->d,
		VisualClassMask|VisualScreenMask|VisualDepthMask,
		&tmp,
		&match);
	if ( match )
		goto next;

	tmp.screen = 0;
	tmp.depth = d->depth;
	tmp.class = TrueColor;
	v = XGetVisualInfo(
		d->d,
		VisualClassMask|VisualScreenMask|VisualDepthMask,
		&tmp,
		&match);
	if ( match )
		goto next;


	d->depth = 16;

	tmp.screen = 0;
	tmp.depth = d->depth;
	tmp.class = DirectColor;
	v = XGetVisualInfo(
		d->d,
		VisualClassMask|VisualScreenMask|VisualDepthMask,
		&tmp,
		&match);
	if ( match )
		goto next;

	tmp.screen = 0;
	tmp.depth = d->depth;
	tmp.class = TrueColor;
	v = XGetVisualInfo(
		d->d,
		VisualClassMask|VisualScreenMask|VisualDepthMask,
		&tmp,
		&match);
	if ( match )
		goto next;


	d->depth = 8;

	tmp.screen = 0;
	tmp.depth = d->depth;
	tmp.class = DirectColor;
	v = XGetVisualInfo(
		d->d,
		VisualClassMask|VisualScreenMask|VisualDepthMask,
		&tmp,
		&match);
	if ( match )
		goto next;

	tmp.screen = 0;
	tmp.depth = d->depth;
	tmp.class = TrueColor;
	v = XGetVisualInfo(
		d->d,
		VisualClassMask|VisualScreenMask|VisualDepthMask,
		&tmp,
		&match);
	if ( match )
		goto next;

	fprintf(stderr,"cannot open the display\n");
	fprintf(stderr,"the DirectColor and TrueColor is not support\n");
	exit(1);
next:


	d->visual = v[0].visual;
switch ( d->visual->class ) {
case TrueColor:
	printf("class = TrueColor ");
	break;
case DirectColor:
	printf("class = DirectColor ");
	break;
default:
	er_panic("other class");
}

	d->r = RootWindow(d->d,0);

	d->gb2m_map.map[GB2M_R] = d_alloc(sizeof(int)*(1<<COL_BIT));
	d->gb2m_map.map[GB2M_G] = d_alloc(sizeof(int)*(1<<COL_BIT));
	d->gb2m_map.map[GB2M_B] = d_alloc(sizeof(int)*(1<<COL_BIT));
	if ( d->depth > 16 )
		d->gb2m_map.pixel_bits = 32;
	else if ( d->depth > 8 )
		d->gb2m_map.pixel_bits = 16;
	else	d->gb2m_map.pixel_bits = 8;
	set_gb2m(d->gb2m_map.map[GB2M_R],d->visual->red_mask);
	set_gb2m(d->gb2m_map.map[GB2M_G],d->visual->green_mask);
	set_gb2m(d->gb2m_map.map[GB2M_B],d->visual->blue_mask);
printf("RGB %x %x %x\n",
d->gb2m_map.map[GB2M_R][0x3ff],
d->gb2m_map.map[GB2M_G][0x3ff],
d->gb2m_map.map[GB2M_B][0x3ff]);
	d->gb2m_map.red_mask = d->visual->red_mask;
	d->gb2m_map.green_mask = d->visual->green_mask;
	d->gb2m_map.blue_mask = d->visual->blue_mask;
	get_gb2m_mul_shift(
		&d->gb2m_map.red_mul,
		&d->gb2m_map.red_shift,
		d->visual->red_mask);
	get_gb2m_mul_shift(
		&d->gb2m_map.green_mul,
		&d->gb2m_map.green_shift,
		d->visual->green_mask);
	get_gb2m_mul_shift(
		&d->gb2m_map.blue_mul,
		&d->gb2m_map.blue_shift,
		d->visual->blue_mask);

	d->cmap = 0;

	if ( tmp.class == DirectColor ) {
		d->cmap = XCreateColormap(d->d,d->r,d->visual,AllocAll);
		get_mask_info(&mul,&max,&shift,d->visual->blue_mask);
		for ( i = 0 ; i < max ; i ++ ) {
			acolor[i].pixel = i*mul;
			acolor[i].red = acolor[i].green = acolor[i].blue
					= i*shift;
			acolor[i].flags = DoBlue;
		}
		acolor[max-1].red = acolor[max-1].green = acolor[max-1].blue
					= 0xffff;
		XStoreColors(d->d,d->cmap,acolor,max);

		get_mask_info(&mul,&max,&shift,d->visual->red_mask);
		for ( i = 0 ; i < max ; i ++ ) {
			acolor[i].pixel = i*mul;
			acolor[i].red = acolor[i].green = acolor[i].blue
					= i*shift;
			acolor[i].flags = DoRed;
		}
		acolor[max-1].red = acolor[max-1].green = acolor[max-1].blue
					= 0xffff;
		XStoreColors(d->d,d->cmap,acolor,max);
		get_mask_info(&mul,&max,&shift,d->visual->green_mask);
		for ( i = 0 ; i < max ; i ++ ) {
			acolor[i].pixel = i*mul;
			acolor[i].red = acolor[i].green = acolor[i].blue
				= i*shift;
			acolor[i].flags = DoGreen;
		}
		acolor[max-1].red = acolor[max-1].green = acolor[max-1].blue
					= 0xffff;
		XStoreColors(d->d,d->cmap,acolor,max);
		XInstallColormap(d->d,d->cmap);
	}

	d_f_ree(acolor);

	d->fe.work = (void*)d;
	d->fe.type = &X_font_engine_type;
	d->fe.name = dis;
	insert_font_engine(&d->fe);

	buf->err->err1 = E_OK;
	buf->ret = d;
	ow.ret = 0;
	ow.err = buf->err;
	ow.d = d;
	ow.x = ow.y = 0;
	ow.w = ow.h = 100;
	ow.map = 0;

	_v_open_win(&ow);

	if ( ow.err->err1 != E_OK )
		return 0;

	d->for_string_pic = ow.ret;

	return 0;
}


int _err_handler(Display * dd,XErrorEvent * ev)
{
VDISPLAY * d;
char str[64];
	for ( d = disp_list ; d ; d = d->next )
		if ( d->d == dd )
			break;
	XGetErrorText(dd,ev->error_code,str,64);
	fprintf(stderr,"error Message = %s\n",str);
	fprintf(stderr,"serial = %d\n",(int)ev->serial);
	fprintf(stderr,"error code = %d\n",ev->error_code);
	fprintf(stderr,"request code = %d\n",ev->request_code);
/*
	fprintf(stderr,"minor code = %d\n".ev->minor_code);
*/
	return 0;
}


VDISPLAY *
v_open_disp(VERROR * err)
{
V_OPEN_DISP_S v;
	v.err = err;
	v.ret = 0;
	ms_do((int (*)(void*))_v_open_disp,&v,1,"v_open_disp");
	return v.ret;
}
