
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

/* Amiga specific headers */
#include <dos/dos.h>
#include <proto/dos.h>

/*
**#define SX 128	 Graphic Width 
**#define SY 128	 Graphic Height 
**#define W 4		 Line Width (2..5) 
**#define VW 3		 Var for Line Width (1..W-1) 
**#define NL 5		 Number of Lines for iteration (3..6) 
**#define VNL 3		 Var for Number of Lines (1..NL-1) 
**#define VP 3		 For diffussion (1..4) 
**#define PASSES 4	 Number of iterations (5..25) 
*/
/*#define PLANES 4*/	/* Number of bitplanes */

int VP,PASSES,PLANES=4,SX=128,SY=128;

int LINES=1;
int W,VW,NL,VNL,VL;

int ANGLES=1;
int AW,AVW,ANA,AVNA,AVL,AA,AVA,ALEN,AVLEN;

/* #define abs(a,b) (((a)>0)?(a):(b)) */
#define swap(a,b) {int tmp;tmp=a;a=b;b=tmp;}

double **p;
double **q;

char *verstr="$VER: Tessels 1.0 "__AMIGADATE__;

void freemat(void **k,int x)
{
	int i;
	
	if(!k)
	{
		return;
	}
	
	for(i=0;i<x;i++)
	{
		free(k[i]);
	}
	free(k);
}

void **allocmat(int x, int y, int siz)
{
	void **k;
	int i;
	char fl=0;
	
	if(!(k=malloc(x*sizeof(void *))))
	{
		return NULL;
	}
	for(i=0;i<x;i++)
	{
		k[i]=malloc(y*siz);
		if(!k[i])
		{
			fl=1;
		}
	}
	if(fl)
	{
		freemat(k,x);
		return NULL;
	}
	return k;
}

double getpixel(int x, int y)
{
	x=(x+16*SX)%SX;
	y=(y+16*SY)%SY;
	return p[x][y];
}

double getpixel2(int x, int y)
{
	x=(x+16*SX)%SX;
	y=(y+16*SY)%SY;
	return q[x][y];
}

void addpixel(int x, int y, double c)
{
	x=(x+16*SX)%SX;
	y=(y+16*SY)%SY;
	p[x][y]+=c;
}

void adddot(int cx, int cy, int r, double c)
{
	int x,y,l;
	
	for(x=-r+1;x<r;x++)
	{
		l=sqrt((double)r*r-x*x);
		
		for(y=-l+1;y<l;y++)
		{
			addpixel(cx+x,cy+y,c);
		}
	}
}

int gauss(int m)
{
	int p;
	p=rand()%255-127;
	p=p*p*p*m;
	p/=(127*127*127);
	return p;
}

void addline(int x1, int y1, int x2, int y2, double c, int vc, int r, int vr, int vp)
{
	int k,dx,dy,i;

	dx=x2-x1;
	dy=y2-y1;
	k=(sqrt((double)dx*dx+dy*dy)*3)/2;
	
	for(i=0;i<k;i++)
	{
		adddot(x1+(dx*i)/k+gauss(vp),y1+(dy*i)/k+gauss(vp),r+gauss(vr),(double)c+gauss(vc));
	}
}

void process(void)
{
	int x,y;
	
	for(x=0;x<SX;x++)
	{
		for(y=0;y<SY;y++)
		{
			q[x][y]=getpixel(x+gauss(VP),y+gauss(VP));
		}
	}
	for(x=0;x<SX;x++)
	{
		for(y=0;y<SY;y++)
		{
			p[x][y]=(8*getpixel2(x,y)+getpixel2(x-1,y-1)+getpixel2(x-1,y)+getpixel2(x-1,y+1)+getpixel2(x,y-1)+getpixel2(x,y+1)+getpixel2(x+1,y-1)+getpixel2(x+1,y)+getpixel2(x+1,y+1))/16;
		}
	}
}

int main(void)
{
	int seed;
	int x,y,i,j,k;
	int x1,y1,x2,y2,c,r;
	double mi,ma;
	
	int cmap[1<<8][3];
	int cm[5][3];
	char **bm;
	
	FILE *fo;
	
	struct RDArgs *rdargs;
	#define TEMPLATE "FILE/A,SEED/N,WIDTH/K/N,HEIGHT/K/N,LINES/S,ANGLES/S,PASSES/K/N,DIFF=DIFFUSION/K/N,LW/K/N,LVW/K/N,LN/K/N,LVN/K/N,LV/K/N,\
AW/K/N,AVW/K/N,AN/K/N,AVN/K/N,AV/K/N,AA/K/N,AVA/K/N,ALE/K/N,AVLE/K/N,PLANES/K/N"
	#define OPT_FILE 0
	#define OPT_SEED 1
	#define OPT_WIDTH 2
	#define OPT_HEIGHT 3
	#define OPT_LINES 4
	#define OPT_ANGLES 5
	#define OPT_PASSES 6
	#define OPT_DIFF 7
	#define OPT_LW 8
	#define OPT_LVW 9
	#define OPT_LN 10
	#define OPT_LVN 11
	#define OPT_LV 12
	#define OPT_AW 13
	#define OPT_AVW 14
	#define OPT_AN 15
	#define OPT_AVN 16
	#define OPT_AV 17
	#define OPT_AA 18
	#define OPT_AVA 19
	#define OPT_ALE 20
	#define OPT_AVLE 21
	#define OPT_PLANES 22
	
	LONG args[23] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	char fname[1025];
	
/*	if(!(fi=fopen("model","rb")))
**	{
**		return(20);
**	}
*/

	if(!(rdargs = ReadArgs(TEMPLATE, (LONG *)&args, NULL)))
	{
		PrintFault(IoErr(),NULL);
		return(20);
	}

	if (args[OPT_PLANES])
	{
		PLANES = *((LONG *)args[OPT_PLANES]);
	}

	if (args[OPT_WIDTH])
	{
		SX = *((LONG *)args[OPT_WIDTH]);
	}
	
	if (args[OPT_HEIGHT])
	{
		SY = *((LONG *)args[OPT_HEIGHT]);
	}

	if(!(q=(double **)allocmat(SX,SY,sizeof(double))))
	{
		FreeArgs(rdargs);
		return(20);
	}
	if(!(p=(double **)allocmat(SX,SY,sizeof(double))))
	{
		FreeArgs(rdargs);
		freemat((void **)q,SX);
		return(20);
	}
	if(!(bm=(char **)allocmat(PLANES,((SX+15)/16)*2,1)))
	{
		FreeArgs(rdargs);
		freemat((void **)p,SX);
		freemat((void **)q,SX);
		return(20);
	}

	if (args[OPT_FILE])
	{
		strcpy(fname,((char *)args[OPT_FILE]));
	}

	if(!(fo=fopen(fname,"wb")))
	{
		FreeArgs(rdargs);
		freemat((void **)p,SX);
		freemat((void **)q,SX);
		/*fclose(fi);*/
		return(20);
	}
	
	if (args[OPT_SEED])
	{
		seed = *((LONG *)args[OPT_SEED]);
	}
	else
	{
		seed=time(NULL);
	}

	srand(seed);
	
	W=2+(rand()%4);
	VW=1+(rand()%(W-1));
	NL=3+(rand()%4);
	VNL=1+(rand()%(NL-1));
	VL=1+(rand()%4);
	VP=1+(rand()%4);
	PASSES=5+(rand()%21);

	AW=2+(rand()%4);
	AVW=1+(rand()%(AW-1));
	ANA=3+(rand()%4);
	AVNA=1+(rand()%(ANA-1));
	AVL=1+(rand()%4);
	AA=rand()%360;
	AVA=(rand()%40);
	ALEN=(SX+SY)/8+(rand()%(2*SX+2*SY));
	AVLEN=1+(rand()%(ALEN));

	if (args[OPT_LINES])
	{
		LINES=1;
	}
	if (args[OPT_ANGLES])
	{
		ANGLES=1;
	}
	if (args[OPT_PASSES])
	{
		PASSES = *((LONG *)args[OPT_PASSES]);
	}
	if (args[OPT_DIFF])
	{
		VP = *((LONG *)args[OPT_DIFF]);
	}
	if (args[OPT_LW])
	{
		W = *((LONG *)args[OPT_LW]);
	}
	if (args[OPT_LVW])
	{
		VW = *((LONG *)args[OPT_LVW]);
	}
	if (args[OPT_LN])
	{
		NL = *((LONG *)args[OPT_LN]);
	}
	if (args[OPT_LVN])
	{
		VNL = *((LONG *)args[OPT_LVN]);
	}
	if (args[OPT_LV])
	{
		VL = *((LONG *)args[OPT_LV]);
	}

	if (args[OPT_AW])
	{
		AW = *((LONG *)args[OPT_AW]);
	}
	if (args[OPT_AVW])
	{
		AVW = *((LONG *)args[OPT_AVW]);
	}
	if (args[OPT_AN])
	{
		ANA = *((LONG *)args[OPT_AN]);
	}
	if (args[OPT_AVN])
	{
		AVNA = *((LONG *)args[OPT_AVN]);
	}
	if (args[OPT_AV])
	{
		AVL = *((LONG *)args[OPT_AV]);
	}
	if (args[OPT_AA])
	{
		AA = *((LONG *)args[OPT_AA]);
	}
	if (args[OPT_AVA])
	{
		AVA = *((LONG *)args[OPT_AVA]);
	}
	if (args[OPT_ALE])
	{
		ALEN = *((LONG *)args[OPT_ALE]);
	}
	if (args[OPT_AVLE])
	{
		AVLEN = *((LONG *)args[OPT_AVLE]);
	}



	if(LINES+ANGLES==0)
	{
		LINES=1;
	}
	
	for(x=0;x<SX;x++)
	{
		for(y=0;y<SY;y++)
		{
			p[x][y]=0;
		}
	}
	
	for(i=0;i<PASSES;i++)
	{
		if(LINES)
		{
			k=NL+gauss(VNL);
			for(j=0;j<k;j++)
			{
				x1=rand()%(3*SX);
				y1=rand()%(3*SY);
				x2=rand()%(3*SX);
				y2=rand()%(3*SY);
				c=6+gauss(4);
				r=W+gauss(VW);
				addline(x1,y1,x2,y2,(double)c,3,r,2,VL);
			}
		}
		if(ANGLES)
		{
			k=ANA+gauss(AVNA);
			for(j=0;j<k;j++)
			{
				c=AA+gauss(AVA);
				r=ALEN+gauss(AVLEN);
				x1=rand()%(3*SX);
				y1=rand()%(3*SY);
				x2=x1+cos((6.28*c)/360)*r;
				y2=y1+sin((6.28*c)/360)*r;
				c=6+gauss(4);
				r=AW+gauss(AVW);
				addline(x1,y1,x2,y2,(double)c,3,r,2,AVL);
			}
		}

		process();
	}
/*	
**	for(i=0;i<18;i++)
**	{
**		fputc(fgetc(fi),fo);
**	}
**	
**	fgetc(fi);
**	fputc(SX&0xff,fo);
**	fgetc(fi);
**	fputc((SX&0xff00)>>8,fo);
**	fgetc(fi);
**	fputc((SX&0xff0000)>>16,fo);
**	fgetc(fi);
**	fputc((SX&0xff000000)>>24,fo);
**
**	fgetc(fi);
**	fputc(SY&0xff,fo);
**	fgetc(fi);
**	fputc((SY&0xff00)>>8,fo);
**	fgetc(fi);
**	fputc((SY&0xff0000)>>16,fo);
**	fgetc(fi);
**	fputc((SY&0xff000000)>>24,fo);
**	
**	for(i=0;i<1594-18-8-512-4;i++)
**	{
**		fputc(fgetc(fi),fo);
**	}
*/

	ma=mi=p[0][0];

	for(y=SY-1;y>=0;y--)
	{
		for(x=0;x<SX;x++)
		{
			ma=max(ma,p[x][y]);
			mi=min(mi,p[x][y]);
		}
	}
	
	ma-=mi;
	
	for(i=0;i<5;i++)
	{
		cm[i][0]=rand()&255;
		cm[i][1]=rand()&255;
		cm[i][2]=rand()&255;
	}
	for(i=0;i<4;i++)
	{
		for(j=i+1;j<5;j++)
		{
			if(cm[i][0]+cm[i][1]+cm[i][2]>cm[j][0]+cm[j][1]+cm[j][2])
			{
				swap(cm[i][0],cm[j][0]);
				swap(cm[i][1],cm[j][1]);
				swap(cm[i][2],cm[j][2]);
			}
		}
	}
	
	for(i=0;i<4;i++)
	{
		j=((1<<PLANES)/4)*i;
		k=min(((1<<PLANES)/4)*(i+1),(1<<PLANES)-1);
		for(c=j;c<=k;c++)
		{
			x1=c-j;
			x2=k-j;
			cmap[c][0]=cm[i][0]+((cm[i+1][0]-cm[i][0])*x1)/x2;
			cmap[c][1]=cm[i][1]+((cm[i+1][1]-cm[i][1])*x1)/x2;
			cmap[c][2]=cm[i][2]+((cm[i+1][2]-cm[i][2])*x1)/x2;
		}
	}
	
/*	for(y=SY-1;y>=0;y--)
**	{
**		for(x=0;x<SX;x++)
**		{
**			c=((p[x][y]-mi)*255)/ma;
**			fputc(c,fo);
**		}
**	}
*/	
/*	fclose(fi);*/
	
	i=4+4+4+4+4+4+4+20+3*(1<<PLANES)+SY*PLANES*(2*((15+SX)/16));
	
	fwrite("FORM",1,4,fo);
	fputc((i&0xff000000)>>24,fo);
	fputc((i&0xff0000)>>16,fo);
	fputc((i&0xff00)>>8,fo);
	fputc((i&0xff),fo);
	fwrite("ILBMBMHD\x00\x00\x00\x14",1,12,fo);
	fputc((SX&0xff00)>>8,fo);
	fputc((SX&0xff),fo);
	fputc((SY&0xff00)>>8,fo);
	fputc((SY&0xff),fo);
	fwrite("\x00\x00\x00\x00",1,4,fo);
	fputc((PLANES&0xff),fo);
	fwrite("\x00\x00\x00\x00\x00\x00\x00",1,7,fo);
	fputc((SX&0xff00)>>8,fo);
	fputc((SX&0xff),fo);
	fputc((SY&0xff00)>>8,fo);
	fputc((SY&0xff),fo);
	fwrite("CMAP\x00\x00",1,6,fo);
	i=3*(1<<PLANES);
	fputc((i&0xff00)>>8,fo);
	fputc((i&0xff),fo);

	for(i=0;i<(1<<PLANES);i++)
	{
		fputc((cmap[i][0]&0xff),fo);
		fputc((cmap[i][1]&0xff),fo);
		fputc((cmap[i][2]&0xff),fo);
	}
	
	fwrite("BODY",1,4,fo);
	i=SY*PLANES*(2*((15+SX)/16));
	fputc((i&0xff000000)>>24,fo);
	fputc((i&0xff0000)>>16,fo);
	fputc((i&0xff00)>>8,fo);
	fputc((i&0xff),fo);
	
	j=(15+SX)&0xfff0;
	
	for(y=SY-1;y>=0;y--)
	{
		for(x=0;x<j;x++)
		{
			if(!(x&7))
			{
				for(k=0;k<PLANES;k++)
				{
					bm[k][x/8]=0;
				}
			}
			c=((getpixel(x,y)-mi)*((1<<PLANES)-1))/ma;
			for(k=0;k<PLANES;k++)
			{
				if(c&(1<<k))
				{
					bm[k][x/8]|=(1<<(7-(x&7)));
				}
			}
		}
		for(k=0;k<PLANES;k++)
		{
			fwrite(bm[k],1,(j/8),fo);
		}
	}
	
	freemat((void **)p,SX);
	freemat((void **)q,SX);
	freemat((void **)bm,PLANES);

	fclose(fo);
	
	FreeArgs(rdargs);
}

/*
**	ILBM				4
**	BMHD				4(BODY)+4(len)		+		20
**	CMAP				4(CMAP)+4(len)		+		3*(1<<PLANES)
**	BODY				4(BODY)+4(len)		+		SY*PLANES*(2*((15+SX)/16))
*/