/* cycle.c */

/* Copyright (c) 1988 David J. Arendash, Inc. */

/* This file contains general purpose routines for the handling of
   multiple timer requests useful in applications requiring things like
   color cycling.  The actual color cycle routine I use is include here
   in a comment, as you may want to tailor it for your application.

   These routines rely on routines in timer.c, also by me.

   You may want to break this file up to library-ize these routines.

   Feel free to do anything you want with this stuff.  I only ask that
	1.  Don't delete these comments
	2.  Follow this indentation and punctuation, as it is the least
	    annoying and easiest to follow, all pinheads aside.
*/

#include "exec/types.h"
#include "exec/nodes.h"
#include "exec/lists.h"
#include "exec/memory.h"
#include "exec/interrupts.h"
#include "exec/ports.h"
#include "exec/libraries.h"
#include "exec/tasks.h"
#include "exec/io.h"
#include "exec/devices.h"
#include "devices/timer.h"
#include <stdio.h>

/* 4 color cycles in most paint programs */
#define CYCS 4

/* used in test routine only */
static char  x[6] = {"0000",13};

struct timeval cyc_time[CYCS];
struct IOStdReq *cyc_msg[CYCS], *prototimer;

/* keep track of the existance of timer(s) */
char TimerOn = 0;

extern struct IOStdReq *CreateTimer();

/*

This is used for testing

main ()
{
   SetCycleTime (3, 3000L);
   SetCycleTime (1, 1500L);
   SetCycleTime (0, 60L);
   SetCycleTime (3, 24L);

   if (!SetupCycles())
      exit (0);

   StartCycles ();
   while (PerformCycles());
   StopCycles();
   DestroyCycles();
}
*/

/* sets a timer period based on the (IFF) convention that 16384 = 1/60 sec and
   273 = 1 sec.
   Does not start a timer */

SetCycleTime (index, period)
short index;
long  period;
{
	long	t;

	t = 273066667 / period;
	cyc_time[index].tv_secs = t / 1000000;
	cyc_time[index].tv_micro = t - (cyc_time[index].tv_secs * 1000000);
}

/* Perform one-time initialization for using timers and cycles */
SetupCycles ()
{
	short i;

	/* create a prototype timer so we can InitTimerDevice */
	prototimer = CreateTimer ();
	if (prototimer == NULL)
		return (0);

 	if (!InitTimerDevice (prototimer, (long)UNIT_VBLANK))
		return (0);

	/* create timers for cycling based on the prototype timer */
	for (i=0; i < CYCS; i++)
	{
		cyc_msg[i] = CreateTimer();
		DupTimer (prototimer, cyc_msg[i]);
	}
}

/* start all timers to get the balls rolling */
StartCycles()
{
	short	i;

	for (i=0; i < CYCS; i++)
	{
		QTimer_tv (cyc_msg[i], &cyc_time[i]);
	}
	TimerOn = 1;
}

/* Perform the action associated with the timers.
   Call as often as possible (like while waiting for a message). */
PerformCycles ()
{
	short i;

	if (!TimerOn)
		return (0);

	for (i=0; i < CYCS; i++)
	{
		/* timer ready? */
		if (CheckTimer (cyc_msg[i]))
		{
			/* call the actual "thing-to-do" */
			CycleRange(i);
			/* restart timer */
			QTimer_tv (cyc_msg[i], &cyc_time[i]);
		}
	}
	return (1);
}

/* stops all timers */
StopCycles()
{
	short	i;

	for (i=0; i < CYCS; i++)
	{
		AbortTimer (cyc_msg[i]);
	}
	TimerOn = 0;
}

/* shut down all timer activity */
DestroyCycles ()
{
	short	i;

	CloseDevice (prototimer);
	for (i=0; i < CYCS; i++)
	{
		DeleteTimer (cyc_msg[i]);
	}
	DeleteTimer (prototimer);
}

/* Do what you want based on the timer.  This is a test routine.  You
   Supply your own */
CycleRange (i)
short i;
{

	x[4] = 13;
	x[5] = 0;

	if (x[i] == '0')
		x[i] = '1';
	else
		x[i] = '0';

	printf ("%s",x);
	fflush (stdout);
}

#ifdef DoingItMyWay

/* These are the routines I use to perform color cycling.
   It assumes you have an array of shorts containing the color pallet (cmap)
   CRange is an IFF chunk and defined as follows:
struct CRange
{
	WORD	pad1;
	WORD	rate;
	WORD	active;
	UBYTE	low, high;
};

   These routines assume (as Deluxe Paint does (in a non-standard way))
   that a cycle is on if the rate is greater than 24 (Hex) and the
   low color is different than the high color.  Had they done it according
   to the IFF spec, active would be non-zero if the range is active.

ShiftRange (vp, cmap, range)
struct Viewport *vp;
short *cmap;
struct CRange *range;
{

	short i,j;
	long	tcol;

	i = range->high;
	j = range->low;

	/* 1 if cylcing low to high */
	/* 3 if cycling high to low (backward) */
	if (range->active == 1)
	{
		tcol = cmap[i];
		for (; i > j; i--)
		{
			cmap[i] = cmap[i-1];
		}
		cmap[j] = tcol;
	}
	else if (range->active == 3)
	{
		tcol = cmap[j];
		for (; j < i; j++)
		{
			cmap[j] = cmap[j+1];
		}
		cmap[i] = tcol;
	}
	LoadRGB4 (vp, cmap, (long)range->high);
}

CycleRange (index)
short index;
{
	/* only cycle if range is active (according to DPaint) */
	if (ColorRange[index].rate < 0x25)
		return (0);
	if (ColorRange[index].low == ColorRange[index].high)
		return (0);
	ShiftRange (&AppScreen->ViewPort, &new_cmap, &ColorRange[index]);

}
#endif
