/*
**	dispatch.c - dispatcher for Font DataType class
**	Copyright © 1995 Michael Letowski
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <graphics/displayinfo.h>
#include <graphics/gfx.h>
#include <graphics/modeid.h>
#include <graphics/rastport.h>
#include <graphics/text.h>
#include <intuition/classes.h>
#include <datatypes/datatypesclass.h>
#include <datatypes/pictureclass.h>
#include <diskfont/diskfont.h>
#include <diskfont/diskfonttag.h>
#include <utility/hooks.h>
#include <support/types.h>

#include <stdlib.h>

#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/diskfont.h>
#include <proto/datatypes.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/utility.h>
#include <clib/support_protos.h>

#include "classbase.h"
#include "dispatch.h"
#include "prefs.h"
#include "otag.h"

#define COL_WHITE						0xFF
#define COL_BLACK						0x00

#define MAX_CHAR						257

/* Get font's name */
#define FontName(f)	((f)->tf_Message.mn_Node.ln_Name)

typedef int (SFUNC)(void const*, void const *);

STATIC ASM Object *Dispatch(R_A0 Class *cl, R_A2 Object *o, R_A1 Msg msg);
STATIC LBOOL GetFont(struct ClassBase *cb, Object *o, struct TagItem *attrs);

/* Font management */
STATIC struct TextFont **OpenFonts(struct ClassBase *cb,
																		struct FontContentsHeader *fch, STRPTR name);
STATIC VOID CloseFonts(struct ClassBase *cb, struct TextFont **f, ULONG cnt);
STATIC VOID GetWH(struct ClassBase *cb, struct RastPort *rp, struct Opts *opt,
										struct TextFont *f, ULONG *w, ULONG *h);
STATIC VOID Render(struct ClassBase *cb, struct RastPort *rp, struct Opts *opt,
										struct TextFont **f, ULONG cnt, ULONG w);
STATIC VOID MakeASCII(struct TextFont *f, CHAR *s);
STATIC VOID MakeFontName(struct TextFont *f, CHAR *s);
STATIC LONG SortFunc(struct TextFont **tf1, struct TextFont **tf2);

/* DataTypes stubs */
STATIC ULONG LocSetDTAttrs(struct ClassBase *cb, Object *o, ULONG data, ...);
STATIC ULONG LocGetDTAttrs(struct ClassBase *cb, Object *o, ULONG data, ...);

Class *InitClass(struct ClassBase *cb)
{
	Class *CL;

	/* Create our class (no instance) */
	if(CL=MakeClass(FONTDTCLASS,PICTUREDTCLASS,NULL,NULL,0))
	{
		CL->cl_Dispatcher.h_Entry=(HOOKFUNC)Dispatch;
		CL->cl_UserData=(ULONG)cb;
		AddClass(CL);
	}
	return(CL);
}	/* InitClass */

STATIC ASM Object *Dispatch(R_A0 Class *cl, R_A2 Object *o, R_A1 Msg msg)
{
	struct ClassBase *cb=(struct ClassBase *)cl->cl_UserData;
	Object *Obj;

	switch(msg->MethodID)
	{
		case OM_NEW:																/* We know this method */
			if(Obj=(Object *)DoSuperMethodA(cl,o,msg))
				unless(GetFont(cb,Obj,((struct opSet *)msg)->ops_AttrList))
				{
					CoerceMethod(cl,Obj,OM_DISPOSE);
					return(NULL);
				}
			break;
		default:																		/* Let the superclass handle it */
			Obj=(Object *)DoSuperMethodA(cl,o,msg);
			break;
	}
	return(Obj);
}

STATIC LBOOL GetFont(struct ClassBase *cb, Object *o, struct TagItem *attrs)
{
	struct RastPort RP;
	struct Opts Opts;
	struct BitMapHeader *BMHD=NULL;
	struct BitMap *BM;
	struct ColorRegister *CMap=NULL;
	LONG *CRegs=NULL;
	struct FileInfoBlock *FIB;
	struct FontContentsHeader *FCH;
	struct TextFont **Fonts,*F;
	struct ColorTextFont *CF;
	struct ColorFontColors *CFC;
	struct PrefsHandle *PH;

	STRPTR Name=NULL,Title;												/* Font file name & picture title */
	BPTR DirLock,FH=0;
	ULONG ModeID,SourceID;												/* Display mode */
	ULONG Width,Height,Depth,NumColors;
	ULONG I,J,NumFonts;
	LBOOL Tall,Wide,Result=FALSE;
	UBYTE Col0,Col1;

	/* Get default title */
	Title=(STRPTR)GetTagData(DTA_Name,NULL,attrs);

	/* Get file handle and BitMapHeader */
	LocGetDTAttrs(cb,o,DTA_Handle,&FH,PDTA_BitMapHeader,&BMHD,TAG_DONE);
	try(FH && BMHD,	EXIT);

	/* Get font file name */
	try(FIB=AllocDosObject(DOS_FIB,NULL),	NO_FIB);/* Create FileInfoBlock */
	if(ExamineFH(FH,FIB))													/* Examine it */
		Name=FIB->fib_FileName;											/* Get name pointer */
	else
		if(Title)																		/* Get file part of title */
			Name=FilePart(Title);
	try(Name,	NO_NAME);

	/* Get font */
	try(DirLock=ParentOfFH(FH),				NO_LOCK);
	try(FCH=NewFC(cb,DirLock,Name),		NO_FCH);
	try(Fonts=OpenFonts(cb,FCH,Name),	NO_FONTS);
	NumFonts=FCH->fch_NumEntries;

	/* Read preferences */
	PH=GetFontPrefs(cb,&Opts);

	/* Calculate sizes */
	Width=Height=0;
	Depth=1;
	InitRastPort(&RP);														/* Set up RastPort - here! */
	for(I=0; I<NumFonts; I++)
		if(F=Fonts[I])															/* Opened successfully */
		{
			GetWH(cb,&RP,&Opts,F,&Width,&Height);			/* Get width and height */
			if(ftst(F->tf_Style,FSF_COLORFONT))				/* This is ColorFont */
			{
				CF=(struct ColorTextFont *)F;
				if(CF->ctf_Depth>Depth)									/* Deeper? */
					Depth=CF->ctf_Depth;									/* Set new depth */
			}
			Tall=ftst(F->tf_Flags,FPF_TALLDOT);				/* Hires? */
			Wide=ftst(F->tf_Flags,FPF_WIDEDOT);				/* Interlaced? */
		}

	/* Set up BitMap header */
	BMHD->bmh_Width=Width;												/* Fill in informations */
	BMHD->bmh_Height=Height;
	BMHD->bmh_Depth=Depth;
	NumColors=1<<Depth;

	/* Get display mode id */
	if(Tall && Wide)	SourceID=HIRESLACE_KEY;
	else if(Tall)			SourceID=HIRES_KEY;
	else if(Wide)			SourceID=LORESLACE_KEY;
	else							SourceID=LORES_KEY;
	ModeID=BestModeID(BIDTAG_DesiredWidth,	Width,
										BIDTAG_DesiredHeight,	Height,
										BIDTAG_Depth,					Depth,
										BIDTAG_SourceID,			SourceID,
										TAG_DONE);

	/* Set colors */
	LocSetDTAttrs(cb,o,PDTA_NumColors,NumColors,TAG_DONE);
	LocGetDTAttrs(cb,o,PDTA_ColorRegisters,&CMap,PDTA_CRegs,&CRegs,TAG_DONE);
	try(CMap && CRegs,	ERROR);
	if(Depth==1)																	/* B&W font */
	{
		Col0=Opts.opt_Inverse ? COL_BLACK : COL_WHITE;
		Col1=Opts.opt_Inverse ? COL_WHITE : COL_BLACK;
		CMap[0].red=CMap[0].green=CMap[0].blue=Col0;
		CMap[1].red=CMap[1].green=CMap[1].blue=Col1;
		CRegs[0]=CRegs[1]=CRegs[2]=Col0<<24;
		CRegs[3]=CRegs[4]=CRegs[5]=Col1<<24;
	}
	else																					/* Some color fonts */
		for(I=0; I<NumFonts; I++)
			if(F=Fonts[I])
				if(ftst(F->tf_Style,FSF_COLORFONT))
				{
					CF=(struct ColorTextFont *)F;
					if(CFC=CF->ctf_ColorFontColors)						/* Color map exists */
						for(J=0; J<CFC->cfc_Count; J++)
						{
							CMap[J].red=		(CFC->cfc_ColorTable[J] & 0x0F00)>>4;
							CRegs[J*3+0]=		CMap[J].red<<24;
							CMap[J].green=	(CFC->cfc_ColorTable[J] & 0x00F0);
							CRegs[J*3+1]=		CMap[J].green<<24;
							CMap[J].blue=		(CFC->cfc_ColorTable[J] & 0x000F)<<4;
							CRegs[J*3+2]=		CMap[J].blue<<24;
						}
				}

	/* Prepare bitmap */
	try(BM=AllocBitMap(BMHD->bmh_Width,BMHD->bmh_Height,BMHD->bmh_Depth,
											BMF_CLEAR | BMF_INTERLEAVED,NULL),
			ERROR);

	/* Do rendering */
	RP.BitMap=BM;																	/* Attach BitMap */
	Render(cb,&RP,&Opts,Fonts,NumFonts,Width);

	/* Set attributes of destination picture */
	LocSetDTAttrs(cb,o,DTA_ObjName,				Title,
											DTA_NominalHoriz,	BMHD->bmh_Width,
											DTA_NominalVert,	BMHD->bmh_Height,
											PDTA_BitMap,			BM,
											PDTA_ModeID,			ModeID,
											TAG_DONE);
	Result=TRUE;

	/* Cleanup */
	except(ERROR,			);
	if(PH)
		FreeFontPrefs(cb,PH);
	except(NO_FONTS,	CloseFonts(cb,Fonts,NumFonts));
	except(NO_FCH,		DisposeFC(cb,FCH));
	except(NO_LOCK,		UnLock(DirLock));
	except(NO_NAME,		);
	except(NO_FIB,		FreeDosObject(DOS_FIB,FIB));
	except(EXIT,			);
	return(Result);
}	/* GetFont */

/****************************************************************************/
/*                             Font management                              */
/****************************************************************************/

STATIC struct TextFont **OpenFonts(struct ClassBase *cb,
																		struct FontContentsHeader *fch, STRPTR name)
{
	struct TextFont **Fonts;
	struct TFontContents *TFC;
	struct TTextAttr TTA;
	ULONG I,NumEntries=fch->fch_NumEntries;
	LBOOL One=FALSE;															/* Got at least one size */

	try(Fonts=AllocVec(NumEntries*sizeof(APTR),MEMF_CLEAR),	NO_FONTS);
	for(I=0; I<NumEntries; I++)
	{
		TFC=&TFontContents(fch)[I];								/* Get FontContents */
		TTA.tta_Name=name;												/* Copy attrs */
		TTA.tta_YSize=TFC->tfc_YSize;
		TTA.tta_Style=TFC->tfc_Style;
		TTA.tta_Flags=TFC->tfc_Flags;
		if(ftst(TFC->tfc_Style,FSF_TAGGED))				/* Tags should be set */
			TTA.tta_Tags=(struct TagItem *)
										&TFC->tfc_FileName
											[MAXFONTPATH-(TFC->tfc_TagCount*sizeof(struct TagItem))];
		if(Fonts[I]=OpenDiskFont((struct TextAttr *)&TTA))
			One=TRUE;
	}
	if(One)																				/* Got at least one font */
	{																							/* Sort by size */
		qsort(Fonts,NumEntries,sizeof(APTR),(SFUNC *)SortFunc);
		return(Fonts);
	}
	else
		SetIoErr(ERROR_OBJECT_NOT_FOUND);						/* Set error */

	except(NO_FONTS,	FreeVec(Fonts));
	return(NULL);
}	/* OpenFonts */

STATIC VOID CloseFonts(struct ClassBase *cb, struct TextFont **f, ULONG cnt)
{
	ULONG I;

	for(I=0; I<cnt; I++)
		if(f[I])
			CloseFont(f[I]);
	FreeVec(f);
}	/* CloseFonts */

STATIC VOID GetWH(struct ClassBase *cb, struct RastPort *rp, struct Opts *opt,
										struct TextFont *f, ULONG *w, ULONG *h)
{
	STRPTR DefStrings[]={NULL,NULL};
	STRPTR CurStr,*Strings;
	CHAR S[MAX_CHAR];
	ULONG W;

	SetFont(rp,f);
	DefStrings[0]=S;
	if(opt->opt_FontName && FontName(f))
		MakeFontName(f,S);													/* String=name+size */
	else
		MakeASCII(f,S);															/* Prepare default string */
	Strings=opt->opt_Strings ? opt->opt_Strings : DefStrings;
	while(CurStr=*Strings++)
	{
		W=TextLength(rp,CurStr,strlen(CurStr));			/* Calculate len of this line */
		if(W>*w)																		/* If larger... */
			*w=W;																			/* Make it new width */
		*h+=f->tf_YSize;														/* Add to height */
	}
}	/* GetWidth */

STATIC VOID Render(struct ClassBase *cb, struct RastPort *rp, struct Opts *opt,
										struct TextFont **f, ULONG cnt, ULONG w)
{
	struct TextFont *F;
	STRPTR DefStrings[]={NULL,NULL};
	STRPTR CurStr,*Strings;
	CHAR S[MAX_CHAR];
	ULONG I,X,Y=0;

	DefStrings[0]=S;
	for(I=0; I<cnt; I++)													/* For each font size */
		if(F=f[I])																	/* Size opened? */
		{
			SetFont(rp,F);														/* Make it current font */
			if(opt->opt_FontName && FontName(F))
				MakeFontName(F,S);											/* Prepare name+size */
			else
				MakeASCII(F,S);													/* Prepare default string */
			Strings=opt->opt_Strings ? opt->opt_Strings : DefStrings;

			while(CurStr=*Strings++)
			{
				X=opt->opt_Center ? (w-TextLength(rp,CurStr,strlen(CurStr)))/2 : 0;
				Move(rp,X,Y+F->tf_Baseline);
				Text(rp,CurStr,strlen(CurStr));
				Y+=F->tf_YSize;
			}
		}
}	/* Render */

STATIC VOID MakeASCII(struct TextFont *f, CHAR *s)
{
	ULONG I;

	memset(s,0,MAX_CHAR);													/* Clear string */
	for(I=f->tf_LoChar; I<=f->tf_HiChar; I++)
		s[I - f->tf_LoChar]=I;											/* Make ASCII array */
}	/* MakeASCII */

STATIC VOID MakeFontName(struct TextFont *f, CHAR *s)
{
	SNPrintf(s,MAX_CHAR-1,"%s %ld",FontName(f),f->tf_YSize);
}	/* MakeASCII */

STATIC LONG SortFunc(struct TextFont **tf1, struct TextFont **tf2)
{
	if(*tf1 && *tf2)	return((LONG)(*tf1)->tf_YSize - (*tf2)->tf_YSize);
	else if(*tf1)			return(-1);
	else if(*tf2)			return(1);
	else							return(0);
}	/* SortFunc */

/****************************************************************************/
/*                             DataTypes stubs                              */
/****************************************************************************/

STATIC ULONG LocSetDTAttrs(struct ClassBase *cb, Object *o, ULONG data, ...)
{
	return(SetDTAttrsA(o,NULL,NULL,(struct TagItem *)&data));
}	/* LocSetDTAttrs */

STATIC ULONG LocGetDTAttrs(struct ClassBase *cb, Object *o, ULONG data, ...)
{
	return(GetDTAttrsA(o,(struct TagItem *)&data));
}	/* LocGetDTAttrs */
