/*
**      $VER: SampleFuncs.c 37.12 (29.6.97)
**
**      Demo functions for example.library
**
**      (C) Copyright 1996-97 Andreas R. Kleinert
**      All Rights Reserved.
*/

#define __USE_SYSBASE        // perhaps only recognized by SAS/C

#include <exec/types.h>
#include <exec/memory.h>

#ifdef __MAXON__
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/graphics_protos.h>
#include <clib/utility_protos.h>
#include <clib/intuition_protos.h>
/*
#include <pragma/exec_lib.h>
#include <pragma/dos_lib.h>
#include <pragma/graphics_lib.h>
#include <pragma/utility_lib.h>
*/
#else
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/utility.h>
#endif

#include <diskfont/diskfont.h>
#include <diskfont/glyph.h>
#include <diskfont/diskfonttag.h>
#include <diskfont/oterrors.h>
#include <graphics/gfx.h>
#include <dos/dos.h>

#pragma header

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

#include "compiler.h"
#include "myglyphengine.h"
#include "myglyphrequest.h"
#include "freetype.h"
#include "libfuncs.h"
#include "freedom.h"

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

#define FIXED_TO_FIXED26_DIV (65536/64)
#define FIXED26_TO_FIXED(x) ((x) << (16 - 6))
#define FIXED_TO_FIXED26(x) ((x) >> (16 - 6))

#define FIXED_TO_APPROX(x) (((x) + 0x8000) >> 16)

#define FLOOR(x) ((x) & -64)

#define CEILING(x) (((x) + 63) & -64)

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

extern struct TrueTypeBase *TrueTypeBase;

static UBYTE emptybitmap[4] = {0,0,0,0};

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

BOOL Config_UseHintedAdvance=TRUE;
BOOL Config_NoPointFraction=FALSE;

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

static void ClearGlyphRequest(struct MyGlyphEngine *glyphEngine)
{
	memset(&glyphEngine->gle_GlyphRequest,0,sizeof(MyGlyphReq));
	
	glyphEngine->gle_GlyphRequest.gr_DeviceDPI_X = 72;
	glyphEngine->gle_GlyphRequest.gr_DeviceDPI_Y = 72;
	glyphEngine->gle_GlyphRequest.gr_PointHeight = 10;
	glyphEngine->gle_GlyphRequest.gr_GlyphWidth = 0;
	glyphEngine->gle_GlyphRequest.gr_SetFactor = 0x10000; /* 1.0 */
}

static ULONG GetName(TT_UShort id,struct FD_FontQuery *query,char *buffer)
{
	TT_String *s;
	TT_UShort platform,encoding,language,nameID,i,i2;
	
	for (i = 0;i < query->face_properties.num_Names;i++)
	{
		if (TT_Get_Name_ID(query->face,
								 i,
								 &platform,
								 &encoding,
								 &language,
								 &nameID) == 0)
		{
			if (nameID == id)
			{
				if (TT_Get_Name_String(query->face,
											  i,
											  &s,
											  &i2) == 0)
				{
					memcpy(buffer,s,i2);
					buffer[i2] = '\0';	/* or are the name strings already 0 terminated :-? */
					
					return 0;
				}								
			}
		}

	} /* for (i = 0;i < query->face_properties.num_Names;i++) */
	
	return FDERR_INFO_NOT_FOUND;
}

static void ShowMessage(char *body)
{
	struct EasyStruct es = {sizeof(struct EasyStruct),0,"TrueType Library","","OK"};
	
	es.es_TextFormat = body;
	
	EasyRequestArgs(0,&es,0,0);
}

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

struct MyGlyphEngine * __saveds ASM Lib_OpenEngine(void)
{
	struct MyGlyphEngine *rc;
	
	if ((rc=AllocMem(sizeof(struct MyGlyphEngine),MEMF_PUBLIC|MEMF_CLEAR)))
	{

		if (TT_Init_FreeType(&rc->gle_Engine) != 0)
		{
			Lib_CloseEngine(rc);
			rc=0;
		} else {
			ClearGlyphRequest(rc);
			rc->gle_Library=(struct Library *)TrueTypeBase;
			rc->gle_Name="freedom";
			
		} /* 		if (TT_Init_FreeType(&rc->gle_Engine) != 0) else ... */

	} /* if ((rc=AllocMem(sizeof(struct MyGlyphEngine),MEMF_PUBLIC|MEMF_CLEAR))) */
	
	return rc;
}

void __saveds ASM Lib_CloseEngine(register __a0 struct MyGlyphEngine *glyphEngine GNUCREG(a0))
{
	if (glyphEngine)
	{
		if (glyphEngine->gle_Glyph.z)
		{
			TT_Done_Glyph(glyphEngine->gle_Glyph);
		}

		if (glyphEngine->gle_Instance.z)
		{
			TT_Done_Instance(glyphEngine->gle_Instance);
		}

		if (glyphEngine->gle_Face.z)
		{
			TT_Close_Face(glyphEngine->gle_Face);
		}
			
		if (glyphEngine->gle_Engine.z)
		{
			TT_Done_FreeType(glyphEngine->gle_Engine);
		}

		FreeMem(glyphEngine,sizeof(struct MyGlyphEngine));
	}
}

ULONG __saveds ASM Lib_SetInfoA(register __a0 struct MyGlyphEngine *glyphEngine GNUCREG(a0),register __a1 struct TagItem *taglist GNUCREG(a1))
{
	struct TagItem *tstate,*ti;
	LONG	data;
	UWORD	platform,encoding;
	WORD	i;
	char	s[300];

	ULONG rc=OTERR_Success;

	tstate=taglist;

	while ((ti=NextTagItem(&tstate)))
	{
		data=ti->ti_Data;

		switch(ti->ti_Tag)
		{
			case OT_DeviceDPI:
				glyphEngine->gle_GlyphRequest.gr_DeviceDPI_X = data >> 16;
				glyphEngine->gle_GlyphRequest.gr_DeviceDPI_Y = data & 0xFFFF;
//				sprintf(s,"DPI is %ld %ld",glyphEngine->gle_GlyphRequest.gr_DeviceDPI_X,
//													glyphEngine->gle_GlyphRequest.gr_DeviceDPI_Y);
//				ShowMessage(s);
				break;

			case OT_GlyphCode:
				glyphEngine->gle_GlyphRequest.gr_GlyphCode = data;
				break;
				
			case OT_GlyphCode2:
				glyphEngine->gle_GlyphRequest.gr_GlyphCode2 = data;
				break;
				
			case OT_GlyphWidth:
				glyphEngine->gle_GlyphRequest.gr_GlyphWidth = data;
				break;
				
			case OT_OTagPath:
				strcpy(glyphEngine->gle_OTagName,(char *)data);
				break;
				
			case OT_OTagList:
				ClearGlyphRequest(glyphEngine);
				Lib_SetInfoA(glyphEngine,(struct TagItem *)data);
				
				/* kill old font */
				
				if (glyphEngine->gle_Glyph.z)
				{
					TT_Done_Glyph(glyphEngine->gle_Glyph);
					glyphEngine->gle_Glyph.z=0;
				}
				
				if (glyphEngine->gle_Instance.z)
				{
					TT_Done_Instance(glyphEngine->gle_Instance);
					glyphEngine->gle_Instance.z=0;
				}
				
				if (glyphEngine->gle_Face.z)
				{
					TT_Close_Face(glyphEngine->gle_Face);
					glyphEngine->gle_Face.z=0;
				}
				
				glyphEngine->gle_FontOK = FALSE;

				/* try creating new font */
				
				strcpy(s,"fonts:_truetype");
				AddPart(s,glyphEngine->gle_FontName,299);
				
				if (TT_Open_Face(glyphEngine->gle_Engine,s,&glyphEngine->gle_Face) != 0)
				{
					rc=OTERR_NoFace;
					ShowMessage("OpenFace failed!");
					break;
				}
				
				TT_Get_Face_Properties(glyphEngine->gle_Face,&glyphEngine->gle_Face_Properties);
				
				if (TT_New_Instance(glyphEngine->gle_Face,&glyphEngine->gle_Instance) != 0)
				{
					TT_Close_Face(glyphEngine->gle_Face);
					glyphEngine->gle_Face.z=0;

					rc=OTERR_NoMemory;
					ShowMessage("NewInstance failed!");
					break;
				}
				
				if (TT_New_Glyph(glyphEngine->gle_Face,&glyphEngine->gle_Glyph) != 0)
				{
					TT_Close_Face(glyphEngine->gle_Face);
					glyphEngine->gle_Face.z=0;

/*					TT_Done_Instance(glyphEngine->gle_Instance);
					glyphEngine->gle_Instance.z=0;*/ /* killed width TT_Close_Face !? */
					
					rc=OTERR_NoMemory;
					ShowMessage("NewGlyph failed!");
					break;
				}
				
				data=glyphEngine->gle_Face_Properties.num_CharMaps;
				
				for (i = 0; i < data; i++ )
				{
					TT_Get_CharMap_ID(glyphEngine->gle_Face,i,&platform,&encoding);
					if ( (platform == 3 && encoding == 1 )  ||
						(platform == 0 && encoding == 0 ) )
					{
						TT_Get_CharMap(glyphEngine->gle_Face,i,&glyphEngine->gle_CharMap);
						break;
					}
				}

				if (i == data)
				{
					/* unicode charmap not found */
					TT_Close_Face(glyphEngine->gle_Face);
					glyphEngine->gle_Face.z=0;

/*					TT_Done_Instance(glyphEngine->gle_Instance);
					glyphEngine->gle_Instance.z=0;*/ /* killed with TT_Close_Face !? */
					
					TT_Done_Glyph(glyphEngine->gle_Glyph);
					glyphEngine->gle_Glyph.z=0;
					
					rc=OTERR_BadFace;

					ShowMessage("Unicode charmap not found!");
					break;
										
				}
				
//				ShowMessage("Font loaded successfully :)");
				glyphEngine->gle_FontOK = TRUE;
				
				break;

			case OT_PointHeight:
				glyphEngine->gle_GlyphRequest.gr_PointHeight = data;
//				sprintf(s,"Pointheight %X",data);
//				ShowMessage(s);
				break;
				
			case OT_PointSize:
				glyphEngine->gle_GlyphRequest.gr_PointHeight = data*16;
//				sprintf(s,"Pointsize %X",data);
//				ShowMessage(s);
				break;
				
			case OT_SetFactor:
				glyphEngine->gle_GlyphRequest.gr_SetFactor = data;
//				sprintf(s,"SetFactor %X",data);
//				ShowMessage(s);
				break;
			
			case (OT_Spec1+1) | OT_Indirect:
				strcpy(glyphEngine->gle_FontName,(char *)data);
				break;
				
			case OT_StrikeThrough:
				rc = OTERR_UnknownTag;
				break;
				
			case OT_UnderLined:
				rc = OTERR_UnknownTag;
				break;
			
		} /* switch(ti->ti_Tag) */

	} /* while ((ti=NextTagItem(&tstate))) */

	return rc;
}

ULONG __saveds ASM Lib_ObtainInfoA(register __a0 struct MyGlyphEngine *glyphEngine GNUCREG(a0),register __a1 struct TagItem *taglist GNUCREG(a1))
{
	struct TagItem *tstate,*ti;
	struct GlyphMap *gm;
	LONG	data,advance;
	WORD	xmin,xmax,ymin,ymax,width,height,width32,glyph;
	char s[100];

	ULONG rc=OTERR_Success;

	tstate=taglist;

	while ((ti=NextTagItem(&tstate)))
	{
		data=ti->ti_Data;

		switch(ti->ti_Tag)
		{
			case OT_GlyphMap:
				if (!data)
				{
					rc = OTERR_BadTag;
					break;
				}
				
				if (!glyphEngine->gle_FontOK)
				{
					rc = OTERR_NoFace;
					break;
				}
				
				if (TT_Set_Instance_Resolutions(glyphEngine->gle_Instance,
														  glyphEngine->gle_GlyphRequest.gr_DeviceDPI_X,
														  glyphEngine->gle_GlyphRequest.gr_DeviceDPI_Y) != 0)
				{
					ShowMessage("SetInstanceResolutions failed!");
					rc = OTERR_Failure;
					break;
				}
				
				if (Config_NoPointFraction)
				{
					advance = 0xFFFF0000;
				} else {
					advance = 0xFFFFFFFF;
				}

				if (TT_Set_Instance_CharSizes(glyphEngine->gle_Instance,
														FIXED_TO_FIXED26(TT_MulDiv(glyphEngine->gle_GlyphRequest.gr_PointHeight,
																							glyphEngine->gle_GlyphRequest.gr_SetFactor,
																							0x10000) & advance),
														FIXED_TO_FIXED26(glyphEngine->gle_GlyphRequest.gr_PointHeight & advance)
													  ) != 0)
				{
					ShowMessage("SetInstanceCharSizes failed!");
					rc = OTERR_Failure;
					break;
				}

				glyph = TT_Char_Index(glyphEngine->gle_CharMap,glyphEngine->gle_GlyphRequest.gr_GlyphCode);

				if (TT_Load_Glyph(glyphEngine->gle_Instance,
										glyphEngine->gle_Glyph,
										glyph,
										TTLOAD_DEFAULT) != 0)
				{
					ShowMessage("LoadGlyph failed!");
					rc = OTERR_Failure;
					break;
				}						
				
				TT_Get_Glyph_Metrics(glyphEngine->gle_Glyph,&glyphEngine->gle_Glyph_Metrics);
				TT_Get_Instance_Metrics(glyphEngine->gle_Instance,&glyphEngine->gle_Instance_Metrics);

				if (Config_UseHintedAdvance)
				{
					advance = TT_MulDiv(glyphEngine->gle_Glyph_Metrics.advance,
											  0x10000,
											  glyphEngine->gle_Instance_Metrics.x_scale);
				} else {
				
					TT_Get_Face_Metrics(glyphEngine->gle_Face,
											  glyph,
											  glyph,
											  0,
											  (TT_UShort *)&width,
											  0,
											  0);
					advance = width;
				}

//				sprintf(s,"Advance: %ld  ppem %ld %ld",advance,glyphEngine->gle_Glyph_Metrics.advance,glyphEngine->gle_Instance_Metrics.x_scale);
//				ShowMessage(s);
				
				advance = TT_MulDiv(advance,
										  0x10000,
										  glyphEngine->gle_Face_Properties.header->Units_Per_EM);
				

				
				xmin=FLOOR(glyphEngine->gle_Glyph_Metrics.bbox.xMin);
				ymin=FLOOR(glyphEngine->gle_Glyph_Metrics.bbox.yMin);
				xmax=CEILING(glyphEngine->gle_Glyph_Metrics.bbox.xMax);
				ymax=CEILING(glyphEngine->gle_Glyph_Metrics.bbox.yMax);
				
				width=(xmax-xmin)/64;
				width32=(width+31) & -32;
				height=(ymax-ymin)/64;

				if (!(gm=AllocVec(sizeof(struct GlyphMap),MEMF_PUBLIC|MEMF_CLEAR)))
				{
					rc = OTERR_NoMemory;
					break;
				}
				
				if (width>0 && height>0)
				{
					if (!(gm->glm_BitMap=AllocVec(height * (width32 / 8),MEMF_PUBLIC|MEMF_CLEAR)))
					{
						sprintf(s,"Bitmap alloc (%ld x %ld) failed: ascii = %ld (%ld)",width,height,glyphEngine->gle_GlyphRequest.gr_GlyphCode,TT_Char_Index(glyphEngine->gle_CharMap,glyphEngine->gle_GlyphRequest.gr_GlyphCode));
						ShowMessage(s);
						FreeVec(gm);
						rc = OTERR_NoMemory;
						break;
					}
				
					glyphEngine->gle_Raster_Map.rows = height;
					glyphEngine->gle_Raster_Map.cols = width32 / 8;
					glyphEngine->gle_Raster_Map.width = width;
					glyphEngine->gle_Raster_Map.flow = TT_Flow_Down;
					glyphEngine->gle_Raster_Map.bitmap = gm->glm_BitMap;
					
					if (TT_Get_Glyph_Bitmap(glyphEngine->gle_Glyph,
													&glyphEngine->gle_Raster_Map,
													-xmin,
													-ymin) != 0)
					{
						ShowMessage("GetGlyphBitMap failed!");
						FreeVec(gm->glm_BitMap);
						FreeVec(gm);
						rc = OTERR_Failure;
						break;
					}

					gm->glm_BMModulo = width32 / 8;
					gm->glm_BMRows = height;
					gm->glm_BlackLeft = 0;
					gm->glm_BlackTop = 0;
					gm->glm_BlackWidth = width;
					gm->glm_BlackHeight = height;

				} /* if (width>0 && height>0) */ else
				{
					width = 1;
					height = 1;

					gm->glm_BitMap = emptybitmap;
				
					gm->glm_BMModulo = 4;
					gm->glm_BMRows = height;
					
					gm->glm_BlackLeft = 0;
					gm->glm_BlackTop = 0;
					gm->glm_BlackWidth = width;
					gm->glm_BlackHeight = height;
				}
				
				gm->glm_XOrigin = FIXED26_TO_FIXED(-glyphEngine->gle_Glyph_Metrics.bearingX);
				gm->glm_YOrigin = FIXED26_TO_FIXED(glyphEngine->gle_Glyph_Metrics.bearingY);
				
				gm->glm_X0 = FIXED_TO_APPROX(gm->glm_XOrigin);
				gm->glm_Y0 = FIXED_TO_APPROX(gm->glm_YOrigin);
				gm->glm_X1 = FIXED_TO_APPROX(gm->glm_XOrigin + FIXED26_TO_FIXED(glyphEngine->gle_Glyph_Metrics.advance));
				gm->glm_Y1 = gm->glm_Y0 + height - 1;

				gm->glm_Width = advance;
				
				*(struct GlyphMap **)data = gm;
				break;
				
			case OT_TextKernPair:
//				ShowMessage("Was asked for OT_TextKernPair ...");
				if (data)
				{
					*(FIXED *)data = 0;
				} else {
					rc = OTERR_BadTag;
				}
				break;
				
			case OT_DesignKernPair:
//				ShowMessage("Was asked for OT_DesignKernPair ...");
				if (data)
				{
					*(FIXED *)data = 0;
				} else {
					rc = OTERR_BadTag;
				}
				break;
			
			case OT_WidthList:
				ShowMessage("Was asked for WidthList ...");
				rc = OTERR_UnknownTag;
				break;
				
			
				
		} /* switch(ti->ti_Tag) */
		
	} /* while ((ti=NextTagItem(&tstate))) */
	
	return rc;
}

void __saveds ASM Lib_ReleaseInfoA(register __a0 struct MyGlyphEngine *glyphEngine GNUCREG(a0),register __a1 struct TagItem *taglist GNUCREG(a1))
{
	struct TagItem *tstate,*ti;
	struct GlyphMap *gm;
	APTR data;
	
	tstate=taglist;
	
	while ((ti=NextTagItem(&tstate)))
	{
		data = (APTR) ti->ti_Data;

		if (data)
		{
			switch(ti->ti_Tag)
			{
				case OT_WidthList:
					break;
					
				case OT_GlyphMap:
					if (data)
					{
						gm = (struct GlyphMap *)data;
						if (gm->glm_BitMap && (gm->glm_BitMap != emptybitmap))
						{
							FreeVec(gm->glm_BitMap);
						}
						FreeVec(gm);
					}
					break;

			} /* switch(ti->ti_Tag) */

		} /* if (data) */

	} /* while ((ti=NextTagItem(&tstate))) */
}

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

struct FD_FontQuery * __saveds ASM Lib_InitQueryFont(register __a0 char *fontname GNUCREG(a0),register __a1 ULONG *errorptr GNUCREG(a1))
{
	struct FD_FontQuery *rc=0;
	
	if (!(rc=AllocVec(sizeof(struct FD_FontQuery),MEMF_PUBLIC|MEMF_CLEAR)))
	{

		if (errorptr) *errorptr = FDERR_NOMEMORY;

	} else {

		if (TT_Init_FreeType(&rc->engine) != 0)
		{

			FreeVec(rc);
			rc=0;

			if (errorptr) *errorptr = FDERR_NOENGINE;

		} else {

			rc->errorptr = errorptr;
			if (TT_Open_Face(rc->engine,
								  fontname,
								  &rc->face) != 0)
			{
				TT_Done_FreeType(rc->engine);
				FreeVec(rc);
				rc=0;
			
				if (errorptr) *errorptr = FDERR_NOFONT;	
			} else {
				
				/* This fails only if face is not valid, which
				   should never be the case here */
				   
				if (TT_Get_Face_Properties(rc->face,
													&rc->face_properties) != 0)
				{
					TT_Close_Face(rc->face);
					TT_Done_FreeType(rc->engine);
					FreeVec(rc);
					rc=0;
					
					if (errorptr) *errorptr = FDERR_BADFONT;
				}

			}		
		}

	}
	
	return rc;
}

void __saveds ASM Lib_EndQueryFont(register __a0 struct FD_FontQuery *fontquery GNUCREG(a0))
{
	if (fontquery)
	{
		if (fontquery->face.z)
		{
			TT_Close_Face(fontquery->face);
		}
		if (fontquery->engine.z)
		{
			TT_Done_FreeType(fontquery->engine);
		}
		FreeVec(fontquery);
	}
}

ULONG __saveds ASM Lib_QueryFont(register __d0 ULONG queryid GNUCREG(d0),register __a0 struct FD_FontQuery *query GNUCREG(a0),register __a1 APTR storage GNUCREG(a1))
{
	LONG data;
	ULONG rc=0;
	
	switch(queryid)
	{
		case FDQUERY_MACSTYLE:
			*(ULONG *)storage = query->face_properties.header->Mac_Style;
			break;

		case FDQUERY_ISBOLD:
			*(ULONG *)storage = query->face_properties.header->Mac_Style & 1 ? 1 : 0;
			break;
			
		case FDQUERY_ISITALIC:
			*(ULONG *)storage = query->face_properties.header->Mac_Style & 2 ? 1 : 0;
			break;

		case FDQUERY_ISFIXED:
			*(ULONG *)storage = query->face_properties.postscript->isFixedPitch;
			break;

		case FDQUERY_FAMILYNAME:
			rc=GetName(1,query,storage);
			break;
		
		case FDQUERY_SUBFAMILYNAME:
			rc=GetName(2,query,storage);
			break;

		case FDQUERY_COPYRIGHT:
			rc=GetName(0,query,storage);
			break;
			
		case FDQUERY_FULLFONTNAME:
			rc=GetName(4,query,storage);
			break;
		
		case FDQUERY_WEIGHT:
			switch(query->face_properties.os2->usWeightClass)
			{
				case 100: data=OTS_Thin;break;
				case 200: data=OTS_ExtraLight;break;
				case 300: data=OTS_Light;break;
				case 400: data=OTS_Book;break;
				case 500: data=OTS_Medium;break;
				case 600: data=OTS_SemiBold;break;
				case 700: data=OTS_Bold;break;
				case 800: data=OTS_ExtraBold;break;
				case 900: data=OTS_Black;break;
				default:	 data=OTS_Book;break;
			}
			*(ULONG *)storage = data;
			break;
		
		case FDQUERY_REALWEIGHT:
			*(ULONG *)storage = query->face_properties.os2->usWeightClass;
			break;

		case FDQUERY_HORIZSTYLE:
			switch(query->face_properties.os2->usWidthClass)
			{
				case 1: data=OTH_UltraCompressed;break;
				case 2: data=OTH_ExtraCompressed;break;
				case 3: data=OTH_Compressed;break;
				case 4: data=OTH_Condensed;break;
				case 5: data=OTH_Normal;break;
				case 6: data=OTH_SemiExpanded;break;
				case 7: data=OTH_Expanded;break;
				case 8:
				case 9: data=OTH_ExtraExpanded;break;
				default: data=OTH_Normal;break;
			}
			*(ULONG *)storage = data;
			break;
			
		case FDQUERY_REALHORIZSTYLE:
			*(ULONG *)storage = query->face_properties.os2->usWidthClass;
			break;

		case FDQUERY_UNITS_PER_EM:
			*(ULONG *)storage = query->face_properties.header->Units_Per_EM;
			break;
	
		case FDQUERY_ASCENDER:
			*(ULONG *)storage = query->face_properties.horizontal->Ascender;
			break;
			
		case FDQUERY_DESCENDER:
			*(ULONG *)storage = query->face_properties.horizontal->Descender;
			break;

		default:
			rc=FDERR_UNKNOWN_QUERYID;
			break;

	} /* switch(rc) */

	return rc;
}


