/****************************************************************************************/
/*  FONT3D.C                                                                            */
/*                                                                                      */
/*  Author: Jason Wood                                                                  */
/*  Description:                                                                        */
/*                                                                                      */
/*  The contents of this file are subject to the Jet3D Public License                   */
/*  Version 1.02 (the "License"); you may not use this file except in                   */
/*  compliance with the License. You may obtain a copy of the License at                */
/*  http://www.jet3d.com                                                                */
/*                                                                                      */
/*  Software distributed under the License is distributed on an "AS IS"                 */
/*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See                */
/*  the License for the specific language governing rights and limitations              */
/*  under the License.                                                                  */
/*                                                                                      */
/*  The Original Code is Jet3D, released December 12, 1999.                             */
/*  Copyright (C) 1996-1999 Eclipse Entertainment, L.L.C. All Rights Reserved           */
/*                                                                                      */
/****************************************************************************************/

#define	WIN32_LEAN_AND_MEAN
#pragma warning(disable : 4201 4214 4115)
#include <windows.h>
#include <windowsx.h>
#pragma warning(default : 4201 4214 4115)

#include <stdio.h>
#include <string.h>
#include <assert.h>

#include "basetype.h"
#include "ram.h"
#include "log.h"
#include "actor.h"
#include "glyph3d.h"

#include "xform3d.h"
#include "font3d.h"

///////////////////////////////////////////////////////////////////////////////////////


typedef struct jeFont3d
{
	jeGlyph3d** apGlyphs;
	uint32 nGlyphs;
	uint32 maxNGlyphs;

	jeGlyph3d_Beveler* pBeveler;

	char sFontName[256];

}jeFont3d;

///////////////////////////////////////////////////////////////////////////////////////
//

// TODO: might want to sort glyphs by ascii/unicode value

static jeGlyph3d* jeFont3d_HasGlyph(jeFont3d* pFont, uint32 val)
{
	uint32 i;

	assert(pFont);

	for (i = 0; i < pFont->nGlyphs; i ++)
	{
		if (jeGlyph3d_GetVal(pFont->apGlyphs[i]) == val) 
			return pFont->apGlyphs[i];
	}

	return NULL;
}

///////////////////////////////////////////////////////////////////////////////////////
// ctor / dtor

jeFont3d* jeFont3d_Create(const char* psFontName)
{
	jeFont3d* pFont;

	pFont = jeRam_Allocate(sizeof(jeFont3d));
	if (! pFont)
	{
		return NULL;
	}

	strcpy(pFont->sFontName, psFontName);
	
	pFont->nGlyphs = 0;
	pFont->maxNGlyphs = 32;
	pFont->apGlyphs = (jeGlyph3d**)jeRam_Allocate(pFont->maxNGlyphs * sizeof(jeGlyph3d*));
	if (pFont->apGlyphs == NULL)
	{
		jeRam_Free(pFont);
		return NULL;
	}

	pFont->pBeveler = NULL;

	return pFont;
}

void jeFont3d_Destroy(jeFont3d** ppFont)
{
	uint32 i;

	assert(ppFont);
	assert(*ppFont);

	for (i = 0; i < (*ppFont)->nGlyphs; i ++)
	{
		assert((*ppFont)->apGlyphs[i]);
		jeGlyph3d_Destroy(&(*ppFont)->apGlyphs[i]);
	}

	jeRam_Free((*ppFont)->apGlyphs);
	(*ppFont)->apGlyphs = NULL;

	*ppFont = NULL;
}

///////////////////////////////////////////////////////////////////////////////////////
// fns

// add glyphs in range [rangeMin..rangeMax] inclusive
jeBoolean jeFont3d_AddGlyphs(jeFont3d* pFont, uint32 rangeMin, uint32 rangeMax,
	int nCurvePoints)
{
	uint32 i;

	assert(pFont);
	assert(rangeMin <= rangeMax);

	for (i = rangeMin; i <= rangeMax; i ++)
	{
		if (jeFont3d_HasGlyph(pFont, i) != NULL)
		{
			continue; // glyph already in font
		}

		if (pFont->nGlyphs == pFont->maxNGlyphs) // grow array
		{
			pFont->maxNGlyphs *= 2;
			pFont->apGlyphs = (jeGlyph3d**)jeRam_Realloc(pFont->apGlyphs,
				pFont->maxNGlyphs * sizeof(jeGlyph3d*));
			if (pFont->apGlyphs == NULL)
			{
				return JE_FALSE;
			}
		}

		pFont->apGlyphs[pFont->nGlyphs] = jeGlyph3d_Create(pFont->sFontName, i, 
			nCurvePoints, pFont->pBeveler);

		if (pFont->apGlyphs[pFont->nGlyphs] == NULL)
		{
			Log_Printf("jeFont3d_AddGlyphs(): jeGlyph3d_Create() failed!\n");
			return JE_FALSE;
		}

		pFont->nGlyphs ++;
	}

	return JE_TRUE;
}

extern JETAPI jeActor *JETCC jeActor_Create(void);

jeActor* jeFont3d_GenActorString(jeFont3d* pFont, const uint32* pString, int len,
	int nCurvePoints, jeBoolean smoothOutline, jeBoolean optimizeMesh)
{
	int i;
	jeGlyph3d* pGlyph;
	jeActor* pActor;
	jeBody* pBody;
	jeActor_Def* pDef;
	int nBevels;
	char sBoneName[64], sMaterialName[64];
	int materialIndex, parentBoneIndex, boneIndex;
	jeXForm3d xform;
	jeGlyph3d_MetricsInfo mi;

	/////////////////////////////////////////////////////////////////////////////////

	assert(pFont);
	assert(pString);

	jeXForm3d_SetIdentity(&xform);

	// init the actor
	pDef = jeActor_DefCreate();
	if (pDef == NULL)
	{
		Log_Printf("jeFont3d_GenActorString(): jeActor_DefCreate() failed!\n");
		return NULL;
	}

	// init the actor's body	
	pBody = jeBody_Create();
	if (pBody == NULL)
	{
		Log_Printf("jeFont3d_GenActorString(): jeBody_Create() failed!\n");
		return NULL;
	}

	if (! optimizeMesh)
	{
		jeBody_SetOptimizeFlags(pBody, JE_FALSE);
	}

	// setup materials
	nBevels = jeGlyph3d_Beveler_GetNBevels(pFont->pBeveler);
	for (i = 0; i < nBevels; i ++)
	{
		sprintf(sMaterialName, "bevelmat_%02d", i);

		if (! jeBody_AddMaterial(pBody, sMaterialName, NULL, 255.f, 255.f, 255.f, 
			NULL, &materialIndex))
		{
			Log_Printf("jeFont3d_GenActorString(): jeBody_AddMaterial() failed!\n");
			jeBody_Destroy(&pBody);
			jeActor_DefDestroy(&pDef);
			return NULL;
		}
	}

	// add top and bottom materials
	sprintf(sMaterialName, "topmat");
	if (! jeBody_AddMaterial(pBody, sMaterialName, NULL, 255.f, 255.f, 255.f, 
		NULL, &materialIndex))
	{
		Log_Printf("jeFont3d_GenActorString(): jeBody_AddMaterial() failed!\n");
		jeBody_Destroy(&pBody);
		jeActor_DefDestroy(&pDef);
		return NULL;
	}
	sprintf(sMaterialName, "bottommat");
	if (! jeBody_AddMaterial(pBody, sMaterialName, NULL, 255.f, 255.f, 255.f, 
		NULL, &materialIndex))
	{
		Log_Printf("jeFont3d_GenActorString(): jeBody_AddMaterial() failed!\n");
		jeBody_Destroy(&pBody);
		jeActor_DefDestroy(&pDef);
		return NULL;
	}

	// scan thru the input string; if the glyph whose value == string[i]
	// isn't in the font's cache, we need to create the glyph. The _GenBone()
	// function will tessellate the glyph if this is the case, else we
	// just use the tessellation that's already stored in the glyph

	for (i = 0, parentBoneIndex = JE_BODY_ROOT; i < len; i ++)
	{
		if (! jeFont3d_AddGlyphs(pFont, pString[i], pString[i], 
			nCurvePoints))
		{
			Log_Printf("jeFont3d_GenActorString(): jeFont3d_AddGlyphs() failed!\n");
			jeBody_Destroy(&pBody);
			jeActor_DefDestroy(&pDef);
			return NULL;
		}

		sprintf(sBoneName, "Bone%02d", i);

		pGlyph = jeFont3d_HasGlyph(pFont, pString[i]);
		assert(pGlyph);

		if (! jeGlyph3d_GenBone(pGlyph, pBody, parentBoneIndex, &boneIndex,
			sBoneName, smoothOutline, &xform))
		{
			Log_Printf("jeFont3d_GenActorString(): jeGlyph3d_GenBone() failed!\n");
			jeBody_Destroy(&pBody);
			jeActor_DefDestroy(&pDef);
			return NULL;
		}

		jeGlyph3d_GetMetricsInfo(pGlyph, &mi);
		jeVec3d_Set(&xform.Translation, mi.xInc, mi.yInc, 0.f);

		parentBoneIndex = boneIndex;
	}

	if (! jeActor_SetBody(pDef, pBody))
	{
		Log_Printf("jeFont3d_GenActorString(): jeActor_SetBody() failed!\n");
		jeBody_Destroy(&pBody);
		jeActor_DefDestroy(&pDef);
		return NULL;
	}

	pActor = NULL;
	pActor = jeActor_Create();
	jeActor_SetActorDef(pActor,pDef);
	if (pActor == NULL)
	{
		Log_Printf("jeFont3d_GenActorString(): jeActor_Create() failed!\n");
		jeBody_Destroy(&pBody);
		jeActor_DefDestroy(&pDef);
		return NULL;
	}

	return pActor;
}

// set the beveler used to bevel glyphs; call this before adding
// glyphs and/or calling _GenActorStringA()

void jeFont3d_SetBeveler(jeFont3d* pFont, const jeGlyph3d_Beveler* pBeveler)
{
	assert(pFont);
	//assert(pBeveler);

	(const jeGlyph3d_Beveler*)pFont->pBeveler = pBeveler;
}
