/*****************************************************************************
* Routines for encoding vectors into the ZVG command set.
*
* Author:  Zonn Moore
* Created: 11/06/02
*
* History:
*
* (c) Copyright 2002, Zektor, LLC.  All Rights Reserved.
*****************************************************************************/
#include	"zstddef.h"
#include	"zvgCmds.h"
#include	"zvgEnc.h"

static uchar	*ZvgStart;			// Pointer to start of ZVG command buffer
static uchar	*ZvgPtr;				// Pointer to buffer of ZVG commands
static int		XPos, YPos;			// Keep track of current ZVG positions
static uint		CColor;				// Keep track of current ZVG colors

// Routines for sending command bytes to buffer

static void sendColor( int color)
{
	*(ZvgPtr++) = (uchar)(color >> 8);
	*(ZvgPtr++) = (uchar)color;
}

static void sendXY( int xx, int yy)
{
	*(ZvgPtr++) = (uchar)xx;
	*(ZvgPtr++) = (uchar)((xx >> 4) & 0xF0) | ((yy >> 8) & 0x0F);
	*(ZvgPtr++) = (uchar)yy;
}

static void sendLen8( int len)
{
	*(ZvgPtr++) = (uchar)len;
}

static void sendLen12( int len)
{
	*(ZvgPtr++) = (uchar)((len >> 8) & 0x0F);
	*(ZvgPtr++) = (uchar)len;
}

static void sendRatioLen8( int ratio, int len)
{
	*(ZvgPtr++) = (uchar)(ratio >> 8);
	*(ZvgPtr++) = (uchar)len;
}

static void sendRatioLen12( int ratio, int len)
{
	*(ZvgPtr++) = (uchar)(ratio >> 8);
	*(ZvgPtr++) = (uchar)(ratio & 0xF0) | ((len >> 8) & 0x0F);
	*(ZvgPtr++) = (uchar)len;
}

/*****************************************************************************
* Reset globals to that of a just powered on ZVG.
*****************************************************************************/
void zEncodeReset( void)
{
	// Reset ZVG states to same as ZVG at power on.

	XPos = 0;						// set 0
	YPos = 0;						// set 0

	CColor = 0;						// blank all colors
}

/*****************************************************************************
* Set ZVG buffer pointer to the start of a buffer.
*****************************************************************************/
void zEncodeSetPtr( uchar *zvgBfr)
{
	// point to start of a command buffer

	ZvgStart = zvgBfr;			// point to start of buffer
	ZvgPtr = zvgBfr;				// set command pointer to start of buffer
}

/*****************************************************************************
* Return the number of bytes in the ZVG buffer.
*****************************************************************************/
uint	zEncodeSize( void)
{
	return ((uint)(ZvgPtr - ZvgStart));
}

/*****************************************************************************
* Center the trace by sending the "Center" command.
*****************************************************************************/
void	zEncodeCenter( void)
{
	*(ZvgPtr++) = zcCENTER;		// send center command
	XPos = 0;						// center current position
	YPos = 0;
}

/*****************************************************************************
* Routine to encode ZVG commands given rectangular coordinates.
* No color is given.  Color of previous vector command is used.
*
* Called with:
*    xStart = Starting X position of vector to be drawn.
*    yStart = Starting Y position of vector to be drawn.
*    xEnd   = Ending X position of vector to be drawn.
*    yEnd   = Ending Y position of vector to be drawn.
*
* Globals:
*    ZvgPtr = Points to next position in ZvgBfr to place ZVG command.
*    XPos   = Current X position.
*    YPos   = Current Y position.
*    CColor = Current color.
*****************************************************************************/
void zEncodeZvgNC( int xStart, int yStart, int xEnd, int yEnd)
{
	zEncodeZvg( xStart, yStart, xEnd, yEnd, CColor);
}

/*****************************************************************************
* Routine to encode ZVG commands given rectangular coordinates.
*
* Called with:
*    xStart = Starting X position of vector to be drawn.
*    yStart = Starting Y position of vector to be drawn.
*    xEnd   = Ending X position of vector to be drawn.
*    yEnd   = Ending Y position of vector to be drawn.
*    color  = Color of vector.
*
* Globals:
*    ZvgPtr = Points to next position in ZvgBfr to place ZVG command.
*    XPos   = Current X position.
*    YPos   = Current Y position.
*    CColor = Current color.
*****************************************************************************/
void zEncodeZvg( int xStart, int yStart, int xEnd, int yEnd, int color)
{
	int	xLen, yLen, len;
	int	xSign, ySign, vRatio;
	int	zvgCmd;

	// Check to see if color has changed

	if (color != CColor)
		zvgCmd = zbCOLOR;						// indicate color information is to be sent

	else
		zvgCmd = 0;

	// Check for point

	if (xStart == xEnd && yStart == yEnd)
	{
		// check for most efficient coding of POINT data

		// get direction of X and Y axis, and their respective lengths
		// using the current position as the starting point

		if (xStart < XPos)
		{	xSign = 1;							// moves from right to left
			xLen = XPos - xStart;			// get length of X axis
		}
		else
		{	xSign = 0;							// moves from left to right
			xLen = xStart - XPos;			// get length of X axis
		}

		if (yStart < YPos)
		{	ySign = 1;							// moves downward
			yLen = YPos - yStart;			// get length of Y axis
		}
		else
		{	ySign = 0;	 						// moves upward
			yLen = yStart - YPos;			// get length of Y axis
		}

		// Check if NOT a 45 or 90 degree jump.  If it is a 45 or 90
		// degree angle from current position, or distance is less
		// than 128 points, then fall through to send relative command,
		// otherwise send an absolute position commmand and return.

		if (xLen != 0 && yLen != 0 && xLen != yLen)
		{
			// if jump is not 45 or 90 degree, then check length
			// start by finding largest length

			if (xLen >= yLen)
				len = xLen;

			else
				len = yLen;

			// if jump would require sending a 12 bit ratio, send
			// absolute position command instead, since it takes
			// the same number of bytes, but is easier for the ZVG
			// to digest

			if (len > 127)
			{
				zvgCmd |= zbABS;				// indicate absolute positioning

				*(ZvgPtr++) = zvgCmd;

				if (zvgCmd & zbCOLOR)
					sendColor( color);

				sendXY( xStart, yStart);	// send POINT position
				return;							// done sending point, return
			}
		}
	}

	// if not POINT, treat as a vector draw

	else
	{
		zvgCmd |= zbVECTOR;					// indicate a vector is being drawn

		// Check to see if start of this vector is same as current trace position

		if (xStart != XPos || yStart != YPos)
			zvgCmd |= zbABS;					// if not, the starting points must be sent

		// get direction of X and Y axis, and their respective lengths

		if (xEnd < xStart)
		{	xSign = 1;							// moves from right to left
			xLen = xStart - xEnd;			// get length of X axis
		}
		else
		{	xSign = 0;							// moves from left to right
			xLen = xEnd - xStart;			// get length of X axis
		}

		if (yEnd < yStart)
		{	ySign = 1;							// moves downward
			yLen = yStart - yEnd;			// get length of Y axis
		}
		else
		{	ySign = 0;	 						// moves upward
			yLen = yEnd - yStart;			// get length of Y axis
		}
	}

	// Either a vector is being drawn, or a point is being jumped to.
	// Determine the distance, angle and direction of vector or jump.

	// check for 45 degree angles

	if (yLen == xLen)
	{
		// if length fits in 8 bits, send short length

		if (xLen < 256)
			zvgCmd |= zbSHORT;		// use short length

		// send ZVG command, and direction

		*(ZvgPtr++) = zvgCmd | (xSign << 1) | ySign;

		if (zvgCmd & zbCOLOR)
			sendColor( color);

		// if starting points required, send them

		if (zvgCmd & zbABS)
			sendXY( xStart, yStart);

		// if short length, send it

		if (zvgCmd & zbSHORT)
			sendLen8( xLen);
	
		// else, send long length

		else
			sendLen12( xLen);
	}

	// check for horizontal line

	else if (yLen == 0)
	{
		// if length fits in 8 bits, use short version of command

		if (xLen < 256)
			zvgCmd |= zbSHORT;			// use short length

		// send command

		*(ZvgPtr++) = zvgCmd | zbHZVT | xSign;

		// send color if needed

		if (zvgCmd & zbCOLOR)
			sendColor( color);

		// send starting positions if needed

		if (zvgCmd & zbABS)
			sendXY( xStart, yStart);

		// send length, short or long

		if (zvgCmd & zbSHORT)
			sendLen8( xLen);
	
		else
			sendLen12( xLen);
	}

	// check for vertical line

	else if (xLen == 0)
	{
		// if length fits in 8 bits, use short version of command

		if (yLen < 256)
			zvgCmd |= zbSHORT;			// use short length

		// send command

		*(ZvgPtr++) = zvgCmd | zbHZVT | zbVERT | ySign;

		// send color if needed

		if (zvgCmd & zbCOLOR)		
			sendColor( color);

		// send starting positions if needed

		if (zvgCmd & zbABS)
			sendXY( xStart, yStart);

		// send length, short or long

		if (zvgCmd & zbSHORT)
			sendLen8( yLen);
	
		else
			sendLen12( yLen);
	}

	// If not 45 degree angle, vertical, or horizontal, then ratio must be
	// calculated.

	else
	{	zvgCmd |= zbRATIO;					// indicate ratio being sent

		// is X length greater the Y length?

		if (yLen < xLen)
		{
			// if length can fit in 7 bits, send short version of command

			if (xLen < 128)
				zvgCmd |= zbSHORT;			// use short length

			// send command

			*(ZvgPtr++) = zvgCmd | (xSign << 1) | ySign;

			// calculate ratio

			vRatio = (unsigned int)(((long)yLen << 16) / xLen);

			// send color if needed
		
			if (zvgCmd & zbCOLOR)
				sendColor( color);

			// send start positions if needed

			if (zvgCmd & zbABS)
				sendXY( xStart, yStart);

			// send length and ratio, short or long

			if (zvgCmd & zbSHORT)
				sendRatioLen8( vRatio, xLen);
	
			else
				sendRatioLen12( vRatio, xLen);
		}

		// Else, Y length is greater than X length.

		else
		{
			// if length can fit in 7 bits, send short version of command

			if (yLen < 128)
				zvgCmd |= zbSHORT;			// use short length

			// send command

			*(ZvgPtr++) = zvgCmd | zbYLEN | (xSign << 1) | ySign;

			// calculate ratio

			vRatio = (unsigned int)(((long)xLen << 16) / yLen);

			// send color if needed
		
			if (zvgCmd & zbCOLOR)
				sendColor( color);

			// send start positions if needed

			if (zvgCmd & zbABS)
				sendXY( xStart, yStart);

			// send length and ratio, short or long

			if (zvgCmd & zbSHORT)
				sendRatioLen8( vRatio, yLen);
	
			else
				sendRatioLen12( vRatio, yLen);
		}
	}
	XPos = xEnd;							// new position is end of vector
	YPos = yEnd;
	CColor = color;						// save new color
}
