/* $Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1990 by MXM
 *
 *	Name .....: Cycling.c
 *	Created ..: Saturday 23-Sep-90 12:30
 *	Revision .: 2
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	11-May-90       Olsen           Rework for Aztec 5.0 release
 *	23-Sep-89       Olsen           Created this file!
 *
 * $Revision Header *********************************************************
 *
 *	This file contains subroutines handling the colour cycling employed
 *	by LoadImage.c
 *
 ***************************************************************************/

	/* LoadCycleRange needs these macros. */

#define CRNG_NORATE	(36)		/* Don't cycle this range. */
#define CRNG_ACTIVE	(1 << 0)	/* This range is active. */
#define CRNG_REVERSE	(1 << 1)	/* This range is cycling backwards. */

	/* The following values are to control the interrupt code. */

#define CY_CYCL (0)	/* Keep on cycling. */
#define CY_WAIT	(1)	/* Don't cycle, wait for startup message. */

	/* CycleCode needs this data, InitCycleCode prepares it. */

static struct ViewPort	*VPort;			/* Where to cycle. */
static struct Interrupt	*VBlank;		/* The Cycling-interrupt. */
static CRange		*Range;			/* The cycling-ranges. */
static LONG		 RangeNum;		/* The number of ranges. */
static UWORD		 ColMap[32];		/* The colourmap. */
static LONG		 ColNum;		/* The number of colours. */
static SHORT		 TicksPassed[16];	/* Event counter for ranges. */
static UWORD		 TempCol;		/* Temporary colour buffer. */
static UWORD		 UndoBuffer[32];	/* Original palette buffer. */
static SHORT		 WeAreWaiting;		/* CycleCode is waiting. */
static SHORT		 Command;		/* What is the CycleCode up to do? */
static LONG		 CodeRun = FALSE;	/* Is the CycleCode already running? */

	/* CycleCode() :
	 *
	 *	CycleCode is the 'C' cycling routine. It runs as an
	 *	interrupt server to save task time.
	 */

static LONG
CycleCode()
{
	SHORT i,j;	/* Loop counter. */

	int_start();

		/* Are we to wait? */

	if(Command == CY_WAIT)
	{
			/* Restore the original palette. */

		if(!WeAreWaiting)
			LoadRGB4(VPort,UndoBuffer,ColNum);

		WeAreWaiting = TRUE;

		int_end();

			/* Don't continue to cycle. */

		return(0);
	}

		/* We aren't waiting any more. */

	if(WeAreWaiting)
	{
			/* Re-initialize the palette. */

		for(i = 0 ; i < ColNum ; i++)
			ColMap[i] = UndoBuffer[i];

			/* Reset event counters. */

		for(i = 0 ; i < RangeNum ; i++)
			TicksPassed[i] = 0;

		WeAreWaiting = FALSE;
	}

		/* Now handle the cycle ranges. */

	for(i = 0 ; i < RangeNum ; i++)
	{
			/* Increment event counter. */

		TicksPassed[i]++;

			/* Is this one up to cycle next? */

		if(TicksPassed[i] == Range[i] . rate)
		{
				/* Reset event counter for this range. */

			TicksPassed[i] = 0;

				/* Is this range active? */

			if(!(Range[i] . active & CRNG_ACTIVE))
				continue;

				/* Cycling backwards? */

			if(Range[i] . active & CRNG_REVERSE)
			{
					/* Move the colours. */

				TempCol = ColMap[Range[i] . low];

				for(j = Range[i] . low ; j < Range[i] . high ; j++)
					ColMap[j] = ColMap[j + 1];

				ColMap[Range[i] . high] = TempCol;
			}
			else
			{
					/* This one is cycling forwards. */

				TempCol = ColMap[Range[i] . high];

				for(j = Range[i] . high ; j > Range[i] . low ; j--)
					ColMap[j] = ColMap[j - 1];

				ColMap[Range[i] . low] = TempCol;
			}

				/* Okay, everything has been moved, now
				 * load the new palette.
				 */

			LoadRGB4(VPort,ColMap,ColNum);
		}
	}

	int_end();

	return(0);
}

	/* InitCycleCode(ViewPort,ColMap,ColNum,Range,RangeNum) :
	 *
	 *	Initializes all data and starts up the interrupt
	 *	server.
	 */

LONG
InitCycleCode(struct ViewPort *Cy_ViewPort,UWORD *Cy_ColMap,LONG Cy_ColNum,CRange *Cy_Range,LONG Cy_RangeNum)
{
	SHORT i;

		/* Check for valid parameters. */

	if(!Cy_ViewPort || !Cy_ColMap || !Cy_ColNum || !Cy_Range || !Cy_RangeNum)
		return(FALSE);

		/* See if we can allocate the Interrupt structure. */

	if(!(VBlank = (struct Interrupt *)AllocMem(sizeof(struct Interrupt),MEMF_PUBLIC | MEMF_CLEAR)))
		return(FALSE);

		/* Copy the data. */

	VPort		= Cy_ViewPort;
	ColNum		= Cy_ColNum;
	Range		= Cy_Range;
	RangeNum	= Cy_RangeNum;

	for(i = 0 ; i < ColNum ; i++)
		ColMap[i] = Cy_ColMap[i];

		/* Clear event counters. */

	for(i = 0 ; i < Cy_RangeNum ; i++)
		TicksPassed[i] = 0;

		/* Copy the original palette to the undo buffer. */

	for(i = 0 ; i < Cy_ColNum ; i++)
		UndoBuffer[i] = Cy_ColMap[i];

		/* Let the CycleCode wait. */

	Command		= CY_WAIT;
	WeAreWaiting	= TRUE;

		/* Ta Da! The CycleCode enters the stage!. */

	VBlank -> is_Code		= CycleCode;
	VBlank -> is_Node . ln_Type	= NT_INTERRUPT;
	VBlank -> is_Node . ln_Name	= "Cycling Interrupt";

	AddIntServer(INTB_VERTB,VBlank);

	CodeRun = TRUE;

	return(TRUE);
}

	/* ClearCycleCode() :
	 *
	 *	Forces the CycleCode to hit and quit.
	 */

VOID
ClearCycleCode()
{
	SHORT i;

		/* Is the interrupt already running? */

	if(CodeRun && VBlank)
	{
			/* Tell it to wait. */

		WeAreWaiting	= TRUE;
		Command		= CY_WAIT;

			/* Remove the server code. */

		RemIntServer(INTB_VERTB,VBlank);

		FreeMem(VBlank,sizeof(struct Interrupt));

			/* Rebuild palette. */

		for(i = 0 ; i < ColNum ; i++)
			ColMap[i] = UndoBuffer[i];

			/* Wait for it to finish. */

		WaitTOF();
		WaitTOF();
		WaitTOF();

			/* Close the shop. */

		CodeRun = FALSE;
	}
}

	/* ToggleCycleCode() :
	 *
	 *	Toggles the activity of the cycling code.
	 */

VOID
ToggleCycleCode()
{
	static BYTE Mode = FALSE;

	Mode ^= TRUE;

	if(Mode)
		Command = CY_CYCL;
	else
		Command = CY_WAIT;

	WaitTOF();
}

	/* IsCycling() :
	 *
	 *	Returns the present status of the cycling code.
	 */

LONG
IsCycling()
{
	if(Command == CY_CYCL)
		return(TRUE);

	if(Command == CY_WAIT)
		return(FALSE);
}

	/* LoadCycleRange(FileName,Range,MaxRange) :
	 *
	 *	Searches the IFF-ILBM-file for CRNG or CCRT
	 *	chunks und initializes the colour-ranges.
	 */

LONG
LoadCycleRange(char *FileName,CRange *Range,LONG MaxRange)
{
	FILE	*CycFile;
	SHORT	 i;

		/* GraphiCraft private cycling chunk. */

	struct
	{
		WORD direction;		/* Which direction? */
		UBYTE start;		/* First colour. */
		UBYTE end;		/* Last colour. */
		LONG seconds;		/* Wait time. */
		LONG microseconds;	/* Wait time. */
		WORD pad;		/* Expansion/padding byte. */
	} CcrtChunk;

		/* Clear all ranges. */

	for(i = 0 ; i < MaxRange ; i++)
		Range[i] . active = 0;

		/* Open the file and try to locate the CRNG chunk. */

	if(!(CycFile = fopen(FileName,"r")))
		return(FALSE);

	if(FindChunk("CRNG",CycFile))
	{
			/* Read all ranges. */

		for(i = 0 ; i < MaxRange ; i++)
		{
			fread(&Range[i],sizeof(CRange),1,CycFile);

				/* Carefully determine if this chunk is
				 * active.
				 */

			if(Range[i] . active == CRNG_NORATE || !Range[i] . rate || Range[i] . low == Range[i] . high)
				Range[i] . active = 0;

				/* Recalculate speed value. */

			if(Range[i] . rate > 0)
				Range[i] . rate = 16384 / Range[i] . rate;
			else
				Range[i] . rate = 0;

				/* Finished reading the chunks? */

			if(!FindChunk("CRNG",CycFile))
			{
				i++;
				break;
			}
		}
	}
	else
	{
			/* Didn't find a CRNG chunk, try to find a
			 * CCRT chunk.
			 */

		fseek(CycFile,0,0);

		if(FindChunk("CCRT",CycFile))
		{
			for(i = 0 ; i < MaxRange ; i++)
			{
				fread(&CcrtChunk,sizeof(CcrtChunk),1,CycFile);

					/* We have located a CCRT chunk, now
					 * make it a CRNG chunk.
					 */

				Range[i] . low	= CcrtChunk . start;
				Range[i] . high	= CcrtChunk . end;

				if(CcrtChunk . direction != 0)
					Range[i] . active = CRNG_ACTIVE;
				else
					Range[i] . active = 0;

				if(CcrtChunk . direction > 0)
					Range[i] . active |= CRNG_REVERSE;

					/* Recalculate speed (by
					 * Carolyn Scheppner).
					 */

				Range[i] . rate	= 16384 / (CcrtChunk . seconds * 60 + (CcrtChunk . microseconds + 8334) / 16667);

					/* See if it is active. */

				if(!Range[i] . rate || Range[i] . low == Range[i] . high)
					Range[i] . active = 0;

					/* Recalculate speed value. */

				if(Range[i] . rate > 0)
					Range[i] . rate = 16384 / Range[i] . rate;
				else
					Range[i] . rate = 0;

					/* See if there is another chunk. */

				if(!FindChunk("CCRT",CycFile))
				{
					i++;
					break;
				}
			}
		}
		else
		{
				/* Seems that we failed. */

			fclose(CycFile);
			return(FALSE);
		}
	}

	fclose(CycFile);

	return(i);
}
