/* Flame Library */

#include <exec/memory.h>
#include <exec/execbase.h>
#include <graphics/gfxbase.h>
#include <intuition/intuitionbase.h>
#include <libraries/iffparse.h>
#include <utility/tagitem.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>

// #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


struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Library *UtilityBase,*MathIeeeDoubBasBase,*MathIeeeDoubTransBase;

#define FlameTAG(o) (BTD_Client+(o))

#define Flame_Colors FlameTAG(0)
#define Flame_Level FlameTAG(1)

#define MAX_COLORS 255L /* seconds until a new graphic is plotted */
#define DEF_COLORS 20L

#define MAX_LEVEL 10L
#define DEF_LEVEL 4L

struct BTDInteger FlameIntParams[] =
 {
  Flame_Colors,"Colors",BTDPT_INTEGER,DEF_COLORS,1L,MAX_COLORS,TRUE,
  Flame_Level,"Patternchange",BTDPT_INTEGER,DEF_LEVEL,1L,MAX_LEVEL,TRUE
 };

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

struct BTDInfo FlameInfo =
 {
  BTDI_Revision,MAKE_ID('F','L','A','M'),
  "Flame","based on Flame\nScientific American","Markus Illenseer",
  FlameParams
 };

#define MAXTOTAL	10000
#define MAXBATCH	10
#define MAXLEV		4

struct XPoint
 {
  int x,y;
 };

struct FlameStruct
 {
  struct BTDDrawInfo *BTDDrawInfo;
  LONG   hs_RandN,hs_RandF,hs_RandI;
  double      f[2][3][MAXLEV];/* three non-homogeneous transforms */
  int         max_levels;
  int         cur_level;
  int         snum;
  int         anum;
  int         width, height;
  int         num_points;
  int         total_points;
  int         pixcol;
  int         numcol;
  int         alt;
  struct XPoint      pts[MAXBATCH];
 };

/* library stuff */

char MyBlankerName[] = "flame.btd";
char MyBlankerID[]   = "Flame 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))
      {
       if (MathIeeeDoubBasBase=OpenLibrary("mathieeedoubbas.library",37L))
        {
         if (MathIeeeDoubTransBase=OpenLibrary("mathieeedoubtrans.library",37L)) return TRUE;

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

void MyBlankerLibFree(void)

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

/* random generator */

void __regargs InitRandom(struct FlameStruct *FlameStruct,ULONG Instance)

{
 ULONG Time[2];

 CurrentTime (&Time[0],&Time[1]);
 FlameStruct->hs_RandN=(LONG)Time[0];

 if (Time[1]<1024L) Time[1]|=1;
 else Time[1]>>=10;
 Time[1]^=Instance;

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

WORD __regargs Random(struct FlameStruct *FlameStruct,WORD Max)

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

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

/* implementation of library functions */

struct BTDInfo *QueryMyBlanker(void)

{
 return &FlameInfo;
}


#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 FlameStruct *InitMyBlanker(struct TagItem *TagList)

{
 struct BTDDrawInfo *BTDDrawInfo;
 struct FlameStruct *FlameStruct;
 ULONG *Error,Dummy,Instance;
 int Index;

 if ((BTDDrawInfo=(struct BTDDrawInfo *)
                   GetTagData(BTD_DrawInfo,NULL,TagList))==NULL) return NULL;
 Error=(ULONG *)GetTagData(BTD_Error,(ULONG)&Dummy,TagList);
 if ((FlameStruct=AllocVec(2*sizeof(struct FlameStruct),MEMF_PUBLIC|MEMF_CLEAR))==NULL)
  {
   *Error=BTDERR_Memory;
   return NULL;
  }
 Instance=GetTagData(BTD_Instance,0L,TagList);

 FlameStruct->BTDDrawInfo=BTDDrawInfo;
 FlameStruct->numcol=GetTagData(Flame_Colors,DEF_COLORS,TagList);
 FlameStruct->max_levels=GetTagData(Flame_Level,DEF_LEVEL,TagList);
 InitRandom (FlameStruct,Instance);

 FlameStruct->width = BTDDrawInfo->BDI_Width-1;
 FlameStruct->height = BTDDrawInfo->BDI_Height-1;

 FlameStruct->cur_level = 0; 
 FlameStruct->alt = 0; 
 FlameStruct->anum = 0;
 FlameStruct->snum = 0;

 if (FlameStruct->numcol>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=FlameStruct->numcol/NUM_RAINBOW_COLORS+1L;
   Index=0L;
   for (RBCol=0L; RBCol<NUM_RAINBOW_COLORS; RBCol++)
    {
     if (RBCol==(FlameStruct->numcol%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<FlameStruct->numcol; 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;
   }

 FlameStruct->pixcol=1;
 SetAPen(FlameStruct->BTDDrawInfo->BDI_RPort,BTDDrawInfo->BDI_Pens[FlameStruct->pixcol]);

 return FlameStruct;
}

void XDrawPoints(struct FlameStruct *fs)
{

 LONG Index;
 int x,y;

 DEBUG_PRINT("Flame: Drawing Points\n");

 for(Index=0; Index<fs->num_points; Index++)
  {
   x=fs->pts[Index].x;
   y=fs->pts[Index].y;

   if(x<fs->width && x>0 && y<fs->height && y>0)
    WritePixel(fs->BTDDrawInfo->BDI_RPort, x, y); 
  }
}

static BOOL recurse(struct FlameStruct *fs, double x, double y, int l)
{
    int         /*xp, yp,*/ i;
    double      nx, ny;

    if (l == fs->max_levels) {
	fs->total_points++;
	if (fs->total_points > MAXTOTAL)	/* how long each fractal runs */
	    return FALSE;

	if (x > -1.0 && x < 1.0 && y > -1.0 && y < 1.0) {
	    /* xp = */ fs->pts[fs->num_points].x = (int) ((fs->width / 2)
						    * (x + 1.0));
	    /* yp = */ fs->pts[fs->num_points].y = (int) ((fs->height / 2)
						    * (y + 1.0));
	    fs->num_points++;
	    if (fs->num_points > MAXBATCH) {	/* point buffer size */
		XDrawPoints(fs);
		fs->num_points = 0;
	    }
	}
    } else {
	for (i = 0; i < fs->snum; i++) {
	    nx = fs->f[0][0][i] * x + fs->f[0][1][i] * y + fs->f[0][2][i];
	    ny = fs->f[1][0][i] * x + fs->f[1][1][i] * y + fs->f[1][2][i];
	    if (i < fs->anum) {
		nx = sin(nx);
		ny = sin(ny);
	    }
	    if (!recurse(fs, nx, ny, l + 1))
		return FALSE;
	}
    }
    return TRUE;
}

void EndMyBlanker(struct FlameStruct *FlameStruct)

{
 DEBUG_PRINT("Flame: End\n");

 FreeVec (FlameStruct);
}

void AnimMyBlanker(struct FlameStruct *fs)

{
    int         i, j, k;

 DEBUG_PRINT("Flame: Anim\n");

    if (!(fs->cur_level++ % fs->max_levels)) {
	SetRast(fs->BTDDrawInfo->BDI_RPort,0);
	fs->alt = !fs->alt;
    } else {
        SetAPen(fs->BTDDrawInfo->BDI_RPort, fs->BTDDrawInfo->BDI_Pens[fs->pixcol]);
	    if (--fs->pixcol < 1)
		fs->pixcol = fs->numcol;
	}
    
    /* number of functions */
    fs->snum = 2 + (fs->cur_level % (MAXLEV - 1));

    /* how many of them are of alternate form */
    if (fs->alt)
	fs->anum = 0;
    else
	fs->anum = Random(fs,fs->snum) + 2;

    /* 6 coefs per function */
    for (k = 0; k < fs->snum; k++) {
	for (i = 0; i < 2; i++)
	    for (j = 0; j < 3; j++)
		fs->f[i][j][k] = ((double) Random(fs,fs->width) / (fs->width/2.0) - 1.0);
    }
    fs->num_points = 0;
    fs->total_points = 0;
    (void) recurse(fs, 0.0, 0.0, 0);
    XDrawPoints(fs);
}

ULONG PenCountMyBlanker(struct TagItem *TagList)

{
 return  GetTagData(Flame_Colors,DEF_COLORS,TagList);
}
