/*
**	SpecialHost for PasTeX
**
**	Copyright © by Olaf Barthel & Georg Heßmann
*/

#include "Global.h"
#include "postlib.h"

/*#define DEBUG*/

#define INTOBP(x) ((float)(x*72.0))		/* converts 'inch' to 'big point' */
#define INTOSP(x) ((float)(x*72.0*65781.76))	/* converts 'inch' to 'scaled point' */
VOID __regargs PSGetSize(struct parse_result *Result, float *width, float *height, float *hoff, float *voff);
VOID __regargs PSGetSizeDot(struct parse_result *Result, LONG *width, LONG *height, LONG *hoff, LONG *voff);



struct Library *PostBase;

STATIC struct PSparm	 PSparm;
STATIC struct BitMap	*BitMap;
STATIC PLANEPTR		 Plane;
STATIC LONG		 ActivationRecord,
			 PlaneSize;
STATIC BOOL		 Transferred;

#ifdef DEBUG
STATIC struct Screen	*LocalScreen;
STATIC struct Window	*LocalWindow;
#endif	/* DEBUG */

extern void insertftrap(void);
extern void deleteftrap(void);

STATIC VOID __saveds
CopyPage(VOID)
{
	CopyMem(Plane,BitMap -> Planes[0],PlaneSize);

	Transferred = TRUE;

#ifdef DEBUG
	if(LocalScreen)
	{
		struct IntuiMessage *IntuiMessage;

		CopyMem(Plane,LocalScreen -> RastPort . BitMap -> Planes[0],LocalScreen -> RastPort . BitMap -> BytesPerRow * LocalScreen -> RastPort . BitMap -> Rows);

		ScreenToFront(LocalScreen);

		WaitPort(LocalWindow -> UserPort);

		while(IntuiMessage = (struct IntuiMessage *)GetMsg(LocalWindow -> UserPort))
			ReplyMsg(IntuiMessage);
	}
#endif	/* DEBUG */
}

void __saveds sigfpe(void)
{
	PSsignalfpe(ActivationRecord);
}

void __saveds sigint(void)
{
	PSsignalint(ActivationRecord,1);
}

struct BitMap * __regargs
ProcessPostscript(struct special_msg *Message,struct special_map *SpecialMap,struct parse_result *Result,LONG *Error)
{
	*Error = 0;
	
	BitMap = NULL;

	if(PostBase = OpenLibrary("post.library",15))
	{
		BPTR NIL;

		if(NIL = Open("NIL:",MODE_READWRITE))
		{
			LONG	NewWidth,
				NewHeight,
				X_DPI = Message -> hresolution,
				Y_DPI = Message -> vresolution;
			STRPTR special_string = NULL, sp;

			memset(&PSparm,0,sizeof(struct PSparm));

			PSGetSizeDot(Result, &NewWidth, &NewHeight, &(SpecialMap -> hoffset), &(SpecialMap -> voffset));

			SpecialMap -> width	= NewWidth;
			SpecialMap -> height	= NewHeight;
			
			PSparm . page . xoff = SpecialMap -> hoffset;
			PSparm . page . yoff = SpecialMap -> voffset;

			SpecialMap -> voffset = - SpecialMap -> voffset;

			special_string = AllocVecPooled(1024,MEMF_ANY);
			sp = special_string;

			sprintf(sp,"TeXDict begin %d %d 1000 %d %d (noname) @start\n", \
				(LONG) (INTOSP((float) NewWidth/(float)X_DPI) + 0.5),  \
				(LONG) (INTOSP((float) NewHeight/(float)Y_DPI) + 0.5), \
				X_DPI, Y_DPI);
			sp += strlen(sp);
			sprintf(sp,"1 0 bop\nResolution neg VResolution neg vsize -72 div 1 add mul a @beginspecial\n");
			sp += strlen(sp);

			if(Result -> gotcontrol & GOT_HSIZE)
			{
				sprintf(sp,"%g @hsize ", INTOBP(Result -> hsize));
				sp += strlen(sp);
			}

			if(Result -> gotcontrol & GOT_VSIZE)
			{
				sprintf(sp,"%g @vsize ", INTOBP(Result -> vsize));
				sp += strlen(sp);
			}

			if(Result -> gotcontrol & GOT_HSCALE)
			{
				sprintf(sp,"%g @hscale ", Result -> hscale * 100.0);
				sp += strlen(sp);
			}

			if(Result -> gotcontrol & GOT_VSCALE)
			{
				sprintf(sp,"%g @vscale ", Result -> vscale * 100.0);
				sp += strlen(sp);
			}

			if(Result -> gotcontrol & GOT_SCALE)
			{
				sprintf(sp,"%g @hscale %g @vscale ", Result -> scale * 100.0, Result -> scale * 100.0);
				sp += strlen(sp);
			}

			if(Result -> gotcontrol & GOT_HOFFSET)
			{
				sprintf(sp,"%g @hoffset ", INTOBP(Result -> hoffset));
				sp += strlen(sp);
			}

			if(Result -> gotcontrol & GOT_VOFFSET)
			{
				sprintf(sp,"%g @voffset ", INTOBP(Result -> voffset));
				sp += strlen(sp);
			}

			if (Result -> gotcontrol & GOT_ANGLE)
			{
				sprintf(sp,"%g @angle ", Result -> angle);
				sp += strlen(sp);
			}

			if (Result -> gotcontrol & GOT_CLIP)
			{
				sprintf(sp,"@clip ");
				sp += strlen(sp);
			}

			if(Result -> gotcontrol & SUFFICIENT_PS_ARGS)
			{
				sprintf(sp,"%g @llx %g @lly %g @urx %g @ury %g @rwi ", \
					INTOBP(Result -> llx), INTOBP(Result -> lly),  \
					INTOBP(Result -> urx), INTOBP(Result -> ury),  \
					INTOBP(Result -> rwi));
				sp += strlen(sp);
				if(Result -> gotcontrol & GOT_RHI)
				{
					sprintf(sp,"%g @rhi ", INTOBP(Result -> rhi));
					sp += strlen(sp);
				}
			}

			sprintf(sp,"@setspecial\n");
			sp += strlen(sp);
	
			if(NewWidth > 30000 || NewHeight > 30000)
				*Error = ERR_TOO_LARGE;
			else
			{
				if(NewWidth < 1 || NewHeight < 1)
					*Error = ERR_TOO_SMALL;
				else
				{
					PrintLine("Page size %ld × %ld, %ld × %ld DPI",NewWidth,NewHeight,Message -> hresolution,Message -> vresolution);

					if(BitMap = CreateBitMap(NewWidth,NewHeight,1,NULL,NULL))
					{
						if(Plane = (PLANEPTR)AllocVecPooled(PlaneSize = BitMap -> BytesPerRow * BitMap -> Rows,MEMF_ANY))
						{
							LONG ResultCode;

							insertftrap();

							PSparm . page . depth	= 1;
							PSparm . page . ydir	= -1;
							PSparm . page . xden	= X_DPI;
							PSparm . page . yden	= Y_DPI;
							PSparm . page . xsize	= NewWidth;
							PSparm . page . ysize	= NewHeight;
							PSparm . page . yheight	= NewHeight;
							PSparm . page . xbytes	= BitMap -> BytesPerRow;
							PSparm . page . len	= PSparm . page . xbytes * PSparm . page . ysize;
							PSparm . page . buf[0]	= Plane;

							PSparm . copyfunc	= (APTR)CopyPage;
							PSparm . infh		= NIL;
							PSparm . outfh		= NIL;
							PSparm . errfh		= NIL;

							ActivationRecord = PScreateact(&PSparm);

							if(ActivationRecord <= errmax)
							{
								if(ActivationRecord)
									*Error = 20000 + ActivationRecord;
								else
									*Error = ERR_NO_MEM;
							}
							else
							{
								ULONG Flags = PSFLAGCLEAR | PSFLAGERASE;
#ifdef DEBUG
								if(LocalScreen = OpenScreenTags(NULL,
									SA_Width,	NewWidth,
									SA_Height,	NewHeight,
									SA_Depth,	1,
									SA_DisplayID,	HIRESLACE_KEY,
									SA_Quiet,	TRUE,
									SA_AutoScroll,	TRUE,
									SA_ShowTitle,	FALSE,
									SA_Overscan,	OSCAN_STANDARD,
								TAG_DONE))
								{
									if(!(LocalWindow = OpenWindowTags(NULL,
										WA_Left,	0,
										WA_Top,		0,
										WA_Width,	LocalScreen -> Width,
										WA_Height,	LocalScreen -> Height,
										WA_RMBTrap,	TRUE,
										WA_Backdrop,	TRUE,
										WA_Borderless,	TRUE,
										WA_CustomScreen,LocalScreen,
										WA_Activate,	TRUE,
										WA_IDCMP,	IDCMP_VANILLAKEY,
									TAG_DONE)))
									{
										CloseScreen(LocalScreen);

										LocalScreen = NULL;
									}
								}
#endif	/* DEBUG */
								Transferred = FALSE;

								if(Result -> psinit_file[0] && !(*Error))
								{
									PrintLine("Executing initialization file...");
	
									if(ResultCode = PSintstring(ActivationRecord,Result -> psinit_file,-1,PSFLAGFILE | Flags))
										*Error = 20000 + ResultCode;
									else
										Flags = NULL;
								}

								if(Result -> psinit_string[0] && !(*Error))
								{
									PrintLine("Executing initialization string...");
	
									if(ResultCode = PSintstring(ActivationRecord,Result -> psinit_string,strlen(Result -> psinit_string),PSFLAGSTRING | Flags))
										*Error = 20000 + ResultCode;
									else
										Flags = NULL;
								}
/*
								if(!(*Error))
								{
									PrintLine("Executing file \"%s\"...","TeX:ps/finclude.pro");
	
									if(ResultCode = PSintstring(ActivationRecord,"TeX:ps/finclude.pro",-1,PSFLAGFILE | Flags))
										*Error = 20000 + ResultCode;
									else
										Flags = NULL;
								}

								if(!(*Error))
								{
									PrintLine("Executing file \"%s\"...","TeX:ps/texps.pro");
	
									if(ResultCode = PSintstring(ActivationRecord,"TeX:ps/texps.pro",-1,PSFLAGFILE | Flags))
										*Error = 20000 + ResultCode;
									else
										Flags = NULL;
								}

								if(!(*Error))
								{
									PrintLine("Executing file \"%s\"...","TeX:ps/special..pro");
	
									if(ResultCode = PSintstring(ActivationRecord,"TeX:ps/special.pro",-1,PSFLAGFILE | Flags))
										*Error = 20000 + ResultCode;
									else
										Flags = NULL;
								}

								if(!(*Error))
								{
									PrintLine("Executing file \"%s\"...","TeX:ps/color.pro");
	
									if(ResultCode = PSintstring(ActivationRecord,"TeX:ps/color.pro",-1,PSFLAGFILE | Flags))
										*Error = 20000 + ResultCode;
									else
										Flags = NULL;
								}
*/

								if(!(*Error))
								{
									PrintLine("Executing file \"%s\"...","TeX:ps/tex.pro");
	
									if(ResultCode = PSintstring(ActivationRecord,"TeX:ps/tex.pro",-1,PSFLAGFILE | Flags))
										*Error = 20000 + ResultCode;
									else
										Flags = NULL;
								}

								if(!(*Error))
								{
									PrintLine("Executing file \"%s\"...","TeX:ps/special.pro");
	
									if(ResultCode = PSintstring(ActivationRecord,"TeX:ps/special.pro",-1,PSFLAGFILE | Flags))
										*Error = 20000 + ResultCode;
									else
										Flags = NULL;
								}

								if (!(*Error))
								{
									PrintLine("Executing special string for @beginspecial...");
//									Write(Output(),special_string,strlen(special_string));

									if(ResultCode = PSintstring(ActivationRecord,special_string,strlen(special_string),PSFLAGSTRING | Flags))
										*Error = 20000 + ResultCode;
									else
										Flags = NULL;									
								}

								if(!(*Error))
								{
									PrintLine("Executing file \"%s\"...",Result -> psfile);
	
									if(ResultCode = PSintstring(ActivationRecord,Result -> psfile,-1,PSFLAGFILE | Flags))
										*Error = 20000 + ResultCode;
									else
									{
										if(!Transferred)
										{
											if(ResultCode = PSintstring(ActivationRecord,"showpage",8,PSFLAGSTRING))
												*Error = 20000 + ResultCode;
										}										
									}
								}

								if (!(*Error))
								{
									PrintLine("Executing special string for @endspecial...");

									sprintf(special_string,"@endspecial eop end userdict /end-hook known{end-hook}if\n");
									if(ResultCode = PSintstring(ActivationRecord,special_string,strlen(special_string),PSFLAGSTRING | Flags))
										*Error = 20000 + ResultCode;
									else
										Flags = NULL;									
								}

								PSdeleteact(ActivationRecord);
#ifdef DEBUG
								if(LocalWindow)
								{
									CloseWindow(LocalWindow);

									LocalWindow = NULL;
								}

								if(LocalScreen)
								{
									CloseScreen(LocalScreen);

									LocalScreen = NULL;
								}
#endif	/* DEBUG */
							}

							deleteftrap();

							FreeVecPooled(Plane);
						}
						else
							*Error = ERR_NO_MEM;

						if(*Error)
						{
							DeleteBitMap(BitMap);

							BitMap = NULL;
						}
					}
					else
						*Error = ERR_NO_MEM;
				}
			}

			Close(NIL);
			if (special_string) FreeVecPooled(special_string);
		}
		else
			*Error = ERR_NO_NIL;

		CloseLibrary(PostBase);

		PostBase = NULL;
	}
	else
		*Error = ERR_NO_POST;

	return(BitMap);
}

/* Here are some functions to get the size of the PostScript picture */

#define TR_ROTATION	1
#define	TR_SCALING	2
#define TR_TRANSLATION	3

#define RAD(a) ((float)(a*3.1415926535358979324/180.0))

struct ctm {
	float a,b,c,d,tx,ty;
	 };

struct bbox {
	float llx, lly, urx, ury;
	};

STATIC void ctm_transf(struct ctm *, float, float, int);
STATIC void point_transf(struct ctm *, float, float, float *, float *);
STATIC void bb_new(struct ctm *, struct bbox *, struct bbox *);

/* concatenates a transformation with the current transformation matrix. */
static void ctm_transf(struct ctm *ctm, float x, float y, int transformation)
{
	float anew, bnew, cnew, dnew, costheta, sintheta;

	switch (transformation) {

		case TR_ROTATION:

			costheta = cos(x);
			sintheta = sin(x);
			anew =   ctm -> a * costheta + ctm -> c * sintheta;
			bnew =   ctm -> b * costheta + ctm -> d * sintheta;
			cnew = - ctm -> a * sintheta + ctm -> c * costheta;
			dnew = - ctm -> b * sintheta + ctm -> d * costheta;
			ctm -> a = anew;
			ctm -> b = bnew;
			ctm -> c = cnew;
			ctm -> d = dnew;
			break;

		case TR_SCALING:

			ctm -> a *= x;
			ctm -> b *= x;
			ctm -> c *= y;
			ctm -> d *= y;
			break;
	
		case TR_TRANSLATION:


			ctm -> tx += ctm -> a * x + ctm -> c * y;
			ctm -> ty += ctm -> b * x + ctm -> d * y;
			break;

		default:

			break;
	}
}


/* Given a point (x,y) and the current transformation matrix ctm, returns
   the transformed point (x1,y1). */
static void point_transf(struct ctm *ctm, float x, float y, float *x1, float *y1)
{
	*x1 = ctm -> a * x + ctm -> c * y + ctm -> tx;
	*y1 = ctm -> b * x + ctm -> d * y + ctm -> ty;
}


/* Given the current transformation matrix and a bounding box, 
   returns the transformed bounding box. */
static void bb_new(struct ctm *ctm, struct bbox *bb, struct bbox *bbnew) {
	
	float x0,y0,x1,y1,x2,y2,x3,y3,xmin,xmax,ymin,ymax;

	xmin = ymin =  1.0e20;
	xmax = ymax = -1.0e20;

	point_transf(ctm, bb -> llx, bb -> lly, &x0, &y0);
	point_transf(ctm, bb -> urx, bb -> lly, &x1, &y1);
	point_transf(ctm, bb -> urx, bb -> ury, &x2, &y2);
	point_transf(ctm, bb -> llx, bb -> ury, &x3, &y3);
	
	xmin = min(xmin,x0);	ymin = min(ymin,y0);
	xmin = min(xmin,x1);	ymin = min(ymin,y1);
	xmin = min(xmin,x2);	ymin = min(ymin,y2);
	xmin = min(xmin,x3);	ymin = min(ymin,y3);
	xmax = max(xmax,x0);	ymax = max(ymax,y0);
	xmax = max(xmax,x1);	ymax = max(ymax,y1);
	xmax = max(xmax,x2);	ymax = max(ymax,y2);
	xmax = max(xmax,x3);	ymax = max(ymax,y3);

	bbnew -> llx = xmin;
	bbnew -> lly = ymin;
	bbnew -> urx = xmax;
	bbnew -> ury = ymax;
}


/* Given a pointer to a struct 'parse_result', returns width, height,
   hoffset, voffset of the picture, in inch. */
VOID __regargs PSGetSize(struct parse_result *Result, float *width, float *height, float *hoff, float *voff)
{
	float	hscale=1.0,	vscale=1.0,
		hscale_rxi=1.0,	vscale_rxi=1.0,
		hoffset=0.0,	voffset=0.0,
		angle=0.0;

	struct bbox bb, bbnew;
	struct ctm ctm;

	bb . llx = 0.0;
	bb . lly = 0.0;
	bb . urx = 8.0;		/* default width  = 8in */
	bb . ury = 11.0;	/* default height = 11in */

/* initialize the current transformation matrix with the identity matrix */
	ctm.a = ctm.d = 1.0;
	ctm.b = ctm.c = ctm.tx = ctm.ty = 0.0;

	if (Result -> gotcontrol & GOT_HSCALE)
		hscale = Result -> hscale;
	
	if (Result -> gotcontrol & GOT_VSCALE)
		vscale = Result -> vscale;

	if (Result -> gotcontrol & GOT_SCALE)
		hscale = vscale = Result -> scale;

	if (Result -> gotcontrol & GOT_HOFFSET)
		hoffset = Result -> hoffset;

	if (Result -> gotcontrol & GOT_VOFFSET)
		voffset = Result -> voffset;

	if (Result -> gotcontrol & GOT_ANGLE)
		angle = Result -> angle;

	if (Result -> gotcontrol & SUFFICIENT_PS_ARGS) {
		bb . llx = Result -> llx;
		bb . lly = Result -> lly;
		bb . urx = Result -> urx;
		bb . ury = Result -> ury;
 
		hscale_rxi = Result -> rwi / (10.0 * (bb . urx - bb . llx));

		if (Result -> gotcontrol & GOT_RHI)
			vscale_rxi = Result -> rhi / (10.0 * (bb. ury - bb . lly));
		else
			vscale_rxi = hscale_rxi;
	}

/* Performs PostScript transformations */
	ctm_transf(&ctm, hoffset, voffset, TR_TRANSLATION);
	ctm_transf(&ctm, hscale, vscale, TR_SCALING);
	ctm_transf(&ctm, RAD(angle), 0.0f, TR_ROTATION);
	ctm_transf(&ctm, hscale_rxi, vscale_rxi, TR_SCALING);
	ctm_transf(&ctm, (float)-bb.llx, (float)-bb.lly,TR_TRANSLATION);

	bb_new(&ctm, &bb, &bbnew);
	*width	= bbnew . urx - bbnew . llx;
	*height	= bbnew . ury - bbnew . lly;
	*hoff = bbnew . llx;
	*voff = bbnew . lly;

/* hsize & vsize define the clipping area; the clipping area isn't
   affected by scaling, translation or rotation. */

	if (Result -> gotcontrol & GOT_HSIZE)
	{
		*width = Result -> hsize ;
		*hoff  = 0.0;
	}

	if (Result -> gotcontrol & GOT_VSIZE)
	{
		*height = Result -> vsize ;
		*voff = 0.0;
	}
}

/* Same of PSGetSize but returns width, height, hoffset, voffset
   in 'dot' or 'pixels', using 'Result->hres' and 'Result->vres'. */
VOID __regargs PSGetSizeDot(struct parse_result *Result, LONG *width, LONG *height, LONG *hoff, LONG *voff)
{
	float f_width, f_height, f_hoff, f_voff;

	PSGetSize(Result, &f_width, &f_height, &f_hoff, &f_voff);
	*width	= (LONG)(f_width  * (float) Result -> hres + 0.5);
	*height	= (LONG)(f_height * (float) Result -> vres + 0.5);
	*hoff	= (LONG)(f_hoff   * (float) Result -> hres + 0.5);
	*voff	= (LONG)(f_voff   * (float) Result -> vres + 0.5);
}
