/**
 * INCLUDES
 **/
#ifndef T1GST
#include "global.h"
#endif
#include <diskfont/oterrors.h>
#include <stdio.h>

/**
 * LOCAL PROTOTYPES
 **/
static int CopyType1GlyphMap(struct T1GlyphEngine *engine, struct GlyphMap *gm, struct GlyphMap **gm_p, unsigned long bmsize);
static int fill(unsigned char *dest, unsigned int h, unsigned int w, struct region *area);
extern void __asm fillrun(register __a0 unsigned char *p, register __d0 x0, register __d1 x1);


#ifndef hypot
#define hypot(a,b) (sqrt(((a)*(a))+((b)*(b))))
#endif

int MyType1OpenScalable(struct T1GlyphEngine *engine)
{
	FontScalableRec *vals = &engine->vals;
	int rc, size;
	struct type1font *type1;

	/* TetiSoft: If we have alredy a font opened, close it first... */
	if (engine->type1fontptr)
		MyType1CloseFont(engine);

	/* Reject ridiculously small font sizes */
	rc = OTERR_TooSmall;
	if ((hypot(vals->pixel_matrix[0], vals->pixel_matrix[1]) >= 1.0) &&
	    (hypot(vals->pixel_matrix[2], vals->pixel_matrix[3]) >= 1.0))
	{
		rc = OTERR_NoMemory;
		if ((type1 = (struct type1font *) xalloc(engine, sizeof(struct type1font))) != NULL)
		{
			bzero(type1, sizeof(struct type1font));
			size = 200000 + 120 * (int)hypot(vals->pixel_matrix[2], vals->pixel_matrix[3]) * sizeof(short);
			if (size > 0)
			{
				delmemory(engine);	// TetiSoft: Safe to be called
				if (addmemory(engine, size))
				{
					if (fontfcnA(engine, engine->fontreq.FontFileName, &rc) &&
					    setencoding(engine, vals->SymbolSet))
					{
						engine->type1fontptr = type1;
						rc = MyType1SetTransform(engine);
						if (rc == OTERR_Success)
							return OTERR_Success;
					}
					delmemory(engine);
				}
			}
			xfree(engine, type1);
		}
	}
	engine->type1fontptr = NULL;
	return rc;
}


int MyType1SetTransform(struct T1GlyphEngine *engine)
{
	FontScalablePtr vals = &engine->vals;

	psobj *fontmatrix;
	double t1 = 0.001, t2 = 0.0, t3 = 0.0, t4 = 0.001;
	struct type1font *type1;
	int i;

	if ((type1 = engine->type1fontptr) == NULL)
		return OTERR_NoMemory;

	/*
	** Changing the transform invalidates any glyphs we've already rendered,
	** so free them and mark them NULL, so we'll know to re-render them if
	** asked
	*/
	for (i = 0; i < NUMCOLS; i++)
	{
		if (type1->glyphs[i].glyphmap != NULL)
		{
			if (type1->glyphs[i].glyphmap->glm_BitMap != NULL)
				xfree(engine, type1->glyphs[i].glyphmap->glm_BitMap);
			xfree(engine, type1->glyphs[i].glyphmap);
			type1->glyphs[i].glyphmap = NULL;
		}
	}

	/* Reject ridiculously small font sizes */
	if ((hypot(vals->pixel_matrix[0], vals->pixel_matrix[1]) < 1.0) ||
	    (hypot(vals->pixel_matrix[2], vals->pixel_matrix[3]) < 1.0))
		return OTERR_TooSmall;

	fontmatrix = &engine->FontP->fontInfoP[FONTMATRIX].value;
	if ((objPIsArray(fontmatrix)) && (fontmatrix->len == 6))
	{
#define assign(n,d,f) if (objPIsInteger(fontmatrix->data.arrayP + n)) \
			  d = fontmatrix->data.arrayP[n].data.integer; \
		      else if (objPIsReal(fontmatrix->data.arrayP + n)) \
			  d = fontmatrix->data.arrayP[n].data.real; \
		      else d = f;

		assign (0, t1, .001);
		assign (1, t2, 0.0);
		assign (2, t3, 0.0);
		assign (3, t4, .001);
	}

	engine->XYS = t1_Transform(engine, engine->IDENTITY, t1, t2, t3, t4);

	/* Oblique */

// TetiSoft: Bug fix: wrong comparison
//	if ((vals->ShearSin != 0.0) && (vals->ShearCos != 1.0))
	if (!((vals->ShearSin == 0.0) && (vals->ShearCos == 1.0)))

		engine->XYS = t1_Transform(engine, engine->XYS, 1.0, 0.0, 

// TetiSoft: Bug fix: The shearing is *clockwise*, as specified on Developer CD, not the other way.
//						-1 * vals->ShearSin / vals->ShearCos, 1.0);
						vals->ShearSin / vals->ShearCos, 1.0);

	/* Rotate */

// TetiSoft: Bug fix: wrong comparison, rotating 180 degrees didn't work
//	if ((vals->RotateSin != 0.0) && (vals->RotateCos != 1.0))
	if (!((vals->RotateSin == 0.0) && (vals->RotateCos == 1.0)))

		engine->XYS = t1_Transform(engine, engine->XYS, vals->RotateCos, vals->RotateSin,
							-1.0 * vals->RotateSin, vals->RotateCos);

	engine->XYS = (struct XYspace *) t1_Permanent(engine, (struct xobject *) t1_Transform(engine, engine->XYS,   vals->pixel_matrix[0],
												-(vals->pixel_matrix[1]),
												  vals->pixel_matrix[2],
												-(vals->pixel_matrix[3])));

	return OTERR_Success;
}


/* TetiSoft: We must return a COPY of the cached GlyphMap
 * to avoid overwriting when e.g. OT_PointHeight changes.
 */
static int CopyType1GlyphMap(struct T1GlyphEngine *engine,
			     struct GlyphMap *gm,
			     struct GlyphMap **gm_p,
			     unsigned long bmsize)
{
	struct GlyphMap *newgm = (struct GlyphMap *)xalloc(engine, sizeof(struct GlyphMap));

	*gm_p = NULL;
	if (!newgm)
		return OTERR_NoMemory;
	memcpy(newgm, gm, sizeof(struct GlyphMap));
	newgm->glm_BitMap = xalloc(engine, bmsize);
	if (!newgm->glm_BitMap)
	{
		xfree(engine, newgm);
		return OTERR_NoMemory;
	}
	memcpy(newgm->glm_BitMap, gm->glm_BitMap, bmsize);
	*gm_p = newgm;
	return OTERR_Success;
}

#define  PAD(bits, pad)  (((bits)+(pad)-1)&-(pad))


int GetType1GlyphMap(
		struct T1GlyphEngine *engine,
		unsigned long GlyphCode,
		struct GlyphMap **gm_p)
{
	struct Type1Glyph *glyphs = engine->type1fontptr->glyphs;
	struct Type1Glyph *glyph;
	struct Type1Glyph uc_glyph;
	char uc_codename[32];
	int len, rc, size;
	char *codename;
	long paddedW, h, w;
	struct region *area;
	struct xobject *xarea;
	struct GlyphMap *gm;
	int emptybm;

	FontScalableRec *vals = &engine->vals;	// TetiSoft: Needed for font height in pixels...

	*gm_p = NULL;

	/*
	 * If the requested character is out of bounds,
	 */
	if ((GlyphCode < FIRSTCOL) || (GlyphCode & 0xffff0000))
		return OTERR_BadGlyph;

	if (GlyphCode <= LASTCOL)
	{
		codename = engine->fontencoding[GlyphCode].data.valueP;
		len = engine->fontencoding[GlyphCode].len;

		/*
		 * Or the encoding says the character is '.notdef',
		 * return default glyph		// TetiSoft: default glyph is NULL...
		 */
		if ((len == 7) && (strcmp(codename, ".notdef") == 0))
			return OTERR_UnknownGlyph;

		glyph = &glyphs[GlyphCode - FIRSTCOL];

		/*
		 * If we have already rendered the character,
		 * return it and go no further
		 */
		if (glyph->glyphmap != NULL)
		{
			if (glyph->rendered_ok)
				return CopyType1GlyphMap(engine, glyph->glyphmap, gm_p, glyph->bitmapsize);
			else
				return OTERR_TooSmall;
		}
	} else {
		/* TetiSoft: An Unicode glyph */
		int i;

		if (engine->fontreq.SymbolSet != UNICODE)
			return OTERR_BadGlyph;

		/* search name in AGL */
		uc_codename[0] = '\0';
		for (i = 0; AGL[i].name; i++)
		{
			if (AGL[i].index == GlyphCode)
				strcpy(uc_codename, AGL[i].name);
			if (AGL[i].index >= GlyphCode)
				break;
		}

		if (uc_codename[0] == '\0')
		{
			/* name not in AGL, construct uni<CODE> name */
			/* Can't use sprintf() in shared library... */
//			sprintf(uc_codename, "uni%04lX", GlyphCode);
			unsigned long val = GlyphCode;
			unsigned char c;
			uc_codename[0] = 'u';
			uc_codename[1] = 'n';
			uc_codename[2] = 'i';
			for (i = 6; i > 2; i--)
			{
				c = (val & 0x0f) + '0';
				if (c > '9')
					c += (('A' - '0') - 10);
				uc_codename[i] = c;
				val >>= 4;
			}
			uc_codename[7] = '\0';
		}

		glyph = &uc_glyph;
		codename = uc_codename;
		len = strlen(codename);
	}

	/** Okay, now we have to render the glyph
	 **/
	rc = 0;	// Could be set to FF_PATH
	area = fontfcnB(engine, engine->XYS, codename, &len, &rc);
	xarea = (struct xobject *)area;

	if ((rc != 0) || (area == NULL))
	{
		if (area != NULL)
			t1_Destroy(engine, xarea);
//kprintf("%ld\t%ld\tfontfcnB failed\n", __LINE__, GlyphCode);
		if (rc == FF_PARSE_ERROR)
			return OTERR_UnknownGlyph;
		else
			return OTERR_NoMemory;
	}

	/** Allocate space for the glyphmap
	 **/
	if (gm = (struct GlyphMap *)xalloc(engine, sizeof(struct GlyphMap)))
	{
		glyph->glyphmap = gm;
		/** Figure out the size of the bitmap, padded to multiples of 32 bits wide
		 **/
		h = area->ymax - area->ymin;
		w = area->xmax - area->xmin;
		paddedW = PAD(w, 32);			/* paddedW is in bits */
		size = h * (paddedW >> 3);		/* size is in bytes */

		if ((h <= 0) || (w <= 0))
		{
			/** If empty/oddball bitmap, return a glyphmap with empty 1*1 bitmap and
			 ** accurate spacing information. (For space characters...)
			 ** This fixes FinalWriter's AllocMem(0, ...)
			 **/
			h = 1;
			w = 1;
			paddedW = PAD(w, 32);		/* paddedW is in bits */
			size = h * (paddedW >> 3);	/* size is in bytes */
			emptybm = TRUE;
		} else
			emptybm = FALSE;

		/** Allocate space for the bitmap
		 **/
		if (gm->glm_BitMap = xalloc(engine, size))
		{
			glyph->bitmapsize = size;
			gm->glm_BMModulo = paddedW >> 3;
			gm->glm_BMRows = h;
			gm->glm_BlackLeft = 0;
			gm->glm_BlackTop = 0;
			gm->glm_BlackWidth = w;
			gm->glm_BlackHeight = h;
			if (emptybm) {
				gm->glm_XOrigin = area->origin.x;
				gm->glm_YOrigin = area->origin.y;
				gm->glm_X0 = NEARESTPEL(area->origin.x);
				gm->glm_Y0 = NEARESTPEL(area->origin.y);
				gm->glm_Y1 = NEARESTPEL(area->ending.y);
			} else {
				gm->glm_XOrigin = area->origin.x + TOFRACTPEL(0-area->xmin);
				gm->glm_YOrigin = area->origin.y + TOFRACTPEL(0-area->ymin);
				gm->glm_X0 = NEARESTPEL(area->origin.x) - area->xmin;
				gm->glm_Y0 = NEARESTPEL(area->origin.y) - area->ymin;
				gm->glm_Y1 = NEARESTPEL(area->ending.y) - area->ymin;
			}
			if(vals->GlyphWidth) {
				gm->glm_X1 = gm->glm_X0 + vals->GlyphWidthPixels;
				gm->glm_Width = vals->GlyphWidth;
			} else if(vals->IsFixed) {
				gm->glm_X1 = gm->glm_X0 + vals->FixedWidthPixels;
				gm->glm_Width = vals->FixedWidth;
			} else {
				gm->glm_X1 = NEARESTPEL(area->ending.x);
				if (!emptybm)
					gm->glm_X1 -= area->xmin;
				gm->glm_Width = hypot((double)area->ending.x, (double)(area->ending.y - area->origin.y)) / vals->pixel;
			}
#if 0
kprintf("o.x %ld o.y %ld e.x %ld e.y %ld xmin %ld ymin %ld xmax %ld ymax %ld hyp %ld pix %ld\n",
	area->origin.x,
	area->origin.y,
	area->ending.x,
	area->ending.y,
	area->xmin,
	area->ymin,
	area->xmax,
	area->ymax,
	(long)hypot((double)area->ending.x, (double)(area->ending.y - area->origin.y)),
	vals->pixel
);
#endif
//			glyph->metrics.leftSideBearing = area->xmin;
//			glyph->metrics.rightSideBearing = w + area->xmin;
//			glyph->metrics.descent = area->ymax - NEARESTPEL(area->origin.y);
//			glyph->metrics.ascent = h - glyph->metrics.descent;
//kprintf("Char %ld = %lc\n", i, i);
//kprintf("area->origin.x = %ld\n", NEARESTPEL(area->origin.x));
//kprintf("area->ending.x = %ld\n", NEARESTPEL(area->ending.x));
//kprintf("area->xmin     = %ld\n", area->xmin);
//kprintf("area->xmax     = %ld\n", area->xmax);
//kprintf("w              = %ld\n", w);
//kprintf("leftSideBear   = %ld\n", glyph->metrics.leftSideBearing);
//kprintf("rightSideBear  = %ld\n\n", glyph->metrics.rightSideBearing);
			bzero(gm->glm_BitMap, size);	// Clear bitmap
			glyph->rendered_ok = TRUE;
			if (!emptybm)
			{
// TetiSoft: We decide that we have an oddball width/height if we had nothing to fill
				if(fill(gm->glm_BitMap, h, paddedW, area))
				{
					t1_Destroy(engine, xarea);
//					*gm_p = gm;
//					return OTERR_Success;
					return CopyType1GlyphMap(engine, gm, gm_p, size);
				} else {
					glyph->rendered_ok = FALSE;
					t1_Destroy(engine, xarea);
					return OTERR_TooSmall;
				}
			} else {
				t1_Destroy(engine, xarea);
//				*gm_p = gm;
//				return OTERR_Success;
				return CopyType1GlyphMap(engine, gm, gm_p, size);
			}
		}
		xfree(engine, glyph->glyphmap);
		glyph->glyphmap = NULL;
	}
	t1_Destroy(engine, xarea);
	return OTERR_NoMemory;
}


void MyType1CloseFont(struct T1GlyphEngine *engine)
{
	int i;
	struct type1font *type1 = engine->type1fontptr;

	delmemory(engine);

	if (type1 == NULL)
		return;

	for (i = 0; i < NUMCOLS; i++)
	{
		if (type1->glyphs[i].glyphmap != NULL)
		{
			if (type1->glyphs[i].glyphmap->glm_BitMap != NULL)
				xfree(engine, type1->glyphs[i].glyphmap->glm_BitMap);
			xfree(engine, type1->glyphs[i].glyphmap);
		}
	}
	xfree(engine, type1);
	engine->type1fontptr = NULL;
}

#if 0
#define  ALLONES  0xFF

static void __inline fillrun(
		unsigned char *p,	/* address of this scan line */
		pel x0, pel x1)		/* left and right X */
{
	unsigned char startmask, endmask;	/* bits to set in first and last char*/
	unsigned int middle;	/* number of chars between start and end + 1    */

// TetiSoft: Don't forget very small vertical lines
#define DRAWEVENSMALLPOINTS
#ifdef DRAWEVENSMALLPOINTS
	if(x1 == x0)
		x1++;
#endif

	if (x1 <= x0)
		return;
	middle = x1 / (unsigned int)8 - x0 / (unsigned int)8;
	p += x0 / (unsigned int)8;
	x0 &= 7;
	x1 &= 7;

	startmask = ALLONES >> x0;
	endmask = ~(ALLONES >> x1);

	if (middle == 0)
		*p |= startmask & endmask;
	else
	{
		*p++ |= startmask;
		while (--middle > 0)
			*p++ = ALLONES;
		*p |= endmask;
	}
}
#endif

//static void fill(
static int fill(	// TetiSoft: return if filled something
		unsigned char *dest,	/* destination bitmap */
		unsigned int h,
		unsigned int w,		/* dimensions of 'dest', w padded */
		struct region *area)	/* region to write to 'dest' */
{
	struct edgelist *edge;		/* for looping through edges */
	unsigned char *p;		/* current scan line in 'dest' */
	unsigned int wbytes;		/* number of bytes in width */
	pel *leftP, *rightP;		/* pointers to X values, left and right */
	int xmin = area->xmin;		/* upper left X */
	int ymin = area->ymin;		/* upper left Y */
	int y;
	int filled = FALSE;

	wbytes = w >> 3;

	for (edge = area->anchor; VALIDEDGE(edge); edge = edge->link->link)
	{
		p = dest + (edge->ymin - ymin) * wbytes;
		leftP = edge->xvalues;
		rightP = edge->link->xvalues;

		for (y = edge->ymin; y < edge->ymax; y++)
		{
			fillrun(p, *leftP++ - xmin, *rightP++ - xmin);
			filled = TRUE;
			p += wbytes;
		}
	}
	return filled;
}
