/*
**	$Id$
**
**	DevCon/ScalableOutlineFonts/sofdemo.c
**
**	(C) Copyright 1991 Robert R. Burns
**	    All Rights Reserved
*/

#include <exec/types.h>
#include <exec/libraries.h>
#include <dos/dos.h>
#include <graphics/rastport.h>
#include <graphics/text.h>
#include <intuition/intuition.h>
#include "libraries/diskfont.h"
#include "libraries/diskfonttag.h"
#include <utility/tagitem.h>

#include <clib/exec_protos.h>
#include <clib/diskfont_protos.h>
#include <clib/dos_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/utility_protos.h>
#include <pragmas/exec_pragmas.h>
#include <pragmas/diskfont_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/intuition_pragmas.h>
#include <pragmas/utility_pragmas.h>

extern struct Library near *SysBase;
extern struct Library near *DOSBase;
extern int (*_ONBREAK)();

struct Library near *DiskfontBase = (struct Library *) 0;
struct GfxBase near *GfxBase = (struct Libary *) 0;
struct Library near *IntuitionBase = (struct Library *) 0;
struct Library near *UtilityBase = (struct Library *) 0;

struct Screen *LockedScreen = 0;
struct Screen *Screen = 0;

struct AvailFontsHeader *AFH = 0;
struct TAvailFonts *TAF;

char FilePath[256];
char FaceName[32];

struct NewWindow NW = {         /* 1.3 compatable part of window definition */
    0, 0,			/* LeftEdge, TopEdge */
    640, 0,			/* Width, Height (filled in later) */
    -1, -1,			/* DetailPen, BlockPen */
    CLOSEWINDOW|NOCAREREFRESH,	/* IDCMPFlags */
    SMART_REFRESH|		/* Flags */
    WINDOWCLOSE|WINDOWDEPTH|WINDOWDRAG,
    0, 0,			/* FirstGadget, CheckMark */
    0,				/* Title */
    0, 0,			/* Screen, BitMap */
    1, 1,			/* MinWidth, MinHeight */
    0, 0,			/* MaxWidth, MaxHeight */
				/*   (same as initial dimension) */
    WBENCHSCREEN		/* Type */
};

struct Window *Window = 0;
struct RastPort *RastPort = 0;


void
EndGame(code, format, args)
int code;
char *format, *args;
{
    struct EasyStruct ez;
    struct Message *im;

    if (IntuitionBase && code) {
	ez.es_StructSize = sizeof(struct EasyStruct);
	ez.es_Flags = 0;
	ez.es_Title = "Abnormal Termination";
	ez.es_TextFormat = format;
	ez.es_GadgetFormat = "OK";
	EasyRequestArgs(Window, &ez, 0, &args);
    }

    if (AFH)
	FreeVec(AFH);
    if (Window) {
	while (im = GetMsg(Window->UserPort))
	    ReplyMsg(im);
	CloseWindow(Window);
    }
    if (LockedScreen)
	UnlockPubScreen(0, LockedScreen);
    if (UtilityBase)
	CloseLibrary(UtilityBase);
    if (DiskfontBase)
	CloseLibrary(DiskfontBase);
    if (IntuitionBase)
	CloseLibrary(IntuitionBase);
    if (GfxBase)
	CloseLibrary(GfxBase);

    exit(code);
}


void CXBRK()
{
    EndGame(1, "Control C Break\n");
}


void
GetAvailFonts()
{
    int afShortage, afSize;

    afSize = 400;
    do {
	AFH = (struct AvailFontsHeader *) AllocVec(afSize, 0);
	if (AFH) {
	    afShortage = AvailFonts((STRPTR) AFH, afSize,
		    AFF_MEMORY|AFF_DISK|AFF_TAGGED);
	    if (afShortage) {
		FreeVec(AFH);
		afSize += afShortage;
	    }
	}    
	else
	    EndGame(RETURN_FAIL, "AFH AllocVec failed\n");
    }
	while (afShortage);
    TAF = (struct TAvailFonts *) (AFH+1);
}


struct TextFont *
OpenAspectedFont(faceName, ySize, xPercent, style)
char *faceName;
UWORD ySize, xPercent;
UBYTE style;
{
    struct TTextAttr tTextAttr;
    struct TagItem tag, *tags, tagList[2];
    struct FontContentsHeader fch;
    BPTR fFile;
    char *s;
    int actual, i, x, y;

    strcpy(FaceName, faceName);
    tTextAttr.tta_Name = FaceName;
    tTextAttr.tta_YSize = ySize;
    tTextAttr.tta_Style = style | FSF_TAGGED;
    tTextAttr.tta_Flags = 0;
    tTextAttr.tta_Tags = tagList;
    tagList[0].ti_Tag = TA_DeviceDPI;
    tagList[0].ti_Data = (100<<16) | xPercent;	/* xPercent -> YDPI */
    tagList[1].ti_Tag = TAG_DONE;

    if (style & (FSF_BOLD|FSF_ITALIC)) {
	/*
	 * The following code will determine if the typeface is an outline
	 * typeface.  If it is, it will try to find an alternate typeface
	 * that better matches the requested style.
	 *
	 * There are two comments I would like to make about how this is
	 * done.  First, it would probably be better to skip the .font
	 * file checking, even though this is how the diskfont library
	 * itself determines that an .otag file exists.  If the .otag
	 * file is looked for right away, it would make the following code
	 * faster and more flexible if future font contents identifiers
	 * are assigned.  Second, when an application looks for an
	 * alternate typeface it gets a better typeface, but it sacrifices
	 * installation independence -- i.e. the appearance of a stylistic
	 * variation depends on whether that variation is installed on a
	 * particular machine.  Admittadly, this variation is just a
	 * subtle form of the already existing font substitution problem.
	 */

	/*   build file path specification for face's font contents file */
	if (strrchr(faceName, ':')) {
	    /* face name contains absolute path specification already */
	    strcpy(FilePath, faceName);
	}
	else {
	    /* face name is reletive to the default FONTS: path */
	    strcpy(FilePath, "FONTS:");
	    AddPart(FilePath, faceName, 256);
	}

	/*   open font contents file */
	fFile = Open(FilePath, MODE_OLDFILE);
	if (fFile == 0) {
	    /* no font contents file: no font */
	    printf("no font contents file \"%s\"\n", FilePath);
	    return(0);
	}
	
	actual = Read(fFile, &fch, sizeof(fch));
	Close(fFile);

	if ((actual != sizeof(fch)) || ((fch.fch_FileID & 0xfff0) != FCH_ID)) {
	    /* failure to read valid font contents file header */
	    printf("fch failure: actual %ld, id $%04lx\n", actual,
		    fch.fch_FileID);
	    return(0);
	}

	if (fch.fch_FileID == OFCH_ID) {
	    /* .font file indicates an .otag file exists */
	    /* generate .otag file path */
	    s = (char *) strrchr(FilePath, '.');
	    strcpy(s, OTSUFFIX);

	    /* open .otag file */
	    if (!(fFile = Open(FilePath, MODE_OLDFILE))) {
		printf("no outline tag file \"%s\"\n", FilePath);
		return(0);
	    }

	    /* read and verify the .otag */
	    actual = Read(fFile, &tag, sizeof(tag));
	    Seek(fFile, 0, OFFSET_END);
	    if ((actual == sizeof(tag)) && (tag.ti_Tag == OT_FileIdent) &&
		    (Seek(fFile, 0, OFFSET_BEGINNING) == tag.ti_Data) &&
		    (tags = (struct TagItem *) AllocVec(tag.ti_Data, 0))) {
		/* this is a valid .otag file header */
		if (Read(fFile, tags, tag.ti_Data) == tag.ti_Data) {
		    /* patch indirect pointers */
		    for (i = 0; i < tag.ti_Data/sizeof(struct TagItem); i++) {
			if (tags[i].ti_Tag == TAG_DONE)
			    break;
			if (tags[i].ti_Tag & OT_Indirect)
			    tags[i].ti_Data += (ULONG) tags;
		    }

		    /* find alternate font for style */
		    s = 0;
		    x = GetTagData(OT_StemWeight, OTS_Medium, tags);
		    y = GetTagData(OT_SlantStyle, OTS_Upright, tags);
		    if ((style & (FSF_ITALIC|FSF_BOLD)) ==
			    (FSF_ITALIC|FSF_BOLD)) {
			/* check if bold italic is appropriate */
			if ((x < ((OTS_Medium+OTS_SemiBold)/2)) &&
				(y == OTS_Upright))
			    s = (char *) GetTagData(OT_BIName, 0, tags);
		    }
		    if ((!s) && (style & FSF_ITALIC)) {
			/* ensure italic */
			if (y == OTS_Upright)
			    s = (char *) GetTagData(OT_IName, 0, tags);
		    }
		    if ((!s) && (style & FSF_BOLD)) {
			/* ensure bold */
			if (x < ((OTS_Medium+OTS_SemiBold)/2))
			    s = (char *) GetTagData(OT_BName, 0, tags);
		    }
		    if (s) {
			strcpy(FaceName, s);
			strcat(FaceName, ".font");
		    }
		}
		FreeVec(tags);
	    }
	    Close(fFile);
	}
    }

    printf("ta %s style $%lx\n", tTextAttr.tta_Name,
	    tTextAttr.tta_Style);

    return(OpenDiskFont((struct TextAttr *) &tTextAttr));
}


void
DisplaySample(tf, string)
struct TextFont *tf;
char *string;
{
    printf("tf %s style $%lx flags $%lx\n", tf->tf_Message.mn_Node.ln_Name,
	    tf->tf_Style, tf->tf_Flags);
    SetFont(RastPort, tf);
    Text(RastPort, string, strlen(string));
    CloseFont(tf);
}


void
DemoMain()
{
    struct TextFont *tf;

    /* GetAvailFonts(); */
    Move(RastPort, 10,  50);
    if (tf = OpenAspectedFont("CGTimes.font", 20, 50, FS_NORMAL))
	DisplaySample(tf, "Times50:100 ");
    if (tf = OpenAspectedFont("CGTimes.font", 20, 100, FS_NORMAL))
	DisplaySample(tf, "Times100:100 ");
    if (tf = OpenAspectedFont("CGTimes.font", 20, 200, FS_NORMAL))
	DisplaySample(tf, "Times100:50 ");
    Move(RastPort, 10, 80);
    if (tf = OpenAspectedFont("CGTimes.font", 20, 100, FSF_BOLD))
	DisplaySample(tf, "Times100:100b ");
    if (tf = OpenAspectedFont("CGTimes.font", 20, 100, FSF_ITALIC))
	DisplaySample(tf, "Times100:100i ");
    if (tf = OpenAspectedFont("CGTimes.font", 20, 100, FSF_BOLD+FSF_ITALIC))
	DisplaySample(tf, "Times100:100bi ");
    Move(RastPort, 10, 110);
    if (tf = OpenAspectedFont("topaz.font", 20, 100, FS_NORMAL))
	DisplaySample(tf, "topaz100:100 ");
    if (tf = OpenAspectedFont("topaz.font", 20, 100, FSF_ITALIC))
	DisplaySample(tf, "topaz100:100i ");
    if (tf = OpenAspectedFont("topaz.font", 20, 100, FSF_BOLD))
	DisplaySample(tf, "topaz100:100b ");
    Move(RastPort, 10, 140);
    if (tf = OpenAspectedFont("topaz.font", 20, 50, FS_NORMAL))
	DisplaySample(tf, "topaz50:100 ");
    if (tf = OpenAspectedFont("topaz.font", 20, 200, FS_NORMAL))
	DisplaySample(tf, "topaz100:50 ");
    Move(RastPort, 10, 170);
    if (tf = OpenAspectedFont("topaz.font", 8, 100, FS_NORMAL))
	DisplaySample(tf, "topaz8-100:100 ");
    if (tf = OpenAspectedFont("topaz.font", 8, 50, FS_NORMAL))
	DisplaySample(tf, "topaz8-50:100 ");
    if (tf = OpenAspectedFont("topaz.font", 8, 200, FS_NORMAL))
	DisplaySample(tf, "topaz8-100:50 ");
    Move(RastPort, 10, 200);
    if (tf = OpenAspectedFont("topaz.font", 9, 100, FS_NORMAL))
	DisplaySample(tf, "topaz9-100:100 ");
    if (tf = OpenAspectedFont("topaz.font", 9, 50, FS_NORMAL))
	DisplaySample(tf, "topaz9-50:100 ");
    if (tf = OpenAspectedFont("topaz.font", 9, 200, FS_NORMAL))
	DisplaySample(tf, "topaz9-100:50 ");
    Move(RastPort, 10, 230);
    if (tf = OpenAspectedFont("topaz.font", 10, 100, FS_NORMAL))
	DisplaySample(tf, "topaz10-100:100 ");
    if (tf = OpenAspectedFont("topaz.font", 10, 50, FS_NORMAL))
	DisplaySample(tf, "topaz10-50:100 ");
    if (tf = OpenAspectedFont("topaz.font", 10, 200, FS_NORMAL))
	DisplaySample(tf, "topaz10-100:50 ");
    Move(RastPort, 10, 260);
    if (tf = OpenAspectedFont("topaz.font", 11, 100, FS_NORMAL))
	DisplaySample(tf, "topaz11-100:100 ");
    if (tf = OpenAspectedFont("topaz.font", 11, 50, FS_NORMAL))
	DisplaySample(tf, "topaz11-50:100 ");
    if (tf = OpenAspectedFont("topaz.font", 11, 200, FS_NORMAL))
	DisplaySample(tf, "topaz11-100:50 ");
}

main()
{
    struct IntuiMessage *im;
    ULONG signal;

    _ONBREAK = (int (*)()) CXBRK;
    /* validate library versions */
    if (SysBase->lib_Version < 36)
	EndGame(RETURN_ERROR, "exec.library version < 36\n");
    if (DOSBase->lib_Version < 36)
	EndGame(RETURN_ERROR, "dos.library version < 36\n");

    /* open libraries */
    if (!(GfxBase = (struct Library *) OpenLibrary("graphics.library", 36)))
	EndGame(RETURN_ERROR, "graphics.library version 36 open failure\n");
    if (!(IntuitionBase = (struct Library *)
	    OpenLibrary("intuition.library", 36)))
	EndGame(RETURN_ERROR, "intuition.library version 36 open failure\n");
    if (!(DiskfontBase = (struct Library *)
	    OpenLibrary("diskfont.library", 36)))
	EndGame(RETURN_ERROR, "diskfont.library version 36 open failure\n");
    if (!(UtilityBase = (struct Library *)
	    OpenLibrary("utility.library", 36)))
	EndGame(RETURN_ERROR, "utility.library version 36 open failure\n");

    /* get the output screen */
    LockedScreen = Screen = LockPubScreen(0);

    /* open the window */
    if (!(Window = OpenWindowTags(&NW,
	    WA_Height, Screen->Height,
	    WA_Title, "ScalableOutlineFonts Demo",
	    WA_AutoAdjust, TRUE,
	    WA_PubScreen, Screen,
	    TAG_DONE)))
	EndGame(RETURN_ERROR, "OpenWindow failed\n");

    /* now that the window is open, the screen lock can be let go */
    UnlockPubScreen(0, LockedScreen);
    LockedScreen = 0;

    RastPort = Window->RPort;

    DemoMain();

    for (;;) {
	while (im = (struct IntuiMessage *) GetMsg(Window->UserPort)) {
	    if (im->Class == CLOSEWINDOW) {
		ReplyMsg((struct Message *) im);
		EndGame(0);
	    }
	    ReplyMsg((struct Message *) im);
	}
	signal = Wait((1<<Window->UserPort->mp_SigBit)|SIGBREAKF_CTRL_C);
	if (signal & SIGBREAKF_CTRL_C)
	    CXBRK();
    }
}
