#include "jiff.h"

struct IORequest *CreateIOTask(length,tsk)
int length;
struct Task *tsk;
{
struct IORequest *ioreq;
struct MsgPort *port;

   ioreq = AllocMem((long) length,MEMF_CLEAR|MEMF_PUBLIC);
   if (ioreq == NULL)
      return(NULL);
   port = AllocMem((long) sizeof(struct MsgPort),MEMF_CLEAR|MEMF_PUBLIC);
   port->mp_Node.ln_Name = "WIPE MSGPORT";
   port->mp_Node.ln_Pri = 0;
   port->mp_Node.ln_Type = NT_MSGPORT;
   port->mp_Flags = PA_SIGNAL;
   port->mp_SigBit = 31;
   port->mp_SigTask = tsk;
   tsk->tc_SigAlloc = 0x80000000;
   AddPort(port);
   ioreq->io_Message.mn_Node.ln_Type = NT_MESSAGE;
   ioreq->io_Message.mn_Length = length;
   ioreq->io_Message.mn_ReplyPort = port;
   return(ioreq);
}

DeleteIOPort(port)
struct MsgPort *port;
{
   RemPort(port);
   FreeMem(port,(LONG) sizeof(*port));
}

CycleScreen(scrn,info)
struct Screen *scrn;
struct ILBM_info *info;
{
int t0;

   for (t0 = 0; t0 != 6; t0++)
      if (info->crng[t0].rate && info->crng[t0].active)
         if (info->crng[t0].low != info->crng[t0].high)
            {
            if (info->crng[t0].active == 3)
               AddCringe(t0,(int) info->crng[t0].high,
                  (int) info->crng[t0].low,info->crng[t0].rate,
                  &scrn->ViewPort);
            else
               AddCringe(t0,(int) info->crng[t0].low,
                  (int) info->crng[t0].high,info->crng[t0].rate,
                  &scrn->ViewPort);
            }
}

void CringeTimer();
struct Task *CringeTask[6] = {0,0,0,0,0,0};
struct timerequest *CringeReq[6];
int start[6],finish[6];
long seclist[6],miclist[6];
struct ViewPort *sport;

AddCringe(num,st,fin,rate,port)
int num,st,fin;
UWORD rate;
struct ViewPort *port;
{
APTR Stack;
double delwk;
long secs,micros;

   sport = port;
   start[num] = st;
   finish[num] = fin;
   delwk = (16384.0/rate)*16666.666667;
   secs = (delwk/1000000.0);
   micros = ((long) delwk) % 1000000;
   CringeTask[num] = (struct Task *) AllocMem((long) sizeof(struct Task),
         MEMF_PUBLIC|MEMF_CLEAR);
   if (!CringeTask[num])
      return;
   Stack = (APTR) AllocMem(1024L,MEMF_CLEAR);
   if (!Stack)
      {
      FreeMem(CringeTask[num],(long) sizeof(struct Task));
      return;
      }
   CringeReq[num] = (struct timerequest *)
      CreateIOTask(sizeof(struct timerequest),CringeTask[num]);
   CringeReq[num]->tr_node.io_Command = TR_ADDREQUEST;
   OpenDevice(TIMERNAME,UNIT_MICROHZ,CringeReq[num],0L);
   seclist[num] = secs;
   miclist[num] = micros;
   CringeTask[num]->tc_SPLower = Stack;
   CringeTask[num]->tc_SPUpper = (APTR) (1024+(ULONG) Stack);
   CringeTask[num]->tc_SPReg = CringeTask[num]->tc_SPUpper;
   CringeTask[num]->tc_Node.ln_Type = NT_TASK;
   CringeTask[num]->tc_Node.ln_Pri = 1;
   CringeTask[num]->tc_Node.ln_Name = "Cringing";
   AddTask(CringeTask[num],CringeTimer,0L);
}

StopCringe()
{
int num;

  for (num = 0; num != 6; num++)
   if (CringeTask[num])
      {
      RemTask(CringeTask[num]);
      FreeMem(CringeTask[num]->tc_SPLower,1024L);
      FreeMem(CringeTask[num],(long) sizeof(struct Task));
      AbortIO(CringeReq[num]);
      CloseDevice(CringeReq[num]);
      DeleteIOPort(CringeReq[num]->tr_node.io_Message.mn_ReplyPort);
      DeleteExtIO(CringeReq[num],(long) sizeof(struct timerequest));
      CringeTask[num] = 0;
      }
}

void CringeTimer()
{
long st,fi,t0,num,sc,mc;
struct ColorMap *smap;
struct Task *mytask;

   geta4();
   mytask = FindTask(0L);
   for (num = 0; num != 6; num++)
      if (CringeTask[num] == mytask)
         break;
   st = start[num];
   fi = finish[num];
   sc = seclist[num];
   mc = miclist[num];
   smap = sport->ColorMap;
   FOREVER
      {
      CringeReq[num]->tr_time.tv_secs = sc;
      CringeReq[num]->tr_time.tv_micro = mc;
      DoIO(CringeReq[num]);
      if (st < fi)
         FWrap(smap,st,fi);
      else
         BWrap(smap,fi,st);
      }
}

FWrap(cmap,s,f)
struct ColorMap *cmap;
long s,f;
{
long t0;
UWORD a,b;

   Forbid();
   a = GetRGB4(cmap,f);
   for (t0 = f-1; t0 >= s; t0--)
      {
      b = GetRGB4(cmap,t0);
      SetRGB4(sport,t0+1L,(b >> 8) & 0x0fL,(b >> 4) & 0x0fL,b & 0x0fL);
      }
   SetRGB4(sport,s,(a >> 8) & 0x0fL,(a >> 4) & 0x0fL,a & 0x0fL);
   Permit();
}

BWrap(cmap,s,f)
struct ColorMap *cmap;
long s,f;
{
long t0;
UWORD a,b;

   Forbid();
   a = GetRGB4(cmap,s);
   for (t0 = s+1; t0 <= f; t0++)
      {
      b = GetRGB4(cmap,t0);
      SetRGB4(sport,t0-1L,(b >> 8) & 0x0fL,(b >> 4) & 0x0fL,b & 0x0fL);
      }
   SetRGB4(sport,f,(a >> 8) & 0x0fL,(a >> 4) & 0x0fL,a & 0x0fL);
   Permit();
}

WipeOut(vp,rp,cmap,ncmap,mx,my,topcol)
UWORD *cmap;
UBYTE *ncmap;
struct RastPort *rp;
struct ViewPort *vp;
int topcol;
long mx,my;
{
int col;
long y,r,g,b;

   SetAPen(rp,0L);
   for (y = 0; y != my; y++)
      RectFill(rp,0L,y,mx-1,y);
   for (col = 0; col != topcol; col++)
      {
      r = ncmap[col*3]; g = ncmap[col*3+1]; b = ncmap[col*3+2];
      SetRGB4(vp,(long) col,r,g,b);
      cmap[col] = (r << 8) | (g << 4) | b;
      }
}
