/*
SVF	- svfout.c
	(C) Copyright 1994 by SoftSource.  All rights reserved.
Scott Sherman 9-94...

	routines for outputting an SVF file
*/

#include <stdio.h>
#include <stdlib.h>
#include "svfcom.h"
#include "svfout.h"


static char *textCommand(SVFCommands command);
static int textoutput=0;

/* open & close svf files */
FILE *SVFOpen(char *filename)
{
	FILE	*fp;

	if (textoutput)
		fp = fopen(filename, "wt");
	else
		fp = fopen(filename, "wb");
	if (fp == NULL)
		return NULL;
	fputs(SVF_HEADER,fp);
	fputc(0,fp);
	if (textoutput)
		fputc('t',fp);
	return fp;
}

int SVFClose(FILE *fp)
{
	return fclose(fp);
}

/* general writing routines */
static void writeShort(FILE *fp,ushort num)
{
	if (textoutput)
		fprintf(fp," %d",(int)num);
	else
#ifdef BIGENDIAN
		fwrite(&num,2,1,fp);
#else
		{
		char *n = (char *)&num;
		fputc(n[1],fp);
		fputc(n[0],fp);
		}
#endif
}

static void writeShortPair(FILE *fp,ushort num1,ushort num2)
{
	writeShort(fp,num1);
	writeShort(fp,num2);
}

static void writeByte(FILE *fp,uchar num)
{
	if (textoutput)
		fprintf(fp," %d",(int)num);
	else
		fputc(num,fp);
}

static void writeBytePair(FILE *fp,uchar num1,uchar num2)
{
	writeByte(fp,num1);
	writeByte(fp,num2);
}

static void writeByteTriplet(FILE *fp,uchar num1,uchar num2,uchar num3)
{
	writeByte(fp,num1);
	writeByte(fp,num2);
	writeByte(fp,num3);
}

static void writeDouble(FILE *fp,double num)
{
	if (textoutput)
		fprintf(fp," %lf",num);
	else
#ifdef BIGENDIAN
		fwrite(&num,8,1,fp);
#else
		{
		char *n = (char *)&num;
		fputc(n[7],fp);
		fputc(n[6],fp);
		fputc(n[5],fp);
		fputc(n[4],fp);
		fputc(n[3],fp);
		fputc(n[2],fp);
		fputc(n[1],fp);
		fputc(n[0],fp);
		}
#endif
}

static void writeString(FILE *fp,svfstring str)
{
	if (str != NULL)
		fputs(str,fp);
	fputc(0,fp);
}

static void writeCommand(FILE *fp,SVFCommands command)
{
	if (textoutput)
		fprintf(fp,"\n%s",textCommand(command));
	else
		fputc(command,fp);
}

static void writeCommand1(FILE *fp,SVFCommands command,svfvalue x)
{
	if (IsCoordLength2(x))
		{
		writeCommand(fp,CommandLength2(command));
		writeShort(fp,x);
		}
	else
		{
		writeCommand(fp,CommandLength1(command));
		writeByte(fp,(uchar)x);
		}
}

static void writeCommand2(FILE *fp,SVFCommands command,svfvalue x,svfvalue y)
{
	if (IsCoordLength2(x) || IsCoordLength2(y))
		{
		writeCommand(fp,CommandLength2(command));
		writeShortPair(fp,x,y);
		}
	else
		{
		writeCommand(fp,CommandLength1(command));
		writeBytePair(fp,(uchar)x,(uchar)y);
		}
}

static void writeCommandOffset(FILE *fp,SVFCommands command,svfoffset x,svfoffset y)
{
	if (IsOffsetLength2(x) || IsOffsetLength2(y))
		{
		writeCommand(fp,CommandLength2(command));
		writeShortPair(fp,(ushort)x,(ushort)y);
		}
	else
		{
		writeCommand(fp,CommandLength1(command));
		writeBytePair(fp,(uchar)x,(uchar)y);
		}
}

static void writeCommand3(FILE *fp,SVFCommands command,svfvalue x,svfvalue y,svfvalue z)
{
	if (IsCoordLength2(x) || IsCoordLength2(y) || IsCoordLength2(z))
		{
		writeCommand(fp,CommandLength2(command));
		writeShortPair(fp,x,y);
		writeShort(fp,z);
		}
	else
		{
		writeCommand(fp,CommandLength1(command));
		writeByteTriplet(fp,(uchar)x,(uchar)y,(uchar)z);
		}
}

static void writeCommand4(FILE *fp,SVFCommands command,
	svfvalue x1,svfvalue y1,svfvalue x2,svfvalue y2)
{
	if (IsCoordLength2(x1) || IsCoordLength2(y1) || IsCoordLength2(x2) || IsCoordLength2(y2))
		{
		writeCommand(fp,CommandLength2(command));
		writeShortPair(fp,x1,y1);
		writeShortPair(fp,x2,y2);
		}
	else
		{
		writeCommand(fp,CommandLength1(command));
		writeBytePair(fp,(uchar)x1,(uchar)y1);
		writeBytePair(fp,(uchar)x2,(uchar)y2);
		}
}

static void writeCommand6(FILE *fp,SVFCommands command,
	svfvalue x1,svfvalue y1,svfvalue x2,svfvalue y2,svfvalue x3,svfvalue y3)
{
	if (IsCoordLength2(x1) || IsCoordLength2(y1) || IsCoordLength2(x2) || IsCoordLength2(y2)
			|| IsCoordLength2(x3) || IsCoordLength2(y3))
		{
		writeCommand(fp,CommandLength2(command));
		writeShortPair(fp,x1,y1);
		writeShortPair(fp,x2,y2);
		writeShortPair(fp,x3,y3);
		}
	else
		{
		writeCommand(fp,CommandLength1(command));
		writeBytePair(fp,(uchar)x1,(uchar)y1);
		writeBytePair(fp,(uchar)x2,(uchar)y2);
		writeBytePair(fp,(uchar)x3,(uchar)y3);
		}
}

/*
	header info
*/
void SVFOutputName(FILE *fp,svfstring name)
{
	writeCommand(fp,CommandLength1(SVF_Name));
	writeString(fp,name);
}

void SVFOutputExtents(FILE *fp,svfvalue xmin,svfvalue ymin,svfvalue xmax,svfvalue ymax)
{
	writeCommand4(fp,SVF_Extents,xmin,ymin,xmax,ymax);
}

void SVFOutputLayerTable(FILE *fp,svfvalue numentries)
{
	writeCommand1(fp,SVF_LayerTable,numentries);
}

void SVFOutputLayerEntry(FILE *fp,uchar status,svfstring layername)
{
	writeByte(fp,status);
	writeString(fp,layername);
}

void SVFOutputColorTable(FILE *fp,svfvalue numentries)
{
	writeCommand1(fp,SVF_ColorTable,numentries);
}

void SVFOutputColorEntry(FILE *fp,uchar r,uchar g,uchar b)
{
	writeByteTriplet(fp,r,g,b);
}

void SVFOutputBackground(FILE *fp,svfvalue color)
{
	writeCommand1(fp,SVF_Background,color);
}

void SVFOutputTransparent(FILE *fp,svfvalue color)
{
	writeCommand1(fp,SVF_Transparent,color);
}

void SVFOutputWidth(FILE *fp,svfvalue mm)
{
	writeCommand1(fp,SVF_Width,mm);
}

void SVFOutputNotifyTable(FILE *fp,svfvalue numin,svfvalue numout)
{
	writeCommand2(fp,SVF_NotifyTable,numin,numout);
}

void SVFOutputNotifyEntry(FILE *fp,svfstring text,svfvalue width)
{
	SVFOutputName(fp,text);
	SVFOutputWidth(fp,width);
}

void SVFOutputTransform(FILE *fp,double scale,double basex,double basey)
{
	writeCommand(fp,CommandLength8(SVF_Transform));
	writeDouble(fp,scale);
	writeDouble(fp,basex);
	writeDouble(fp,basey);
}

void SVFOutputSortByLayer(FILE *fp,int sortbylayer)
{
	if (sortbylayer)
		writeCommand1(fp,SVF_Flags,1);
	else
		writeCommand1(fp,SVF_Flags,0);
}

/*
	outputting commands
*/
/* points */
void SVFOutputPoint(FILE *fp,svfvalue x,svfvalue y)
{
	writeCommand2(fp,SVF_Point,x,y);
}

/* lines */
void SVFOutputMoveTo(FILE *fp,svfvalue x,svfvalue y)
{
	writeCommand2(fp,SVF_MoveTo,x,y);
}

void SVFOutputRelMoveTo(FILE *fp,svfoffset x,svfoffset y)
{
	writeCommand2(fp,SVF_RelMoveTo,(svfvalue)x,(svfvalue)y);
}

void SVFOutputLineTo(FILE *fp,svfvalue x,svfvalue y)
{
	writeCommand2(fp,SVF_LineTo,x,y);
}

void SVFOutputRelLineTo(FILE *fp,svfoffset x,svfoffset y)
{
	writeCommandOffset(fp,SVF_RelLineTo,x,y);
}

/* polylines & polygons */
static void outputStart(FILE *fp,SVFCommands command,svfvalue n,int bytes)
{
	if (bytes == 1)
		{
		writeCommand(fp,CommandLength1(command));
		writeByte(fp,(uchar)n);
		}
	else
		{
		writeCommand(fp,CommandLength2(command));
		writeShort(fp,n);
		}
}

void SVFOutputPolylineStart(FILE *fp,svfvalue n,int bytes)
{
	outputStart(fp,SVF_Polyline,n,bytes);
}

void SVFOutputRelPolylineStart(FILE *fp,svfvalue n,int bytes)
{
	outputStart(fp,SVF_RelPolyline,n,bytes);
}

void SVFOutputPolyPoint(FILE *fp,svfvalue x,svfvalue y,int bytes)
{
	if (bytes == 1)
		writeBytePair(fp,(uchar)x,(uchar)y);
	else
		writeShortPair(fp,x,y);
}

void SVFOutputRelPolyPoint(FILE *fp,svfoffset x,svfoffset y,int bytes)
{
	if (bytes == 1)
		writeBytePair(fp,(uchar)x,(uchar)y);
	else
		writeShortPair(fp,(ushort)x,(ushort)y);
}


/* rectangles */
void SVFOutputRectangle(FILE *fp,svfvalue width,svfvalue height)
{
	writeCommand2(fp,SVF_Rectangle,width,height);
}

/* curves */
void SVFOutputCircle(FILE *fp,svfvalue radius)
{
	writeCommand1(fp,SVF_Circle,radius);
}

void SVFOutputArc(FILE *fp,svfvalue radius,svfvalue startangle,svfvalue endangle)
{
	writeCommand3(fp,SVF_Arc,radius,startangle,endangle);
}

void SVFOutputArcLength(FILE *fp,svfvalue radius,svfvalue startangle,svfvalue endangle,int bytes)
{	/* force writing of the a particular length of the arc command */
	if (bytes == 1)
		{
		writeCommand(fp,CommandLength1(SVF_Arc));
		writeByte(fp,(uchar)radius);
		writeBytePair(fp,(uchar)startangle,(uchar)endangle);
		}
	else
		{
		writeCommand(fp,CommandLength2(SVF_Arc));
		writeShort(fp,radius);
		writeShortPair(fp,startangle,endangle);
		}
}

/* cubic Bezier curve */
void SVFOutputBezierCurve(FILE *fp,svfvalue cx1,svfvalue cy1,
	svfvalue cx2,svfvalue cy2,svfvalue x2,svfvalue y2)
{
	writeCommand6(fp,SVF_Bezier,cx1,cy1,cx2,cy2,x2,y2);
}

/* text */
void SVFOutputTextHeight(FILE *fp,svfvalue height)
{
	writeCommand1(fp,SVF_TextHeight,height);
}

void SVFOutputText(FILE *fp,ushort width,svfstring text)
{
	writeCommand1(fp,CommandLength1(SVF_Text),width);
	writeString(fp,text);
}

/* layers */
void SVFOutputSetLayer(FILE *fp,svfvalue layer)
{
	writeCommand1(fp,SVF_SetLayer,layer);
}

/* colors */
void SVFOutputSetColor(FILE *fp,svfvalue color)
{
	writeCommand1(fp,SVF_SetColor,color);
}

void SVFOutputInvisible(FILE *fp)
{
	writeCommand(fp,CommandLength1(SVF_Invisible));
}

/* return the closest color from the default palette for the given RGB value */
svfvalue SVFGetClosestColor(uchar r,uchar g,uchar b)
{
	uchar rclose,gclose,bclose;
	rclose = (uchar)( (r+18.214) / 36.429 );
	gclose = (uchar)( (g+18.214) / 36.429 );
	bclose = (uchar)( (b+42.5) / 85 );
	return (svfvalue)((rclose << 5) + (gclose << 2) + bclose);
}

/* pen width */
void SVFOutputSetPenWidth(FILE *fp,svfvalue width)
{
	writeCommand1(fp,SVF_SetPenWidth,width);
}

/* fill mode */
void SVFOutputFillMode(FILE *fp,svfvalue mode)
{
	writeCommand1(fp,SVF_FillMode,mode);
}

/* generic data */
void SVFOutputData(FILE *fp,svfvalue numbytes,void *data)
{
	writeCommand1(fp,SVF_Data,numbytes);
	fwrite(data,(size_t)numbytes,(size_t)1,fp);
}

/* define a hyperlink */
void SVFOutput1dLink(FILE *fp,svfstring action,svfstring extra)
{
	writeCommand(fp,CommandLength1(SVF_1dLink));
	writeString(fp,action);
	writeString(fp,extra);
}

void SVFOutput2dLink(FILE *fp,svfstring action,svfstring extra)
{
	writeCommand(fp,CommandLength1(SVF_2dLink));
	writeString(fp,action);
	writeString(fp,extra);
}

void SVFOutputEndLink(FILE *fp)
{
	SVFOutput1dLink(fp,NULL,NULL);
}


/*
	commands & their text equivalents
*/
static char *textCommand(SVFCommands command)
{
	command = BasicCommand(command);
	switch (command)
		{
		case SVF_Name : return "NA";
		case SVF_Extents : return "EX";
		case SVF_ColorTable : return "CT";
		case SVF_Background : return "BG";
		case SVF_LayerTable : return "LT";
		case SVF_Width : return "WI";
		case SVF_NotifyTable : return "NT";
		case SVF_Transparent : return "TC";	/* Transparent Color */
		case SVF_Transform : return "OT";	/* Original Transform */
		case SVF_Flags : return "FL";

		case SVF_Point : return "PO";
		case SVF_MoveTo : return "MT";
		case SVF_LineTo : return "LT";
		case SVF_Polyline : return "PL";
		case SVF_RelMoveTo : return "RM";
		case SVF_RelLineTo : return "RL";
		case SVF_RelPolyline : return "RP";
		case SVF_Rectangle : return "RT";
		case SVF_Circle : return "CI";
		case SVF_Arc : return "AR";
		case SVF_Bezier : return "BZ";
		case SVF_Text : return "TX";
		case SVF_TextHeight : return "TH";
		case SVF_SetColor : return "SC";
		case SVF_SetLayer : return "SL";
		case SVF_SetPenWidth : return "PW";
		case SVF_FillMode : return "FM";
		case SVF_Data : return "DA";
		case SVF_1dLink : return "L1";
		case SVF_2dLink : return "L2";
		case SVF_Invisible : return "IN";
		}
	return NULL;
}

/* if you wish to output binary or text */
/* note that the text version is not displayed by browsers */
void SVFTextOutput(int text)
{
	textoutput = text;
}
