/* spline Library */

#include <exec/memory.h>
#include <exec/execbase.h>
#include <libraries/iffparse.h>
#include <utility/tagitem.h>
#include <graphics/gfxbase.h>
#include <intuition/intuitionbase.h>

#include <clib/macros.h>

#define __USE_SYSBASE 42

#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/utility.h>

#include <math.h>

#include <BTD.h>

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Library *UtilityBase;

/* #define DEBUG YES */

#ifdef DEBUG 

void KPrintF(char *,...);

#define DEBUG_PRINTF(a,b)  KPrintF(a,b);
#define DEBUG_PRINT(a)     KPrintF(a)
#else
#define DEBUG_PRINTF(a,b)
#define DEBUG_PRINT(a)
#endif


#define QTAG(o) (BTD_Client+(o))

#define SP_Seconds QTAG(0)
#define SP_Colors  QTAG(2)

#define MAX_SECONDS 2048L /* seconds until a new graphic is plotted */

#define DEF_SECONDS 128L

#define DEF_COLORS 8L
#define MAX_COLORS 255L

struct BTDInteger splineIntParams[] =
 {
  SP_Seconds,"Patternchange",BTDPT_INTEGER,DEF_SECONDS,1L,MAX_SECONDS,TRUE,
  SP_Colors,"Colors",BTDPT_INTEGER,DEF_COLORS,4L,MAX_COLORS,TRUE
 };

struct BTDNode *splineParams[] = 
 {
  &splineIntParams[0].BI_Node,
  &splineIntParams[1].BI_Node,NULL
 };

struct BTDInfo splineInfo =
 {
  BTDI_Revision,MAKE_ID('S','P','L','I'),
  "Splinefun","Yet another standard modul","Markus Illenseer 1995",
  splineParams
 };

char MyBlankerName[] = "spline.btd";
char MyBlankerID[]   = "Spline Fun Blanker V" VERSION "." REVISION " for BTD";


LONG MyBlankerLibInit(void)

{
 if (GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",37L))
  {
   if (IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",37L))
    {
     if (UtilityBase=OpenLibrary("utility.library",37L)) return TRUE;

     CloseLibrary (&IntuitionBase->LibNode);
    }
   CloseLibrary (&GfxBase->LibNode);
  }
 return FALSE;
}

void MyBlankerLibFree(void)

{
 CloseLibrary (UtilityBase);
 CloseLibrary (&IntuitionBase->LibNode);
 CloseLibrary (&GfxBase->LibNode);
}


typedef struct {
  LONG  x,y;
} point;

#define MAXPOINTS 99
#define MAX_DELTA 3
#define MAX_SPLINES 2048

#define SPLINE_THRESH 5

struct splineStruct
 {
  struct BTDDrawInfo *BTDDrawInfo;
  LONG   hs_RandN,hs_RandF,hs_RandI;
  int colors;
  int		width;
  int		height;
  int		color;
  int		points;
  int         generation;
  int x[MAXPOINTS], y[MAXPOINTS], dx[MAXPOINTS], dy[MAXPOINTS];
  int ncolors;
  int seconds;
 };


struct BTDInfo *QueryMyBlanker(void)

{
 return &splineInfo;
}


void __regargs InitRandom(struct splineStruct *SP,ULONG Instance)

{
 ULONG Time[2];

 CurrentTime (&Time[0],&Time[1]);
 SP->hs_RandN=(LONG)Time[0];
 if (Time[1]<1024L) Time[1]|=1;
 else Time[1]>>=10;
 Time[1]^=Instance;

 SP->hs_RandF=4*Time[1]+1;
 SP->hs_RandI=2*Time[1]+1;
}

WORD __regargs Random(struct splineStruct *SP,WORD Max)

{
 SP->hs_RandN=SP->hs_RandF*SP->hs_RandN+SP->hs_RandI;
 if (SP->hs_RandN<0L) SP->hs_RandN=-SP->hs_RandN;

 return (WORD)(SP->hs_RandN%Max);
}


#define FindTagData(l,t,d) GetTagData((t),(d),(l))
/* rainbow colors */

#define NUM_RAINBOW_COLORS 6

UBYTE RBRed[NUM_RAINBOW_COLORS+1]   = {0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF};
UBYTE RBGreen[NUM_RAINBOW_COLORS+1] = {0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00};
UBYTE RBBlue[NUM_RAINBOW_COLORS+1]  = {0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00};

/* spline routine. */

struct splineStruct *InitMyBlanker(struct TagItem *TagList)
{
 LONG i;
 struct splineStruct *SP;
 struct BTDDrawInfo *BTDDrawInfo;
 ULONG *Error,Dummy,Index,Instance;

 if ((BTDDrawInfo=(struct BTDDrawInfo *)
                   FindTagData(TagList,BTD_DrawInfo,NULL))==NULL) return NULL;
 Error=(LONG *)FindTagData(TagList,BTD_Error,(ULONG)&Dummy);
 if ((SP=AllocVec(sizeof(struct splineStruct),MEMF_PUBLIC|MEMF_CLEAR))==NULL)
  {
   *Error=BTDERR_Memory;
   return NULL;
  }

 SP->seconds=FindTagData(TagList,SP_Seconds,DEF_SECONDS);
 SP->ncolors=FindTagData(TagList,SP_Colors,DEF_COLORS);
 Instance=FindTagData(TagList,BTD_Instance,0L);

 InitRandom(SP,Instance);

 SP->BTDDrawInfo=BTDDrawInfo;
 SP->width=BTDDrawInfo->BDI_Width;
 SP->height=BTDDrawInfo->BDI_Height;

 SP->points = Random(SP,5-3) + 3;
 SP->generation = 0;

    for ( i = 0; i < SP->points; ++i )
        {
        SP->x[i] = Random(SP,SP->width);
        SP->y[i] = Random(SP,SP->height);
        SP->dx[i] = Random(SP,MAX_DELTA * 2 ) - MAX_DELTA;
        if ( SP->dx[i] <= 0 ) --SP->dx[i];
        SP->dy[i] = Random(SP,MAX_DELTA * 2 ) - MAX_DELTA;
        if ( SP->dy[i] <= 0 ) --SP->dy[i];
        }
    
    SP->color = 0;


 if (SP->ncolors>NUM_RAINBOW_COLORS)
  {
   LONG ColNum,Col,RBCol;
   UBYTE *Red,*Green,*Blue,*Changed;

   Red=BTDDrawInfo->BDI_Red;
   Green=BTDDrawInfo->BDI_Green;
   Blue=BTDDrawInfo->BDI_Blue;
   Changed=BTDDrawInfo->BDI_Changed;
   ColNum=SP->ncolors/NUM_RAINBOW_COLORS+1L;
   Index=0L;
   for (RBCol=0L; RBCol<NUM_RAINBOW_COLORS; RBCol++)
    {
     if (RBCol==(SP->ncolors%NUM_RAINBOW_COLORS)) ColNum--;

     for (Col=0L; Col<ColNum; Col++)
      {
       Red[BTDDrawInfo->BDI_Pens[Index]]=RBRed[RBCol]+((RBRed[RBCol+1]-RBRed[RBCol])*Col)/ColNum;
       Green[BTDDrawInfo->BDI_Pens[Index]]=RBGreen[RBCol]+((RBGreen[RBCol+1]-RBGreen[RBCol])*Col)/ColNum;
       Blue[BTDDrawInfo->BDI_Pens[Index]]=RBBlue[RBCol]+((RBBlue[RBCol+1]-RBBlue[RBCol])*Col)/ColNum;
       Changed[BTDDrawInfo->BDI_Pens[Index++]]=TRUE;
      }
    }
  }
 else
  for (Index=0L; Index<SP->ncolors; Index++)
   {
    BTDDrawInfo->BDI_Red[BTDDrawInfo->BDI_Pens[Index]]=RBRed[Index];
    BTDDrawInfo->BDI_Green[BTDDrawInfo->BDI_Pens[Index]]=RBGreen[Index];
    BTDDrawInfo->BDI_Blue[BTDDrawInfo->BDI_Pens[Index]]=RBBlue[Index];
    BTDDrawInfo->BDI_Changed[BTDDrawInfo->BDI_Pens[Index]]=TRUE;
   }

DEBUG_PRINT("spline: Init ready\n");

 return SP;
}

void EndMyBlanker(struct splineStruct *SP)

{
DEBUG_PRINT("spline: FreeMem\n");
 FreeVec (SP);
}

void initspline(struct splineStruct *SP)
{

 int i;

 SetRast(SP->BTDDrawInfo->BDI_RPort,0);

 SP->points = Random(SP,5-3) + 3;
 SP->generation = 0;

    for ( i = 0; i < SP->points; ++i )
        {
        SP->x[i] = Random(SP,SP->width);
        SP->y[i] = Random(SP,SP->height);
        SP->dx[i] = Random(SP,MAX_DELTA * 2 ) - MAX_DELTA;
        if ( SP->dx[i] <= 0 ) --SP->dx[i];
        SP->dy[i] = Random(SP,MAX_DELTA * 2 ) - MAX_DELTA;
        if ( SP->dy[i] <= 0 ) --SP->dy[i];
        }
    
    SP->color = 0;
}

void XDrawLine(struct splineStruct *SP, int x0, int y0, int x1, int y1)
{
  Move(SP->BTDDrawInfo->BDI_RPort,x0,y0);
  Draw(SP->BTDDrawInfo->BDI_RPort,x1,y1);
}

void XDrawSpline(struct splineStruct *SP, int x0, int y0, int x1, int y1, int x2, int y2)
{
    register int xa, ya, xb, yb, xc, yc, xp, yp;

    xa = (x0 + x1) / 2;
    ya = (y0 + y1) / 2;
    xc = (x1 + x2) / 2;
    yc = (y1 + y2) / 2;
    xb = (xa + xc) / 2;
    yb = (ya + yc) / 2;

    xp = (x0 + xb) / 2;
    yp = (y0 + yb) / 2;
    if (ABS(xa - xp) + ABS(ya - yp) > SPLINE_THRESH)
        XDrawSpline(SP, x0, y0, xa, ya, xb, yb);
    else
        XDrawLine(SP, x0, y0, xb, yb);

    xp = (x2 + xb) / 2;
    yp = (y2 + yb) / 2;
    if (ABS(xc - xp) + ABS(yc - yp) > SPLINE_THRESH)
        XDrawSpline(SP, xb, yb, xc, yc, x2, y2);
    else
        XDrawLine(SP, xb, yb, x2, y2);
}

void AnimMyBlanker(struct splineStruct *SP)

{
  int i, t, px, py, zx, zy, nx, ny;

    /* Move the points. */
    for ( i = 0; i < SP->points; i++ )
        {
        for ( ; ; )
            {
            t = SP->x[i] + SP->dx[i];
            if ( t >= 0 && t < SP->width ) break;
            SP->dx[i] = Random(SP, MAX_DELTA * 2 ) - MAX_DELTA;
            if ( SP->dx[i] <= 0 ) --SP->dx[i];
            }
        SP->x[i] = t;
        for ( ; ; )
            {
            t = SP->y[i] + SP->dy[i];
            if ( t >= 0 && t < SP->height ) break;
            SP->dy[i] = Random(SP,MAX_DELTA * 2 ) - MAX_DELTA;
            if ( SP->dy[i] <= 0 ) --SP->dy[i];
            }
        SP->y[i] = t;
        }

    /* Draw the figure. */
    px = zx = ( SP->x[0] + SP->x[SP->points-1] ) / 2;
    py = zy = ( SP->y[0] + SP->y[SP->points-1] ) / 2;
    for ( i = 0; i < SP->points-1; ++i )
        {
        nx = ( SP->x[i+1] + SP->x[i] ) / 2;
        ny = ( SP->y[i+1] + SP->y[i] ) / 2;
        XDrawSpline(SP,px, py, SP->x[i], SP->y[i], nx, ny );
        px = nx;
        py = ny;
        }
    SetAPen(SP->BTDDrawInfo->BDI_RPort,SP->BTDDrawInfo->BDI_Pens[SP->color]);
    if (++SP->color >= SP->ncolors)
      SP->color = 0;
    XDrawSpline(SP, px, py, SP->x[SP->points-1], SP->y[SP->points-1], zx, zy );

    if (++SP->generation > SP->seconds) 
     initspline(SP);
}

ULONG PenCountMyBlanker(struct TagItem *TagList)

{
 ULONG colors;
 colors=FindTagData(TagList,SP_Colors,DEF_COLORS);
 return colors;
}
