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

#include <LControlPane.h>

#include "VApplication.h"
#include "v/VDisplay.h"
#include "v/VMenu.h"
#include	"v/v.h"

extern "C" {
#include	"memory_debug.h"
#include	"pri_level.h"

LC_FONT_ENGINE fe;
extern LC_FONT_ENGINE_TYPE mac_font_engine_type;
LC_FONT_ENGINE_TYPE *vobj_fet[] = {&mac_font_engine_type, 0};

int (*vobject_quit_callback)() = 0;

int vobject_init_m1(int *argc, char ***argv);
int vobject_init_m2(int *argc, char ***argv);
void v_min_size_from_desc(const L_CHAR *desc, VSize* min_size, FMFontFamily fid, short fsize);
void v_nl2rt(unsigned char *d);
VFONT* _v_set_descriptor_get_font(LC_FONT ** ff,const L_CHAR * descriptor,
			const LC_WRITING_STYLE * ws,
			int fsize, int *size);
char * mac_string(LC_FONT * f, const L_CHAR * s);
void v_set_descriptor(LPane *pane,
		const L_CHAR *desc,
		const LC_WRITING_STYLE *ws,
		int fsize,
		bool vert_desc,
		VSize *min_size);
LTabGroup*GetTabGroupForWindow(LView* view);

extern VMenuItem *menu_no_window;

int
vobject_init_m1(int *argc, char ***argv)
{
//	VERROR err;
//	disp = v_open_disp(&err);

	fe.work = &fe;
	fe.type = &mac_font_engine_type;
	fe.name = "Main";
	insert_font_engine(&fe);
	return 0;
}

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

int
vobject_main()
{
	if ( LThread::InMainThread() ) {
		create_task(ms_loop,0,PRI_TOOLKIT);
		return 0;
	}
	ms_loop();
	return 0;
}

int
vobject_quit()
{
	static bool quit_flag = 0;
	if ( quit_flag == 0 ) {
		if ( vobject_quit_callback && !(*vobject_quit_callback)() )
			return 0;
		quit_flag = 1;
		theApp->SendAEQuit();
	}
	return 1;
}

void
vobject_layout()
{
	VLayout::layout_marked_window();
}

void
vobject_beep()
{
	SysBeep(9);
}

unsigned int
vobject_get_hilite_color()
{
	static unsigned int hilite_color = 0;
	if ( hilite_color )
		return hilite_color;
	RGBColor rgb;
	LMGetHiliteRGB(&rgb);
	SET_RGB8_32(hilite_color, rgb.red>>8, rgb.green>>8, rgb.blue>>8, 0xff);
	return hilite_color;
}

void
v_min_size_from_desc(const L_CHAR *desc, VSize* min_size, FMFontFamily fid, short fsize)
{
	*min_size = (VSize){0,0};
	if ( desc == 0 || desc[0] == 0 ) {
		return;
	}

	OSStatus err;
	FMFont fm_font;
	FMFontStyle fm_style;
	err = FMGetFontFromFontFamilyInstance(fid, 0, &fm_font, &fm_style);
	if ( err ) {
		printf("Font Manager Error : %d\n", err);
		return;
	}
	
	L_CHAR *u = code_convert_with_combine(
			(L_CHAR*)desc, l_strlen((L_CHAR*)desc),
			utf16_cm.main_code,
			CBF_SRC_PLANE|CBF_DST_PLANE);
	int len = l_strlen(u);
	char *ucd = n_string(&utf16_cm, u);
	d_f_ree(u);
	
	int last = 0;
	for ( int i = 1 ; i <= len+1 ; i++ ) {
		UniChar target = ((UniChar*)ucd)[i];
		if ( i <= len && target != '\n' )
			continue;
		
		ATSUStyle style;
		ATSUCreateStyle(&style);
		Fixed size = (fsize<=0 ? V_DEFAULT_FSIZE : fsize)/10 << 16;
		ATSUSetAttributes(style, 2,
			(ATSUAttributeTag[]){kATSUFontTag, kATSUSizeTag},
			(ByteCount[]){sizeof(FMFont), sizeof(Fixed)},
			(ATSUAttributeValuePtr[]){&fm_font, &size});

		ATSUTextLayout layout;
		UniCharCount cnt[] = {kATSUToTextEnd};
		err = ATSUCreateTextLayoutWithTextPtr(
				((UniChar*)ucd)+last+1, 0, i-last-1,
				i-last-1, 1, cnt, &style, &layout);
		if ( err ) {
			printf("ATSUI Create Layout Error : %d\n", err);
			ATSUDisposeStyle(style);
			return;
		}
		
		ATSTrapezoid trapezoid;
		ItemCount c;
		err = ATSUGetGlyphBounds(
				layout, 0, 0, kATSUFromTextBeginning, kATSUToTextEnd,
				kATSUseDeviceOrigins, 1, &trapezoid, &c);
		if ( err ) {
			printf("ATSUI Measure Text Error : %d\n", err);
			ATSUDisposeStyle(style);
			ATSUDisposeTextLayout(layout);
			return;
		}
		
#define max(a,b) ((a)>(b) ? (a) : (b))
#define min(a,b) ((a)<(b) ? (a) : (b))
		Fixed w = max(trapezoid.upperRight.x, trapezoid.lowerRight.x)
				- min(trapezoid.upperLeft.x, trapezoid.lowerLeft.x);
		Fixed h = max(trapezoid.lowerRight.y, trapezoid.lowerLeft.y)
				- min(trapezoid.upperRight.y, trapezoid.upperLeft.y);
		w += 0x8000;
		if ( w & 0xffff )
			w += 0x10000;
		if ( h & 0xffff )
			h += 0x10000;
		
		short w0 = w >> 16, h0 = h >> 16;
		if ( min_size->w < w0 )
			min_size->w = w0;
		min_size->h += h0;
		
		ATSUDisposeTextLayout(layout);
		ATSUDisposeStyle(style);
		
		last = i;
	}
}


void
v_nl2rt(unsigned char *d)
{
	int c, len = d[0];
	short script = FontScript();
	
	for ( int i = 1 ; i <= len ; ) {
		c = CharacterByteType((Ptr)d,i,script);
		if ( c == smSingleByte && d[i] == '\n' ) {
			d[i] = '\r';
		}
		if ( c == smFirstByte )
			i += 2;
		else
			i++;
	}
}

VFONT*
_v_set_descriptor_get_font(LC_FONT ** ff,const L_CHAR * descriptor,
			const LC_WRITING_STYLE * ws,
			int fsize, int *size)
{
	LC_FONT *f;
	LC_WS_COND cond;
//	LC_FONT_ENGINE_TYPE *fet[] = {&mac_font_engine_type, 0};
	cond.cond = WSC_FET_LIST;
	cond.fet = &vobj_fet[0];
	f = lc_select_font(size,
		const_cast<L_CHAR*>(descriptor),
		descriptor ? l_strlen(const_cast<L_CHAR*>(descriptor)) : 0,
		const_cast<LC_WRITING_STYLE*>(ws),
		fsize, &cond, 0);
	if ( f ) {
		for ( int i = 0 ; i < f->fw_len ; i ++ ) {
			if ( f->fw_list[i].fe->type == &mac_font_engine_type ) {
				*ff = f;
				return (VFONT*)f->fw_list[i].work;
			}
	    }
	}
	*ff = f;
	return 0;
}


char *
mac_string(LC_FONT * f, const L_CHAR * s)
{
L_CHAR *_str = const_cast<L_CHAR*>(s);
L_CHAR tmask;
char * ret;
char * p;
int len;
L_CHAR * str,* org;
LCZ_SET lset[3];

	if ( f ) {
		font2lcz(lset,f);
		str = org = code_convert_with_combine(
						_str,l_strlen(_str),
						lset, CBF_SRC_PLANE|CBF_DST_PLANE);
	}
	else {
		return n_string(std_cm, _str);
	}
	len = l_strlen(str);
	ret = (char*)d_alloc(len*2 + 1);
	p = ret;
	for ( ; *str ; str ++ ) {
		tmask = get_lc_mask(*str);
		switch ( tmask ) {
		case LCZM_1B_TYPE:
		case LCZM_7b_TYPE:
			*p ++ = (*str) & 0x0ff;
			break;
		case LCZM_2B_TYPE:
			if ( (*str) & 0xff00 ) {
				*p ++ = ((*str)>>8) & 0x0ff;
				*p ++ = (*str) & 0x0ff;
			}
			else {
				*p ++ = (*str) & 0x0ff;
			}
			break;
		case LCZM_4B_TYPE:
			if ( (LCZM_7b_TYPE & (*str)) == 0 )
				*p ++ = (*str) & 0x0ff;
			else	*p ++ = '?';
			break;
		default:
			*p ++ = '?';
			break;
		}
	}
	*p ++ = 0;
	if ( org )
		d_f_ree(org);
	set_buffer(ret);
	return ret;
}

void
v_set_descriptor(LPane *pane,
		const L_CHAR *desc,
		const LC_WRITING_STYLE *ws,
		int fsize,
		bool vert_desc,
		VSize *min_size)
{
	VFONT *vf = 0;
	LC_FONT * f = 0;
	int size = 0;
	LControlPane *c = dynamic_cast<LControlPane*>(pane);
	if ( c ) {
		ControlFontStyleRec cfs;
		cfs.flags = kControlUseFontMask | kControlUseSizeMask;

		if ( desc && ws ) {
			vf = _v_set_descriptor_get_font(&f,desc, ws, fsize, &size);
			cfs.font = vf ? vf->id : 0;
		}
		else
			cfs.font = 0;

		if ( size <= 0 )
			size = fsize;
		if ( size >= 0 )
			cfs.size = size/10;
		else
			cfs.size = 0;

		c->SetFontStyle(cfs);
	}
	else if ( desc && ws )
		vf = _v_set_descriptor_get_font(&f,desc, ws, fsize, &size);
	
	LStr255 d;
	if ( desc == 0 || desc[0] == 0 )
		d = "\p";
	else {
		if ( vert_desc )
			desc = v_make_vert_desc(desc);
		d = mac_string(f,desc);
		v_nl2rt(d);
	}

	StGDeviceSaver gds;
	{
		StGrafPortSaver gps;
		pane->FocusDraw();
		pane->SetDescriptor(d);
		
		if ( min_size )
			v_min_size_from_desc(desc,min_size,vf?vf->id:0,fsize);
	}
}


VExError
v_get_status_standard(VObjectStatus *s, int *flags, const VInfo *info)
{
	if ( *flags & VSF_POSITION ) {
		SPoint32 pos;
		info->GetFrameLocation(pos);
		s->position.x = pos.h;
		s->position.y = pos.v;
	}
	if ( *flags & VSF_SIZE ) {
		SDimension16 size;
		info->GetFrameSize(size);
		s->size.w = size.width;
		s->size.h = size.height;
	}
	if ( *flags & VSF_ENABLED ) {
		s->enabled = info->IsEnabled();
	}
	*flags &= ~(VSF_POSITION|VSF_SIZE|VSF_ENABLED);
	return initial_VExError(V_ER_NO_ERR,*flags,0);
}

VExError
v_set_status_standard(const VObjectStatus *s, int flags, VObjectStatus *sts, VInfo *info)
{
	VExError err = initial_VExError(
			V_ER_NO_ERR,flags&~(VSF_POSITION|VSF_SIZE|VSF_ENABLED|VSF_VISIBLE),0);
	if ( flags & VSF_SIZE ) {
		if ( s->size.w != sts->size.w || s->size.h != sts->size.h )
			info->ResizeFrameTo(s->size.w, s->size.h, true);
	}
	if ( flags & VSF_POSITION ) {
		if ( s->position.x != sts->position.x || s->position.y != sts->position.y )
			info->PlaceInSuperImageAt(s->position.x, s->position.y, true);
	}
	if ( flags & VSF_ENABLED ) {
		if ( s->enabled )
			info->Enable();
		else
			info->Disable();
	}
	if ( flags & VSF_VISIBLE ) {
		if ( s->visible )
			info->Show();
		else
			info->Hide();
	}
	return err;
}

struct VTabGroupList {
	LWindow*		window;
	LTabGroup*		group;
	VTabGroupList*	next;
};

LTabGroup*
GetTabGroupForWindow(LView* view)
{
int key;
const int hash_size = 23;
static VTabGroupList** hash = 0;
	if ( hash == 0 ) {	// initialize
		hash = new VTabGroupList*[hash_size];
		for ( key = 0 ; key < hash_size ; key++ ) {
			hash[key] = 0;
		}
	}
	
	LWindow *win;
	while ( view && (win = dynamic_cast<LWindow*>(view)) == 0 )
		view = view->GetSuperView();
	
	key = (unsigned(win)) % hash_size;
	VTabGroupList *list = hash[key];
	for ( ; list ; list = list->next )
		if ( list->window == win )
			return list->group;
	
	list = new VTabGroupList;
	list->window = win;
	list->group = new LTabGroup(win);
	list->next = hash[key];
	hash[key] = list;
	return list->group;
}

} // extern "C"