/*
 *----------------------------------------------------------------------
 *
 * PROGRAM	: gksmc
 *
 * FILE		: CGMutils.c
 *
 * CONTENTS	: Utility routines to write various things to the CGM
 *		  output file, such as integers, spearators points etc.
 *
 * GLOBALS USED : SepStrings, item_no, Encoding, vdc_type,
 *		  PointListForm, colr_prec, NumColrDefs, ColrDefList,
 *		  NumColrBits.
 *
 * DATE		: 26th April 1988
 *
 *----------------------------------------------------------------------
 */
#include <stdio.h>
#include "defns.h"
#include "tables.h"

/* Constants used when writing character encoded values */
#define THREE_BIT_MASK		7
#define FOUR_BIT_MASK		15
#define FIVE_BIT_MASK		31
#define CLEAR_SIXTH_BIT		95
#define SET_SEVENTH_BIT		64
#define POSITIVE_SIGN_MASK	0
#define NEGATIVE_SIGN_MASK	16
#define LHS 1
#define RHS 0

extern char *SepStrings[];
extern int item_no;


/*
 *-------------------------------------------------------------------------
 * write_item_name:
 *	Write the specified item name from the selected table to the
 * output file.
 *-------------------------------------------------------------------------
 */
write_item_name(outfile, table_id, item_id)
	FILE   *outfile;
	int     item_id;
{
	if (item_id == DEFAULT)
		item_id = item_no;
	if (table_id == OP_TABLE)
		fprintf(outfile, "%s", OpTable[item_id]);
	else
		fprintf(outfile, "%s", CgmTable[item_id]);
}


/*
 *-------------------------------------------------------------------------
 * write_enum_value:
 *	For Clear Text the appropriate enumeration literal text string is
 * written to the output file. For Character Encoding the integer value
 * of the enumeration literal is written to the output file.
 *-------------------------------------------------------------------------
 */
write_enum_value(outfile, enum_no, entry_id)
	FILE   *outfile;
	int     enum_no,
	        entry_id;
{
	if (Encoding == CLEAR_TEXT)
		fprintf(outfile, "%s", Enum[enum_no][entry_id]);
	else
		write_int(outfile, entry_id);
}


/*
 *-------------------------------------------------------------------------
 * write_separator:
 *	For Clear Text encoding, given the separator identifier the
 * corresponding separator string is written to the output file.
 * The other encodings do not have separators, so this routine does nothing
 * if creating a character or binary encoded CGM.
 *-------------------------------------------------------------------------
 */
write_separator(outfile, sep_id)
	FILE   *outfile;
	int     sep_id;
{
	if (Encoding == CLEAR_TEXT)
		fprintf(outfile, "%s", SepStrings[sep_id]);
}


/*
 *-------------------------------------------------------------------------
 * write_int:
 *	Writes an integer value to the output file in the format
 * specified by the CGM encoding style in force.
 *-------------------------------------------------------------------------
 */
write_int(outfile, value)
	FILE   *outfile;
	int     value;
{
	int     sign,
	        ind = 0,
	        i,
	        cont_flag = 0;
	char    str[20];

	if (Encoding == CLEAR_TEXT)
		fprintf(outfile, "%d", value);
	else
	{
		sign = (value < 0) ? NEGATIVE_SIGN_MASK : POSITIVE_SIGN_MASK;
		value = abs(value);
		while (value != 0 || ind == 0 || cont_flag)
		{
			cont_flag = 0;
			if ((value & FOUR_BIT_MASK) == value)
			{
				str[ind++] = 96 | sign | value;
				value >>= 4;
			}
			else
			{
				str[ind++] = 96 | (value & FIVE_BIT_MASK);
				value >>= 5;
				cont_flag = (value == 0) ? 1 : 0;
			}
		}
		ind--;
		str[0] &= CLEAR_SIXTH_BIT;
		for (i = ind; i >= 0; i--)
			fprintf(outfile, "%c", str[i]);
	}
}


/*
 *-------------------------------------------------------------------------
 * write_real:
 *	Writes a real value to the output file in the correct format for
 * the CGM encoding style in force.
 *-------------------------------------------------------------------------
 */
write_real(outfile, value)
	FILE   *outfile;
	double  value;
{
	int     mantissa,
	        exponent = 0,
	        sign,
	        i;
	double  dtmp,
	        int_part;

	if (Encoding == CLEAR_TEXT)
	{
		if (value < 0.0)
		{
			putc('-', outfile);
			value = -value;
		}
		dtmp = value;
		while (dtmp > 99999999)
			dtmp -= (double) 99999999.0;
		int_part = value - (dtmp - (int) dtmp);
		fprintf(outfile, "%.0f", int_part);
		putc('.', outfile);
		value -= int_part;
		for (i = 0; i < SigPlaces; i++)
		{
			value *= 10.0;
			fprintf(outfile, "%d", (int) value);
			value -= (int) value;
		}
	}
	else
	{
		sign = (value < 0.0) ? 1 : 0;
		value = (value < 0.0) ? -value : value;
		mantissa = (int) value;
		value -= mantissa;
		while (value != 0.0 && abs(exponent) < NumExpBits)
		{
			value *= 2;
			mantissa *= 2;
			exponent--;
			mantissa += (int) value;
			value -= (int) value;
		}
		if (sign)
			mantissa = -mantissa;
		write_mantissa(outfile, mantissa);
		write_int(outfile, exponent);
	}
}


/*
 *-------------------------------------------------------------------------
 * write_mantissa:
 *	Writes the mantissa of a real value to the output file.
 * This routine is only used in the character encoding
 *-------------------------------------------------------------------------
 */
write_mantissa(outfile, value)
	FILE   *outfile;
	int     value;
{
	int     sign,
	        ind = 0,
	        i,
	        cont_flag = 0;
	char    str[20];

	sign = (value < 0) ? NEGATIVE_SIGN_MASK : POSITIVE_SIGN_MASK;
	value = abs(value);
	while (value != 0 || ind == 0 || cont_flag)
	{
		cont_flag = 0;
		if ((value & THREE_BIT_MASK) == value)
		{
			str[ind++] = 104 | sign | value;
			value >>= 3;
		}
		else
		{
			str[ind++] = 96 | (value & FIVE_BIT_MASK);
			value >>= 5;
			cont_flag = (value == 0) ? 1 : 0;
		}
	}
	ind--;
	str[0] &= CLEAR_SIXTH_BIT;
	for (i = ind; i >= 0; i--)
		fprintf(outfile, "%c", str[i]);
}


/*
 *-------------------------------------------------------------------------
 * write_vdc:
 *	Writes a specified value in Virtual Device Coordinates. The type
 * depends on the current setting of VDCTYPE - either integer or real.
 *-------------------------------------------------------------------------
 */
write_vdc(outfile, vdc_value)
	FILE   *outfile;
	double  vdc_value;
{
	if (vdc_type == VDC_INT)
		write_int(outfile, (int) vdc_value);
	else
		write_real(outfile, vdc_value);
}


/*
 *-------------------------------------------------------------------------
 * write_point:
 *	Checks the current VDC type and calls the appropriate point
 * writing routine
 *-------------------------------------------------------------------------
 */
write_point(outfile, pt)
	FILE   *outfile;
	Point   pt;
{
	if (vdc_type == VDC_INT)
		write_int_point(outfile, pt);
	else
		write_real_point(outfile, pt);
}


/*
 *-------------------------------------------------------------------------
 * write_real_point:
 *	Writes the given real valued point to the output file, in the
 * correct encoding format.
 *-------------------------------------------------------------------------
 */
write_real_point(outfile, pt)
	FILE   *outfile;
	Point   pt;
{
	if (Encoding == CLEAR_TEXT)
	{
		putc('(', outfile);
		write_separator(outfile, OPTSEP);
		write_real(outfile, pt.x);
		write_separator(outfile, SEP);
		write_real(outfile, pt.y);
		write_separator(outfile, OPTSEP);
		putc(')', outfile);
	}
	else
	{
		write_real(outfile, pt.x);
		write_real(outfile, pt.y);
	}
}


/*
 *-------------------------------------------------------------------------
 * write_int_point:
 *	Writes the given integer valued point to the output file, in the
 * correct encoding format.
 *-------------------------------------------------------------------------
 */
write_int_point(outfile, pt)
	FILE   *outfile;
	Point   pt;
{
	if (Encoding == CLEAR_TEXT)
	{
		putc('(', outfile);
		write_separator(outfile, OPTSEP);
		write_int(outfile, (int) pt.x);
		write_separator(outfile, SEP);
		write_int(outfile, (int) pt.y);
		write_separator(outfile, OPTSEP);
		putc(')', outfile);
	}
	else
	{
		write_int(outfile, (int) pt.x);
		write_int(outfile, (int) pt.y);
	}
}


/*
 *-------------------------------------------------------------------------
 * write_point_list:
 *	Takes a pointer to a list of points and calls write_point for
 * each point in order.
 *-------------------------------------------------------------------------
 */
write_point_list(outfile, pt_list, num_pts)
	FILE   *outfile;
	Point  *pt_list;
	int     num_pts;
{
	int     i;

	if (Encoding == CHARACTER || PointListForm == LIST_INCR)
		conv_pt_list_to_incr(pt_list, num_pts);
	if (Encoding == CLEAR_TEXT)
	{
		write_separator(outfile, SOFTSEP);
		for (i = 1; i <= num_pts; i++)
		{
			write_point(outfile, *pt_list++);
			if (i < num_pts)
				write_separator(outfile, SEP);
			if (i % 4 == 0 && i != num_pts)
				putc('\n', outfile);
		}
	}
	else
	{
		for (i = 1; i <= num_pts; i++)
			write_point(outfile, *pt_list++);
	}
}


/*
 *-------------------------------------------------------------------------
 * conv_pt_list_to_incr:
 *	Takes a pointer to a list of points, and converts the list into
 * incremental form. The first point is specified in absolute coordinates,
 * for remaining points, the x and y values for each point give the
 * displacement from the previous point
 *-------------------------------------------------------------------------
 */
conv_pt_list_to_incr(pt_list, num_pts)
	Point  *pt_list;
	int     num_pts;
{
	int     i;

	for (i = num_pts - 1; i > 0; i--)
	{
		pt_list[i].x = pt_list[i].x - pt_list[i - 1].x;
		pt_list[i].y = pt_list[i].y - pt_list[i - 1].y;
	}
}


/*
 *-------------------------------------------------------------------------
 * write_rgb:
 *	Writes a rgb colour representation in the bitstream format used
 * in the character encoding.
 *-------------------------------------------------------------------------
 */
write_rgb(outfile, red, green, blue)
	FILE   *outfile;
	int     red,
	        green,
	        blue;
{
	char    str[20];
	int     ind = 0,
	        side,
	        i,
	        mask = 0;

	for (i = 0; i < NumColrBits; i++)
		mask = (mask << 1) | 1;
	red &= mask;
	green &= mask;
	blue &= mask;
	if (2 * (int) (NumColrBits / 2.0) == NumColrBits)	/* even no. of bits */
		side = RHS;
	else
		side = LHS;
	str[0] = SET_SEVENTH_BIT;
	for (i = 1; i <= NumColrBits; i++)
	{
		if (side == RHS)
		{
			str[ind] |= ((red & 1) << 2) | ((green & 1) << 1) | (blue & 1);
			side = LHS;
		}
		else
		{
			str[ind] |= ((red & 1) << 5) | ((green & 1) << 4) | ((blue & 1) << 3);
			side = RHS;
			ind++;
			str[ind] = SET_SEVENTH_BIT;
		}
		red >>= 1;
		green >>= 1;
		blue >>= 1;
	}
	ind--;
	for (i = ind; i >= 0; i--)
		putc(str[i], outfile);
}


/*
 *-------------------------------------------------------------------------
 * write_cell_row:
 *	Writes the list of colour indices referenced to the output file.
 * This is used in the CELL ARRAY and PATTERN REPRESENTATION items.
 *-------------------------------------------------------------------------
 */
write_cell_row(outfile, list, num_elts)
	FILE   *outfile;
	int    *list,
	        num_elts;
{
	int     i;

	write_separator(outfile, SEP);
	if (Encoding == CLEAR_TEXT)
	{
		putc('(', outfile);
		for (i = 0; i < num_elts; i++)
		{
			write_int(outfile, list[i]);
			if (i < num_elts - 1)
				write_separator(outfile, SEP);
			if (i % 40 == 0)
				putc('\n', outfile);
		}
		putc(')', outfile);
	}
	else
	{
		write_int(outfile, 0);	/* indicates indexed normal colour
					   list */
		for (i = 0; i < num_elts; i++)
			write_int(outfile, list[i]);
	}
}


/*
 *-------------------------------------------------------------------------
 * write_delta_pair:
 *	Writes a delta pair to the output file.
 *-------------------------------------------------------------------------
 */
write_delta_pair(outfile, dp)
	FILE   *outfile;
	Point   dp;
{
	write_separator(outfile, OPTSEP);
	write_vdc(outfile, dp.x);
	write_separator(outfile, SEP);
	write_vdc(outfile, dp.y);
	write_separator(outfile, OPTSEP);
}


/*
 *-------------------------------------------------------------------------
 * write_CGM_string:
 *	Writes the given string to the output file. For character encoding,
 * if inc_sos = 0 then the Start Of String sequence will not be printed.
 * This is needed for the Begin Metafile item substitution string.
 *-------------------------------------------------------------------------
 */
write_CGM_string(outfile, string, inc_sos)
	FILE   *outfile;
	char   *string;
	int     inc_sos;
{
	int     i;

	if (Encoding == CLEAR_TEXT)
	{
		putc('"', outfile);
		for (i = 0; string[i] != '\0'; i++)
			if (string[i] != '"')
				putc(string[i], outfile);
			else
				fprintf(outfile, "\"\"");
		putc('"', outfile);
	}
	else
	{	/* character encoding strings */
		if (inc_sos != NO_SOS)
			fprintf(outfile, "%s", SOS);
		fprintf(outfile, "%s%s", string, ST);
	}
}


/*
 *-------------------------------------------------------------------------
 * write_datarecord:
 *	Writes the given integer and real data to the output file as a
 * 'datarecord'. The datarecord type is defined as a string. The format
 * for this was not specified, so the path of least resistance was taken -
 * the values are written directly in the string, in the current encoding
 * format.
 *-------------------------------------------------------------------------
 */
write_datarecord(outfile, num_ints, int_list, num_reals, real_list)
	FILE   *outfile;
	int     num_ints,
	       *int_list,
	        num_reals;
	double *real_list;
{
	int     i;

	if (Encoding == CLEAR_TEXT)
		fprintf(outfile, "\"");
	else
		fprintf(outfile, "%s", SOS);
	write_int(outfile, num_ints);
	write_separator(outfile, SEP);
	write_int(outfile, num_reals);
	write_separator(outfile, SEP);
	for (i = 0; i < num_ints; i++)
	{
		write_int(outfile, int_list[i]);
		write_separator(outfile, SEP);
	}
	for (i = 0; i < num_reals; i++)
	{
		write_real(outfile, real_list[i]);
		if (i < num_reals - 1)
			write_separator(outfile, SEP);
	}
	if (Encoding == CLEAR_TEXT)
		fprintf(outfile, "\"");
	else
		fprintf(outfile, "%s", ST);
}


/*
 *-------------------------------------------------------------------------
 * write_colour_defn:
 *	This takes a single colour definition and writes a Colour Table
 * item to the output metafile. The colour definition is in the structure
 * type ColourDef (see defns.h).
 *-------------------------------------------------------------------------
 */
write_colour_defn(outfile, colr_def)
	FILE   *outfile;
	ColourDef colr_def;
{
	write_item_name(outfile, OP_TABLE, COLRTABLE);
	write_separator(outfile, SOFTSEP);
	write_int(outfile, colr_def.ColrIndex);
	if (Encoding == CLEAR_TEXT)
	{
		write_separator(outfile, SEP);
		write_int(outfile, colr_def.red);
		write_separator(outfile, SEP);
		write_int(outfile, colr_def.green);
		write_separator(outfile, SEP);
		write_int(outfile, colr_def.blue);
	}
	else
	{
		write_int(outfile, 0);
		write_rgb(outfile, colr_def.red, colr_def.green, colr_def.blue);
	}
}


/*
 *-------------------------------------------------------------------------
 * write_colour_list:
 *	This writes a colour definition item to the output metafile for
 * each entry in ColrDefList, which holds every colour definition done so
 * far. This is called at the start of a picture if the list contains any
 * definitions to maintain the colour table between pictures.
 *-------------------------------------------------------------------------
 */
write_colour_list(outfile)
	FILE   *outfile;
{
	int     i;

	for (i = 0; i < NumColrDefs; i++)
		write_colour_defn(outfile, ColrDefList[i]);
}


/*
 *-------------------------------------------------------------------------
 * update_colour_list:
 *	This is called whenever a Set Colour Representation item is read
 * from the Annex E metafile. The colour definition is added to the list
 * ColourDef to allow the colour table to be maintained between pictures.
 *-------------------------------------------------------------------------
 */
update_colour_list(colr_def)
	ColourDef colr_def;
{
	ColourDef *temp_colrs;
	int     i;

	NumColrDefs++;
	temp_colrs = (ColourDef *) calloc(NumColrDefs, sizeof(ColourDef));
	for (i = 0; i < NumColrDefs - 1; i++)
	{
		temp_colrs[i].ColrIndex = ColrDefList[i].ColrIndex;
		temp_colrs[i].red = ColrDefList[i].red;
		temp_colrs[i].green = ColrDefList[i].green;
		temp_colrs[i].blue = ColrDefList[i].blue;
	}
	temp_colrs[i].ColrIndex = colr_def.ColrIndex;
	temp_colrs[i].red = colr_def.red;
	temp_colrs[i].green = colr_def.green;
	temp_colrs[i].blue = colr_def.blue;
	if (NumColrDefs > 1)
		cfree(ColrDefList);
	ColrDefList = temp_colrs;
}


/*
 *-------------------------------------------------------------------------
 * check_param_range:
 *	Takes an integer value and compares it with the bounds given. If
 * it lies outside the bounds, write_error is called with the error
 * message number specified.
 *-------------------------------------------------------------------------
 */
check_param_range(value, lower, upper, error_number)
	int     value,
	        lower,
	        upper,
	        error_number;
{
	if (value < lower || value > upper)
		write_error(error_number);
}


/*
 *-------------------------------------------------------------------------
 * write_error:
 *	Takes an error number and writes the corresponding error message
 * to stderr.
 *-------------------------------------------------------------------------
 */
write_error(err_num)
	int     err_num;
{
	switch (err_num)
	{
		case 1:
			fprintf(stderr, "Insufficient points for ");
			fprintf(stderr, "polypoint primitive\n");
			break;
		case 2:
			fprintf(stderr, "Binary number format not accepted\n");
			break;
		case 3:
			fprintf(stderr, "Defaults file not present\n");
			break;
		case 4:
			fprintf(stderr, "Illegal entry in defaults file\n");
			break;
		case 5:
			fprintf(stderr, "Error in defaults file : SOFTSEP\n");
			break;
		case 6:
			fprintf(stderr, "Error in defaults file : OPTSEP\n");
			break;
		case 7:
			fprintf(stderr, "Error in defaults file : HARDSEP\n");
			break;
		case 8:
			fprintf(stderr, "Error in defaults file : SEP\n");
			break;
		case 9:
			fprintf(stderr, "Error in defaults file : TERM\n");
			break;
		case 10:
			fprintf(stderr, "Error in defaults file : CASE\n");
			break;
		case 11:
			fprintf(stderr, "Error in defaults file : MAXCOLR\n");
			break;
		case 12:
			fprintf(stderr, "Error in defaults file : COLRPREC\n");
			break;
		case 13:
			fprintf(stderr, "Error in defaults file\n");
			break;
		case 14:
			fprintf(stderr, "Error in defaults file : quoted string\n");
			break;
		case 15:
			fprintf(stderr, "Illegal DEVSPEC parameter ");
			fprintf(stderr, "in defaults file\n");
			break;
		case 16:
			fprintf(stderr, "Illegal DEVSCALE parameter ");
			fprintf(stderr, "in defaults file\n");
			break;
		case 17:
			fprintf(stderr, "Illegal POINTLIST parameter ");
			fprintf(stderr, "in defaults file\n");
			break;
		case 18:
			fprintf(stderr, "Illegal ENCODING parameter ");
			fprintf(stderr, "in defaults file\n");
			break;
		case 19:
			fprintf(stderr, "Segment priority > 1.0 not allowed");
			break;
		case 20:
			fprintf(stderr, "Error in input metafile, ");
			fprintf(stderr, "incorrect GKSM chars\n");
			break;
		case 21:
			fprintf(stderr, "Illegal metafile item number in ");
			fprintf(stderr, "input metafile\n");
			break;
		case 22:
			fprintf(stderr, "Illegal Annex E metafile header\n");
			break;
		case 23:
			fprintf(stderr, "Illegal integer in Annex E metafile\n");
			fprintf(stderr, "Most recent item number = %d\n", item_no);
			break;
		case 24:
			fprintf(stderr, "Illegal real in Annex E metafile\n");
			fprintf(stderr, "Most recent item number = %d\n", item_no);
			break;
		case 25:
			fprintf(stderr, "Illegal colour index (negative) ");
			fprintf(stderr, "in Annex E metafile\n");
			break;
		case 26:
			fprintf(stderr, "Illegal style index in Annex E metafile\n");
			break;
		case 27:
			fprintf(stderr, "Illegal integer parameter, positive ");
			fprintf(stderr, "value expected\n");
			fprintf(stderr, "Item number = %d\n", item_no);
			break;
		case 28:
			fprintf(stderr, "Illegal integer parameter, non negative ");
			fprintf(stderr, "value expected]n");
			fprintf(stderr, "Item number = %d\n", item_no);
			break;
		case 29:
			fprintf(stderr, "Illegal real parameter, positive value ");
			fprintf(stderr, "expected\n");
			fprintf(stderr, "Item number = %d\n", item_no);
			break;
		case 30:
			fprintf(stderr, "Illegal real parameter, non negative ");
			fprintf(stderr, "value expected\n");
			fprintf(stderr, "Item number = %d\n", item_no);
			break;
		case 31:
			fprintf(stderr, "Illegal value for Clearing Control Flag ");
			fprintf(stderr, "parameter of Clear Workstation item\n");
			break;
		case 32:
			fprintf(stderr, "Illegal value for deferral mode ");
			fprintf(stderr, "parameter of Deferral Mode item\n");
			break;
		case 33:
			fprintf(stderr, "Illegal value for implicit regeneration ");
			fprintf(stderr, "mode parameter of Deferral Mode item\n");
			break;
		case 34:
			fprintf(stderr, "Illegal value for Text precision\n");
			break;
		case 35:
			fprintf(stderr, "Illegal horizontal alignment parameter ");
			fprintf(stderr, "in Text alignment item\n");
			break;
		case 36:
			fprintf(stderr, "Illegal vertical alignment parameter ");
			fprintf(stderr, "in Text alignment item\n");
			break;
		case 37:
			fprintf(stderr, "Illegal flag in Aspect Source Flags item\n");
			break;
		case 38:
			fprintf(stderr, "Illegal line index in Polyline ");
			fprintf(stderr, "representation item\n");
			break;
		case 39:
			fprintf(stderr, "Illegal line width in Polyline ");
			fprintf(stderr, "representation item\n");
			break;
		case 40:
			fprintf(stderr, "Illegal line colour in Polyline ");
			fprintf(stderr, "representation item\n");
			break;
		case 41:
			fprintf(stderr, "Illegal marker index in Polymarker ");
			fprintf(stderr, "representation item\n");
			break;
		case 42:
			fprintf(stderr, "Illegal marker size in Polymarker ");
			fprintf(stderr, "representation item\n");
			break;
		case 43:
			fprintf(stderr, "Illegal marker colour in Polymarker ");
			fprintf(stderr, "representation item\n");
			break;
		case 44:
			fprintf(stderr, "Illegal text bundle index in Text ");
			fprintf(stderr, "representation item\n");
			break;
		case 45:
			fprintf(stderr, "Illegal text precision in Text ");
			fprintf(stderr, "representation item\n");
			break;
		case 46:
			fprintf(stderr, "Illegal text colour in Text ");
			fprintf(stderr, "representation item\n");
			break;
		case 47:
			fprintf(stderr, "Illegal fill area index in Fill area ");
			fprintf(stderr, "representation item\n");
			break;
		case 48:
			fprintf(stderr, "Illegal interior style in Fill area ");
			fprintf(stderr, "representation item\n");
			break;
		case 49:
			fprintf(stderr, "Illegal fill colour in Fill area ");
			fprintf(stderr, "representation item\n");
			break;
		case 50:
			fprintf(stderr, "Illegal pattern index in Pattern ");
			fprintf(stderr, "representation item\n");
			break;
		default:
			fprintf(stderr, "Illegal input metafile\n");
	}
	exit(1);
}
