/* Qix Library */

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

#include <clib/macros.h>

#define __USE_SYSBASE 42

#include <proto/dos.h>
#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 QP_Seconds QTAG(0)
#define QP_Lines   QTAG(1)
#define QP_Colors  QTAG(2)

#define MAX_SECONDS 360L /* seconds until a new graphic is plotted */
#define MAX_LINES 255L

#define DEF_SECONDS 60L
#define DEF_LINES 10L

#define DEF_COLORS 8L
#define MAX_COLORS 256L

#define BORDER 5L

struct BTDInteger QixIntParams[] =
 {
  QP_Seconds,"Patternchange",BTDPT_INTEGER,DEF_SECONDS,1L,MAX_SECONDS,TRUE,
  QP_Lines,"Lines",BTDPT_INTEGER,DEF_LINES,4L,MAX_LINES,TRUE,
  QP_Colors,"Colors",BTDPT_INTEGER,DEF_COLORS,4L,MAX_COLORS,TRUE
 };

struct BTDNode *QixParams[] = 
 {
  &QixIntParams[0].BI_Node,
  &QixIntParams[1].BI_Node,
  &QixIntParams[2].BI_Node,NULL
 };

struct BTDInfo QixInfo =
 {
  BTDI_Revision,MAKE_ID('Q','I','X',' '),
  "Qix Line Blanker","One of those lonely standard modules","Markus Illenseer 1994",
  QixParams
 };

char MyBlankerName[] = "qix.btd";
char MyBlankerID[]   = "Qix Line 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);
}


#define check_bounds(qp, val, del, min, max)                \
{                                                            \
    if ((val) <= min) {                                       \
  *(del) = (Random((qp),(qp)->qp_delta)) +1 + (qp)->qp_offset; \
    } else if ((val) >= (max)) {                                \
  *(del) = -(Random((qp),(qp)->qp_delta))+1 - (qp)->qp_offset;   \
    }                                                             \
}                                                                  \

typedef struct {
  LONG  x,y;
} point;

struct QixStruct
 {
  struct BTDDrawInfo *BTDDrawInfo;
  LONG   qp_Seconds;
  LONG   qp_Time;
  LONG   qp_Left;    /* Left Offset of our Rect */
  LONG   qp_Top;     /* Top Offset of our Rect */
  LONG   qp_Width;   /* Width of our Rect */
  LONG   qp_Height;  /* Height of our Rect */
  LONG   qp_pix;
  LONG   qp_first;
  LONG   qp_last;
  LONG   qp_dx1;
  LONG   qp_dy1;
  LONG   qp_dx2;
  LONG   qp_dy2;
  LONG   qp_x1;
  LONG   qp_y1;
  LONG   qp_x2;
  LONG   qp_y2;
  LONG   qp_offset;
  LONG   qp_delta;
  LONG   qp_nlines;
  LONG   qp_ncolors;
  point  *qp_lineq;
  LONG   hs_RandN,hs_RandF,hs_RandI;
 };


struct BTDInfo *QueryMyBlanker(void)

{
 return &QixInfo;
}


void __regargs InitRandom(struct QixStruct *QP,ULONG Instance)

{
 ULONG Time[2];

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

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

WORD __regargs Random(struct QixStruct *QP,WORD Max)

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

 return (WORD)(QP->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};

struct QixStruct *InitMyBlanker(struct TagItem *TagList)
{
 LONG Seconds,Lines,Colors;
 struct QixStruct *QP;
 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 ((QP=AllocVec(sizeof(struct QixStruct),MEMF_PUBLIC|MEMF_CLEAR))==NULL)
  {
   *Error=BTDERR_Memory;
   return NULL;
  }

 Lines=FindTagData(TagList,QP_Lines,DEF_LINES);
 Seconds=FindTagData(TagList,QP_Seconds,DEF_SECONDS);
 Colors=FindTagData(TagList,QP_Colors,DEF_COLORS);
 Instance=FindTagData(TagList,BTD_Instance,0L);

 if (!QP->qp_lineq)
  {
   if ((QP->qp_lineq=(point *)AllocVec((Lines%2==0?Lines:Lines+1)*sizeof(point),MEMF_PUBLIC|MEMF_CLEAR))==NULL) 
    {
     *Error=BTDERR_Memory;
     return NULL;
    }
  }


 InitRandom(QP,Instance);

 QP->BTDDrawInfo=BTDDrawInfo;
 QP->qp_Left=QP->BTDDrawInfo->BDI_Left;
 QP->qp_Top=QP->BTDDrawInfo->BDI_Top;
 QP->qp_Width=QP->BTDDrawInfo->BDI_Width;
 QP->qp_Height=QP->BTDDrawInfo->BDI_Height;

 QP->qp_nlines=(Lines%2==0?Lines:Lines+1);
 QP->qp_ncolors=Colors;
 QP->qp_Seconds= Seconds;
 QP->qp_delta=16;
 QP->qp_offset = (LONG)(QP->qp_delta / 3);
 QP->qp_last = 0;
 QP->qp_pix = Random(QP,Colors);
 QP->qp_x1 = Random(QP,QP->qp_Width)+1;
 QP->qp_y1 = Random(QP,QP->qp_Height)+1;
 QP->qp_x2 = Random(QP,QP->qp_Width)+1;
 QP->qp_y2 = Random(QP,QP->qp_Height)+1;

 QP->qp_dx1 = Random(QP,QP->qp_delta) + QP->qp_offset;
 QP->qp_dy1 = Random(QP,QP->qp_delta) + QP->qp_offset;
 QP->qp_dx2 = Random(QP,QP->qp_delta) + QP->qp_offset;
 QP->qp_dy2 = Random(QP,QP->qp_delta) + QP->qp_offset;

 if (QP->qp_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=QP->qp_ncolors/NUM_RAINBOW_COLORS+1L;
   Index=0L;
   for (RBCol=0L; RBCol<NUM_RAINBOW_COLORS; RBCol++)
    {
     if (RBCol==(QP->qp_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<QP->qp_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("Qix: Init ready\n");

 return QP;
}

void EndMyBlanker(struct QixStruct *QP)

{
DEBUG_PRINT("Qix: FreeMem\n");
 FreeVec (QP->qp_lineq);
 FreeVec (QP);
}

void AnimMyBlanker(struct QixStruct *QP)

{

  ULONG Seconds,Micros;
  struct BTDDrawInfo *BTDDrawInfo;

  CurrentTime (&Seconds,&Micros);
  if (Seconds>=QP->qp_Time)
  {
   QP->qp_Time=Seconds+QP->qp_Seconds;

   QP->qp_dx1 = Random(QP,QP->qp_delta) + QP->qp_offset;
   QP->qp_dy1 = Random(QP,QP->qp_delta) + QP->qp_offset;
   QP->qp_dx2 = Random(QP,QP->qp_delta) + QP->qp_offset;
   QP->qp_dy2 = Random(QP,QP->qp_delta) + QP->qp_offset;
  }

  QP->qp_first = (QP->qp_last + 2) % QP->qp_nlines;

  if(QP->qp_first<0) QP->qp_first=0;
  if(QP->qp_first>QP->qp_nlines) QP->qp_first=QP->qp_nlines;

  QP->qp_x1 += QP->qp_dx1;
  QP->qp_y1 += QP->qp_dy1;
  QP->qp_x2 += QP->qp_dx2;
  QP->qp_y2 += QP->qp_dy2;

  check_bounds(QP, QP->qp_x1, &QP->qp_dx1, QP->qp_Left, QP->qp_Width+QP->qp_Left);
  check_bounds(QP, QP->qp_y1, &QP->qp_dy1, QP->qp_Top, QP->qp_Height+QP->qp_Top);
  check_bounds(QP, QP->qp_x2, &QP->qp_dx2, QP->qp_Left, QP->qp_Width+QP->qp_Left);
  check_bounds(QP, QP->qp_y2, &QP->qp_dy2, QP->qp_Top, QP->qp_Height+QP->qp_Top);

  if(QP->qp_x1>=QP->qp_Width+QP->qp_Left) QP->qp_x1=QP->qp_Width+QP->qp_Left-QP->qp_delta*2;
  if(QP->qp_x1<=QP->qp_Left) QP->qp_x1=QP->qp_Left+QP->qp_delta*2;
  if(QP->qp_x2>=QP->qp_Width+QP->qp_Left) QP->qp_x2=QP->qp_Width+QP->qp_Left-QP->qp_delta*2;
  if(QP->qp_x2<=QP->qp_Left) QP->qp_x2=QP->qp_Left+QP->qp_delta*2;

  if(QP->qp_y1>=QP->qp_Height+QP->qp_Top) QP->qp_y1=QP->qp_Height+QP->qp_Top-QP->qp_delta*2;
  if(QP->qp_y1<=QP->qp_Top) QP->qp_y1=QP->qp_Top+QP->qp_delta*2;
  if(QP->qp_y2>=QP->qp_Height+QP->qp_Top) QP->qp_y2=QP->qp_Height+QP->qp_Top-QP->qp_delta*2;
  if(QP->qp_y2<=QP->qp_Top) QP->qp_y2=QP->qp_Top+QP->qp_delta*2;

  BTDDrawInfo= QP->BTDDrawInfo;

  SetAPen(BTDDrawInfo->BDI_RPort,BTD_BgPen);
  Move(BTDDrawInfo->BDI_RPort,QP->qp_lineq[QP->qp_first].x, QP->qp_lineq[QP->qp_first].y);
  Draw(BTDDrawInfo->BDI_RPort,QP->qp_lineq[QP->qp_first + 1].x, QP->qp_lineq[QP->qp_first + 1].y);

  SetAPen(BTDDrawInfo->BDI_RPort,BTDDrawInfo->BDI_Pens[QP->qp_pix]);
  if (QP->qp_pix++ > QP->qp_ncolors) QP->qp_pix = 0;

  Move(BTDDrawInfo->BDI_RPort,QP->qp_x1,QP->qp_y1);
  Draw(BTDDrawInfo->BDI_RPort,QP->qp_x2,QP->qp_y2);

  QP->qp_lineq[QP->qp_last].x = QP->qp_x1;
  QP->qp_lineq[QP->qp_last].y = QP->qp_y1;
  QP->qp_last++;
  if (QP->qp_last >= QP->qp_nlines) QP->qp_last = 0;

  QP->qp_lineq[QP->qp_last].x = QP->qp_x2;
  QP->qp_lineq[QP->qp_last].y = QP->qp_y2;
  QP->qp_last++;
  if (QP->qp_last >= QP->qp_nlines)	QP->qp_last = 0;
}

ULONG PenCountMyBlanker(struct TagItem *TagList)

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