/* *2Pi/360 */
#include <exec/memory.h>
#include <exec/execbase.h>
#include <graphics/gfxbase.h>
#include <intuition/intuitionbase.h>
#include <dos/dosextens.h>
#include <libraries/iffparse.h>
#include <utility/tagitem.h>
#include <time.h>
#include <limits.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 <string.h>
#include <math.h>
#include <m68881.h>

#include "BTD.h"

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

// #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 GATAG(o) (BTD_Client+(o))

#define GA_Seconds GATAG(0)
#define GA_Galaxy  GATAG(1)
#define GA_Stars   GATAG(2)
#define GA_Deltat  GATAG(3)

#define DEF_SECONDS 100
#define DEF_GALAXY  2
#define DEF_STARS   100
#define DEF_DELTAT  100

#define MIN_SECONDS 1
#define MIN_GALAXY  1
#define MIN_STARS   10
#define MIN_DELTAT  10

#define MAX_SECONDS 3600
#define MAX_GALAXY  5
#define MAX_STARS   1000
#define MAX_DELTAT  500

struct BTDInteger GalaxyIntParams[] =
 {
  GA_Seconds,"Big Bang",BTDPT_INTEGER,DEF_SECONDS,MIN_SECONDS,MAX_SECONDS,TRUE,
  GA_Galaxy ,"Galaxies",BTDPT_INTEGER,DEF_GALAXY,MIN_GALAXY,MAX_GALAXY,TRUE,
  GA_Stars,"Stars per Galaxy",BTDPT_INTEGER,DEF_STARS,MIN_STARS,MAX_STARS,TRUE,
  GA_Deltat,"Zoom",BTDPT_INTEGER,DEF_DELTAT,MIN_DELTAT,MAX_DELTAT,TRUE
 } 
;

struct BTDNode *GalaxyParams[] =
 {
  &GalaxyIntParams[0].BI_Node,
  &GalaxyIntParams[1].BI_Node,
  &GalaxyIntParams[2].BI_Node,
  &GalaxyIntParams[3].BI_Node,
  NULL
 };

struct BTDInfo GalaxyInfo =
 {
  BTDI_Revision,MAKE_ID('G','A','L','X'),
  "Galaxia","Galaxy blanker, spinning and colliding Galaxies\nbased on an EGS Blanker from Uli Siegmund","Markus Illenseer",
  GalaxyParams
 };

char MyBlankerName[] = "galaxy.btd";
char MyBlankerID[]   = "Spinning Galaxy 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);
}

struct BTDInfo *QueryMyBlanker(void)
{
 return &GalaxyInfo;
}

#define MAX_GALAXIES		5
#define MAX_HITITERATIONS	200
#define MAX_IDELTAT		50
/* These come originally from the Cluster-version */
#define DEFAULT_GALAXIES	2
#define DEFAULT_STARS		100
#define DEFAULT_HITITERATIONS	7500
#define DEFAULT_IDELTAT		200     /* 0.02 */

#define GALAKSIZE	3.0
#define QCONS		0.001

#define COLORBASE4	4
	/* Colors for stars start here */
#define COLORSTEP4	4

typedef double Vector[3];
typedef double Matrix[3][3];

typedef struct {
	Vector pos, vel;
	int px, py;
	int color;
	} Star;

typedef struct {
	int mass;
	int starscnt;
	Star *stars;
	int basecolor;
	Vector pos, vel;
	} Galaxy;

struct unistruct {
 struct {
 int left;                             /* x minimum */
 int right;                            /* x maximum */
 int top;                              /* y minimum */
 int bottom;                           /* y maximum */
} clip;				      
    int galcol[MAX_GALAXIES];                 /* colors */
    Matrix mat;                               /* Movement of stars(?) */
    double scale;                             /* Scale */
    int midx;                                 /* Middle of screen, x */
    int midy;                                 /* Middle of screen, y */
    double size;                              /* */
    Vector diff;                              /* */
    Galaxy galaxies[MAX_GALAXIES];            /* the Whole Universe */
    double f_deltat;                          /* quality of calculation, calc'd by d_ideltat */
    int f_galaxies;                           /* # galaxies */
    int f_stars;                              /* # stars per galaxy */
    int f_hititerations;                      /* # iterations before restart */
    int step;                                 /* */
    int init;                                 /* 1 -> re-initialize */
  struct BTDDrawInfo *BTDDrawInfo;
  int Colors;
  LONG RandN,RandF,RandI;
};

#define FindTagData(l,t,d) GetTagData((t),(d),(l))

void __regargs InitRandom(struct unistruct *RP,ULONG Instance)

{
 ULONG Time[2];

 CurrentTime (&Time[0],&Time[1]);
 RP->RandN=(LONG)Time[0];

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

 RP->RandF=4*Time[1]+1;
 RP->RandI=2*Time[1]+1;
}

WORD __regargs Random(struct unistruct *GA,WORD Max)

{
 GA->RandN=GA->RandF*GA->RandN+GA->RandI;
 if (GA->RandN<0L) GA->RandN=-GA->RandN;

 return (WORD)(GA->RandN%Max);
}

/* 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 unistruct *InitMyBlanker(struct TagItem *TagList)
{
    struct unistruct *GA;
    int i;
    struct BTDDrawInfo *BTDDrawInfo;
    ULONG *Error,Dummy,Instance,Index;
    unsigned int time[2], a;

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

DEBUG_PRINT("Init\n");

 GA->BTDDrawInfo=BTDDrawInfo;
 Instance=FindTagData(TagList,BTD_Instance,0L);

 InitRandom(GA,Instance);
 timer(time);
 a=time[0]^time[1];
 srand48(a);

 GA->f_galaxies=FindTagData(TagList,GA_Galaxy,DEF_GALAXY);
 GA->Colors=GA->f_galaxies*COLORSTEP4+COLORSTEP4;

    GA->f_stars          =  FindTagData(TagList,GA_Stars,DEF_STARS);
    GA->f_hititerations  =  FindTagData(TagList,GA_Seconds,DEF_SECONDS);
    GA->f_deltat         =  (double)FindTagData(TagList,GA_Deltat,DEF_DELTAT)/10000.0;

    GA->clip.left    = 0;
    GA->clip.top     = 0;
    GA->clip.right   = GA->BTDDrawInfo->BDI_Width-1;
    GA->clip.bottom  = GA->BTDDrawInfo->BDI_Height-1;
    GA->scale        = (double)(BTDDrawInfo->BDI_Width-1)/4.0;
    GA->midx         = GA->clip.right/2;
    GA->midy         = GA->clip.bottom/2;
    GA->init         = 1;

    if(!GA->galaxies[0].stars){
	
	for (i=0; i<MAX_GALAXIES; ++i) {
	    GA->galaxies[i].starscnt=0;	/* 0 valid entries */
	    GA->galaxies[i].stars=(Star *)AllocVec(GA->f_stars*sizeof(Star),MEMF_PUBLIC|MEMF_CLEAR);
	}
	
    }

 if (GA->Colors>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=GA->Colors/NUM_RAINBOW_COLORS+1L;
   Index=0L;
   for (RBCol=0L; RBCol<NUM_RAINBOW_COLORS; RBCol++)
    {
     if (RBCol==(GA->Colors%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<GA->Colors; 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("Init Ready.\n");

 return GA;

}
void EndMyBlanker(struct unistruct *GA)

{
 int i;
 if(GA->galaxies[0].stars)
  for (i=0; i<MAX_GALAXIES; ++i) 
   FreeVec(GA->galaxies[i].stars);
 FreeVec (GA);
}

UWORD PutChar[2] = {0x16C0,0x4E75};

/* dirty hack to avoid assembler part :-)

   16C0: move.b d0,(a3)+
   4E75: rts
*/

void SPrintF(char *Buffer,char *FormatString,...)

{
 RawDoFmt (FormatString,(APTR)((LONG *)&FormatString+1L),(void *)PutChar,Buffer);
}

void AnimMyBlanker(struct unistruct *GA)
{
    double d;                                  /* tmp */
    int i, j, k;                               /* more tmp */
    int t1, t2, t3;

DEBUG_PRINT("Anim\n");

    if(GA->init){
	double w1, w2;                     /* more tmp */
	double v,w, h;                         /* yet more tmp */

	GA->init=0;
	GA->step=0;
	
	for (i=0; i<MAX_GALAXIES; ++i){
	    GA->galcol[i]=i;
DEBUG_PRINT("New Init\n");
	}

   for (i=0; i<MAX_GALAXIES; ++i)
     GA->galcol[i]=i;
   for (i=0; i<MAX_GALAXIES; ++i) {
     t1=(int)(Random(GA,MAX_GALAXIES));	/* t1=[0..MAX_GALAXIES-1] */
     t2=(int)(Random(GA,MAX_GALAXIES));	/* t2=[0..MAX_GALAXIES-1] */

     t3=GA->galcol[t1];
     GA->galcol[t1]=GA->galcol[t2];
     GA->galcol[t2]=t3;
     }

   for (i=0; i<GA->f_galaxies; ++i) {
     GA->galaxies[i].basecolor=GA->galcol[i];

     GA->galaxies[i].starscnt=(int)(Random(GA,GA->f_stars/2))+GA->f_stars/2;

     w1=2.0*PI*drand48();
     w2=2.0*PI*drand48();

     GA->mat[0][0]=         cos(w2);
     GA->mat[0][1]=-sin(w1)*sin(w2);
     GA->mat[0][2]= cos(w1)*sin(w2);
     GA->mat[1][0]= 0.0;
     GA->mat[1][1]= cos(w1);
     GA->mat[1][2]= sin(w1);
     GA->mat[2][0]=-        sin(w2);
     GA->mat[2][1]=-sin(w1)*cos(w2);
     GA->mat[2][2]= cos(w1)*cos(w2);

     GA->galaxies[i].vel[0]=Random(GA,2)-1.0;
     GA->galaxies[i].vel[1]=Random(GA,2)-1.0;
     GA->galaxies[i].vel[2]=Random(GA,2)-1.0;
     GA->galaxies[i].pos[0]=-GA->galaxies[i].vel[0]*GA->f_deltat*GA->f_hititerations+drand48()-0.5;
     GA->galaxies[i].pos[1]=-GA->galaxies[i].vel[1]*GA->f_deltat*GA->f_hititerations+drand48()-0.5;
     GA->galaxies[i].pos[2]=-GA->galaxies[i].vel[2]*GA->f_deltat*GA->f_hititerations+drand48()-0.5;

     GA->galaxies[i].mass=(double)Random(GA,1000);

     t1=drand48();
     GA->size=t1*t1*GALAKSIZE+0.1;

     for (j=0; j<GA->galaxies[i].starscnt; ++j) {
	w=2.0*PI*drand48();
	d=drand48()*GA->size;
	h=drand48()*exp(-2.0*(d/GA->size))/5.0*GA->size;
	if (drand48()<0.5) h=-h;
	GA->galaxies[i].stars[j].pos[0]=GA->mat[0][0]*d*cos(w)+GA->mat[1][0]*d*sin(w)+GA->mat[2][0]*h+GA->galaxies[i].pos[0];
	GA->galaxies[i].stars[j].pos[1]=GA->mat[0][1]*d*cos(w)+GA->mat[1][1]*d*sin(w)+GA->mat[2][1]*h+GA->galaxies[i].pos[1];
	GA->galaxies[i].stars[j].pos[2]=GA->mat[0][2]*d*cos(w)+GA->mat[1][2]*d*sin(w)+GA->mat[2][2]*h+GA->galaxies[i].pos[2];

	v=sqrt(GA->galaxies[i].mass*QCONS/sqrt(d*d+h*h));
	GA->galaxies[i].stars[j].vel[0]=-GA->mat[0][0]*v*sin(w)+GA->mat[1][0]*v*cos(w)+GA->galaxies[i].vel[0];
	GA->galaxies[i].stars[j].vel[1]=-GA->mat[0][1]*v*sin(w)+GA->mat[1][1]*v*cos(w)+GA->galaxies[i].vel[1];
	GA->galaxies[i].stars[j].vel[2]=-GA->mat[0][2]*v*sin(w)+GA->mat[1][2]*v*cos(w)+GA->galaxies[i].vel[2];

	GA->galaxies[i].stars[j].color=COLORBASE4+COLORSTEP4*GA->galaxies[i].basecolor+j%COLORSTEP4;
        if(GA->galaxies[i].stars[j].color>GA->Colors) GA->galaxies[i].stars[j].color=GA->galaxies[i].stars[j].color%GA->Colors;

	GA->galaxies[i].stars[j].px=0;
	GA->galaxies[i].stars[j].py=0;
	}
     }

     SetRast(GA->BTDDrawInfo->BDI_RPort, 0);
    
     }

     for (i=0; i<GA->f_galaxies; ++i) {
	for (j=0; j<GA->galaxies[i].starscnt; ++j) {
	  for (k=0; k<GA->f_galaxies; ++k) {
	    GA->diff[0]=GA->galaxies[k].pos[0]-GA->galaxies[i].stars[j].pos[0];
	    GA->diff[1]=GA->galaxies[k].pos[1]-GA->galaxies[i].stars[j].pos[1];
	    GA->diff[2]=GA->galaxies[k].pos[2]-GA->galaxies[i].stars[j].pos[2];
	    d=GA->diff[0]*GA->diff[0]+GA->diff[1]*GA->diff[1]+GA->diff[2]*GA->diff[2];
	    d=GA->galaxies[k].mass/(d*sqrt(d))*GA->f_deltat*QCONS;
	    GA->diff[0]*=d;
	    GA->diff[1]*=d;
	    GA->diff[2]*=d;
	    GA->galaxies[i].stars[j].vel[0]+=GA->diff[0];
	    GA->galaxies[i].stars[j].vel[1]+=GA->diff[1];
	    GA->galaxies[i].stars[j].vel[2]+=GA->diff[2];
	    }
	  GA->galaxies[i].stars[j].pos[0]+=GA->galaxies[i].stars[j].vel[0]*GA->f_deltat;
	  GA->galaxies[i].stars[j].pos[1]+=GA->galaxies[i].stars[j].vel[1]*GA->f_deltat;
	  GA->galaxies[i].stars[j].pos[2]+=GA->galaxies[i].stars[j].vel[2]*GA->f_deltat;

	  if (   GA->galaxies[i].stars[j].px>=GA->clip.left 
              && GA->galaxies[i].stars[j].px<=GA->clip.right
	      && GA->galaxies[i].stars[j].py>=GA->clip.top 
              && GA->galaxies[i].stars[j].py<=GA->clip.bottom)
		  {
		  SetAPen(GA->BTDDrawInfo->BDI_RPort,BTD_BgPen);
		  WritePixel(GA->BTDDrawInfo->BDI_RPort, GA->galaxies[i].stars[j].px, GA->galaxies[i].stars[j].py);
		  }

	  GA->galaxies[i].stars[j].px=(int)(GA->galaxies[i].stars[j].pos[0]*GA->scale)+GA->midx;
	  GA->galaxies[i].stars[j].py=(int)(GA->galaxies[i].stars[j].pos[1]*GA->scale)+GA->midy;

	  if (   GA->galaxies[i].stars[j].px>=GA->clip.left 
              && GA->galaxies[i].stars[j].px<=GA->clip.right
	      && GA->galaxies[i].stars[j].py>=GA->clip.top 
              && GA->galaxies[i].stars[j].py<=GA->clip.bottom)
		  {
		  SetAPen(GA->BTDDrawInfo->BDI_RPort,GA->BTDDrawInfo->BDI_Pens[GA->galaxies[i].stars[j].color]);
		  WritePixel(GA->BTDDrawInfo->BDI_RPort, GA->galaxies[i].stars[j].px, GA->galaxies[i].stars[j].py);
		  }

	  }

	for (k=i+1; k<GA->f_galaxies; ++k) {
	  GA->diff[0]=GA->galaxies[k].pos[0]-GA->galaxies[i].pos[0];
	  GA->diff[1]=GA->galaxies[k].pos[1]-GA->galaxies[i].pos[1];
	  GA->diff[2]=GA->galaxies[k].pos[2]-GA->galaxies[i].pos[2];
	  d=GA->diff[0]*GA->diff[0]+GA->diff[1]*GA->diff[1]+GA->diff[2]*GA->diff[2];
	  d=GA->galaxies[i].mass*GA->galaxies[k].mass/(d*sqrt(d))*GA->f_deltat*QCONS;
	  GA->diff[0]*=d;
	  GA->diff[1]*=d;
	  GA->diff[2]*=d;
	  GA->galaxies[i].vel[0]+=GA->diff[0]/GA->galaxies[i].mass;
	  GA->galaxies[i].vel[1]+=GA->diff[1]/GA->galaxies[i].mass;
	  GA->galaxies[i].vel[2]+=GA->diff[2]/GA->galaxies[i].mass;
	  GA->galaxies[k].vel[0]-=GA->diff[0]/GA->galaxies[k].mass;
	  GA->galaxies[k].vel[1]-=GA->diff[1]/GA->galaxies[k].mass;
	  GA->galaxies[k].vel[2]-=GA->diff[2]/GA->galaxies[k].mass;
	  }
	GA->galaxies[i].pos[0]+=GA->galaxies[i].vel[0]*GA->f_deltat;
	GA->galaxies[i].pos[1]+=GA->galaxies[i].vel[1]*GA->f_deltat;
	GA->galaxies[i].pos[2]+=GA->galaxies[i].vel[2]*GA->f_deltat;
   }
    GA->step++;
    if(GA->step > GA->f_hititerations*4) GA->init=1;
}


ULONG PenCountMyBlanker(struct TagItem *TagList)

{
 return GetTagData(GA_Galaxy,DEF_GALAXY,TagList)*COLORSTEP4+COLORSTEP4;
}
