#include <../source/PAL/glctx.h>
#include <../source/TAL/objbase.h>
#include <../source/PAL/dataman.h>
#include <../source/TAL/fonthead.h>

void	V_GLOBAL
vglDestroyFont(vhandle_t handle)
{
	libvCloseHandle(handle,"vglDestroyFont",fh_object,FH_MAGIC);
}

void	V_GLOBAL
vglSetFontAdjustment(vhandle_t handle,gl_font_adjustment_t adjustment)
{
	fonthead_p font=(fonthead_p)libvIsHandle(handle,
    	"vglSetFontAdjustment",fh_object,FH_MAGIC);
	if(!font)
    	return;
	font->sp.adjustment=adjustment;
}

/*
 *	custom_adjustment contain coordinates for text pivot point;
 *	{0,0} mean "left,bottom", {1,1} mean "right,top".
 */
void	V_GLOBAL
vglSetFontCustomAdjustment(vhandle_t handle,vec3_t custom_adjustment)
{
	fonthead_p font=(fonthead_p)libvIsHandle(handle,
    	"vglSetFontCustomAdjustment",fh_object,FH_MAGIC);
	if(!font)
    	return;
	font->sp.adjustment=fa_custom;
	vec3Copy(font->sp.custom_adjustment,custom_adjustment);
}

/*
 *	vglSetFontScale()
 */
void	V_GLOBAL
vglSetFontScale(vhandle_t handle,vec_t scale)
{
	fonthead_p font=(fonthead_p)libvIsHandle(handle,
    	"vglSetFontScale",fh_object,FH_MAGIC);
	if(!font)
    	return;
	font->sp.use_fov=false;
	font->sp.scale=scale;
}

/*
 *	vglSetFontFOV
 */
void	V_GLOBAL
vglSetFontFOV(vhandle_t handle,vec_t fov)
{
	fonthead_p font=(fonthead_p)libvIsHandle(handle,
    	"vglSetFontFOV",fh_object,FH_MAGIC);
	if(!font)
    	return;
	font->sp.use_fov=true;
	font->sp.fov=fov;
}

vboolean	V_GLOBAL
vglSelectFont(vhandle_t handle)
{
    gl_context_t *ctx;
	fonthead_p font;
    ctx=(gl_context_t*)GetThreadData()->gl_context;
    if(!ctx)
    	return false;
	/* Decrease reference count / destroy currently selected font */
	if(ctx->font)
		vglDestroyFont(ctx->font);
	ctx->font=NULL;
	/* If passed non-NULL, try to select new font */
	if(handle)
	{
	    font=(fonthead_p)libvIsHandle(handle,
    		"vglSelectFont",fh_object,FH_MAGIC);
		if(!font)
    		return false;
	   	ctx->font=font;
   		font->base.ref_count++;
   	}
    return true;
}

/*
 *	Returns font selected in current context.
 *	Returns NULL if:
 *		- no OpenGL context;
 *		- no font in context;
 *		- no drawing procedure in font.
 */
vhandle_t	V_GLOBAL
vglGetCurrentFont(void)
{
    gl_context_t *ctx;
    fonthead_t *font;
    ctx=(gl_context_t*)GetThreadData()->gl_context;
    if(!ctx || !ctx->font)
    	return NULL;
	font=(fonthead_t*)ctx->font;
    if(!font->draw_char)
    	return NULL;
	return font;
}

/*
 *	Returns text x-extent and height from the base line.
 */
void	V_GLOBAL
vglGetTextExtents(char *str,vec_t *width,vec_t *height)
{
	vec_t x, dx;
	fonthead_p font=(fonthead_p)vglGetCurrentFont();
	if(str && font)
	{
		if(height)
    		*height=font->height;
		if(width)
    	{
			x=0;
    		for(;*str;str++)
		    {
    			font->get_char_extents(font,*str,&dx,NULL);
        		x+=dx;
		    }
    	    *width=x;
		}
		return;
	}
	/* Just pass some non-0 parameters ... */
	if(!str)
    {
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"vglGetTextExtents(NULL,%X,%X): "
			"invalid parameter.\n",width,height);
#endif	/* CHECKED_BUILD */
	}
    if(!font)
    {
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"vglGetTextExtents(%s,%X,%X): "
			"no font in context.\n",dbs(str),width,height);
#endif	/* CHECKED_BUILD */
    }
	/* Passing nonzero parameters can avoid zero-divide */
	if(height)
		*height=(vec_t)8;
	if(width)
		*width=(vec_t)8;
   	return;
}

void    V_GLOBAL
vglPuts(char *str)
{
    vec_t cw, dx, sw, ts;
    u32_t ch;
    unsigned char *ptr;
    fonthead_p font;
	if(!str)
    {
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"vglPuts(NULL): invalid parameter.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
    font=(fonthead_p)vglGetCurrentFont();
    if(!font)
    {
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"vglPuts(%s): "
			"no font in context.\n",dbs(str));
#endif	/* CHECKED_BUILD */
		return;
    }
    font->get_char_extents(font,32,&sw,NULL);
    dx=0;       /* accumulate increment to avoid GL stack pushes/pops */
    ts=sw*4;    /* TODO: implement real tabs */
    for(ptr=(unsigned char*)str;*ptr;ptr++)
    {
		ch=*ptr;
        switch(ch)
        {
            case '\t':	/* Tab */
                glTranslatef(ts,0,0);
                dx+=ts;
                break;
            case 32:	/* Space */
                glTranslatef(sw,0,0);
                dx+=sw;
                break;
            case 0x0D:
                break;
            case 0x0A:	/* CR + LF */
                glTranslatef(-dx,-font->height,0);
                dx=0;
                break;
            default:
                font->draw_char(font,ch,&cw);
                dx+=cw;
        }
    }
    return;
}

void    V_GLOBAL
vglPutsKoi8r(char *str)
{
	/* TODO: convert to use inputLocaleChar() */
    static unsigned char koi8abc[]="JCUKENG[]ZH_FYWAPROLDV\\"
        "Q^SMITXB@#jcukeng{}zhfywaproldv|q~smitxb`3";
    u8_t *ptr,*koi;
	if(!str)
    {
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"vglPutsKoi8r(NULL): invalid parameter.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
    koi=(u8_t*)strDup(str);
    for(ptr=koi;*ptr;ptr++)
    {
        if(strchr((const char*)koi8abc,*ptr))
            *ptr+=128;
    }
    vglPuts((char*)koi);
    memFree(koi);
}

/*
 *	OpenGL "printf()".
 */
void	V_GLOBAL_VA
vglPrintf(const char *fmt,...)
{
	va_list marker;
	char 	*ptr;
	char 	fmt_buffer[MAX_FORMAT_BUFFER];
	if(fmt)
	{
		ptr=fmt_buffer;
		va_start(marker,fmt);
		vsprintf(ptr,fmt,marker);
		va_end(marker);
		strStripTrail(ptr);
        vglPuts(ptr);
	}
#ifdef	CHECKED_BUILD
	else
		conlPuts(LOG_ERROR,
			"vglPrintf([NULL]): invalid parameter.\n");
#endif	/* CHECKED_BUILD */
}

/*
 *	There are 9 useful pivot point positions for text:
 *			left+top;			center+top;				right+top;
 *			left+center;		center+center;			right+center;
 *			left+bottom;		center+bottom;			right+bottom.
 */
V_LOCAL	vec3_t	label_origins[9]=
{
	{0,-1,0},	{-0.5,-1,0},	{-1,-1,0},
	{0,-0.5,0},	{-0.5,-0.5,0},	{-1,-0.5,0},
	{0,0,0},	{-0.5,0,0},		{-1,0,0}
};

/*
 *	Sets LOD based on scale and view size (font height is about 1.0).
 *	Used in vglDrawString() / vglDrawText().
 */
#define	GL_FONT_LOD_LEVELS 3
V_LOCAL	void
vglSetFontLODFromScale(vec_t scale)
{
	int i;
	V_LOCAL	vec_t lod_limits[GL_FONT_LOD_LEVELS]={0.05f,0.02f,0};
	scale=v_abs(scale);
	for(i=0;i<GL_FONT_LOD_LEVELS;i++)
		if(scale>lod_limits[i])
			break;
	switch(i)
    {
    	case 0:
        	glLineWidth(3.0f);
            break;
        case 1:
        	glLineWidth(2.4f);
            break;
        default:
        	glLineWidth(1.3f);
            break;
	}
}

/*
 *	Loads appropriate matrix on modelview TOS.
 *	Assumes all args are valid.
 */
void	V_LOCAL
vglSetupStringTransform(
	fonthead_p font,
	vec3_t origin,
	cam_t *cam,
	char *text)
{
	vec3_t t,v;
	vec_t w,h,scale,lod_scale,t_len;
	glLoadIdentity();
	mat3x4VecMul(t,cam->transform,origin);
	t_len=vec3Abs(t);
	if(font->sp.use_fov || IsZero(t_len))
	{
		lod_scale=(vec_t)tan(font->sp.fov * .5);
		scale = lod_scale * t_len;
	}
	else
	{
		scale=font->sp.scale;
		lod_scale = scale / t_len;
	}
	vglGetTextExtents(text,&w,&h);
	if((unsigned)(font->sp.adjustment) > (unsigned)fa_right_bottom)
		vec3Negate(v,font->sp.custom_adjustment);
	else
		vec3Copy(v,label_origins[font->sp.adjustment]);
	v[0]*=w;
	v[1]*=h;
	v[2]=0;
	vec3Scale(v,v,scale);
	vglSetFontLODFromScale(lod_scale);
	vec3Add(t,t,v);
	glTranslatef(t[0],t[1],t[2]);
	glScalef(scale,scale,scale);
}

/*
 *	vglDrawString()
 */
void	V_GLOBAL
vglDrawString(
	vec3_t origin,
	cam_t *cam,
	char *text)
{
	fonthead_p font=(fonthead_p)vglGetCurrentFont();
	if(!font)
	{
		/* this place can produce a lot of errors, so we set log level
		 * to LOG_TRACE only */
#ifdef	CHECKED_BUILD
		conlPuts(LOG_TRACE,"vglDrawString(): no current font.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	glPushMatrix();
	vglSetupStringTransform(font,origin,cam,text);
	vglPuts(text);
	glPopMatrix();
}
