/*
   Copyright 2008 Jeffrey Lee
   This file is part of WOUM.
   WOUM is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
   WOUM is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   You should have received a copy of the GNU General Public License
   along with WOUM.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kernel.h"
#include "swis.h"

#include "screen.h"
#include "gp/screen.h"
#include "span.h"
#include "spandraw.h"
#include "tmat.h"
#include "keyboard.h"
#include "timer.h"
#include "amdl.h"

/* Maximum vectors (verticies) per model */
#define MAXVEC 4096
/* Maximum polygons per model */
#define MAXPOLY 4096

/* Verticies for a model */
vec16 verts[MAXVEC];
int nvert;
/* Polygons for a model */
typedef struct {
	int nsides; /* Number of sides */
	int col; /* Colour */
	int verts[MAXVERT]; /* The sides themselves; indexes into the verts array */
} poly;
poly polys[MAXPOLY];
int npoly;

void loadfile(char *name)
{
	/* Load the file... */
	int tmp,tmp2;
	FILE *f;
	float fa,fb,fc;
	f = fopen(name,"r");
	if (f == 0)
	{
		printf("Unable to open %s\n",name);
		exit(1);
	}
	fscanf(f,"%d\n",&nvert); /* Read number of verticies */
	fscanf(f,"%d\n",&npoly); /* Read number of polygons */
	for (tmp=0;tmp<nvert;tmp++)
	{
		fscanf(f,"{%f,%f,%f}\n",&fa,&fb,&fc); /* Read each vertex */
		verts[tmp].x = f1616_FromFloat(fa);
		verts[tmp].y = f1616_FromFloat(fb);
		verts[tmp].z = f1616_FromFloat(fc);
	}
	for (tmp=0;tmp<npoly;tmp++) /* Read each polygon: */
	{
		fscanf(f,"%d\n",&(polys[tmp].nsides)); /* Number of verticies */
		fscanf(f,"%d\n",&(polys[tmp].col)); /* Colour */
		for (tmp2=0;tmp2<polys[tmp].nsides;tmp2++) /* Each vertex no. */
			fscanf(f,"%d ",&(polys[tmp].verts[tmp2]));
	}
	fclose(f);
}

void loaddfmfile(char *name)
{
	/* Load the dfm file... */
	int tmp;
	FILE *f;
	float fa,fb,fc;
	f = fopen(name,"r");
	if (f == 0)
	{
		printf("Unable to open %s\n",name);
		exit(1);
	}
	fscanf(f,"DFM1\n%d\n",&nvert);
	for (tmp=0;tmp<nvert;tmp++)
	{
		fscanf(f,"%f %f %f\n",&fa,&fb,&fc);
		verts[tmp].y = f1616_FromFloat(fa); /* Swap axis round to use our coordinate system */
		verts[tmp].z = f1616_FromFloat(fb);
		verts[tmp].x = f1616_FromFloat(fc);
	}
	fscanf(f,"%d\n",&npoly);
	for (tmp=0;tmp<npoly;tmp++)
	{
		polys[tmp].nsides = 3; /* tsk! */
		polys[tmp].col = (tmp+1)*5; /* double tsk! */
		fscanf(f,"%d %d %d %*f %*f %*f %*f %*f %*f %*s\n",&(polys[tmp].verts[0]),&(polys[tmp].verts[2]),&(polys[tmp].verts[1]));
	}
	fclose(f);
}

/* Code for .an8 format */

char symstr[32];
float symval;
int symtype;
int symbrace;
#define SYM_ID 1
#define SYM_STR 2
#define SYM_NUM 3
#define SYM_EOF 4

void getsym(FILE *f)
{
	int c;
	int i;
	/* Skip whitespace */
	do {
		c = fgetc(f);
		if (c == EOF)
		{
			symtype = SYM_EOF;
			return;
		}
	} while ((c <= 32) || (c > 126));
	/* Check type */
	if (c == '"')
	{
		symtype = SYM_STR;
		i=0;
		do {
			c = fgetc(f);
			symstr[i++] = c;
		} while ((c != '"') && (c >= 32) && (c < 127));
		if (i)
			symstr[i-1] = 0;
		else
			symstr[0] = 0; /* Bad! */
	/*	printf("STR: %s\n",symstr);*/
	}
	else if ((c == '-') || ((c >= '0') && (c <= '9')))
	{
		symtype = SYM_NUM;
		i=0;
		do {
			symstr[i++] = c;
			c = fgetc(f);
		} while (((c >= '0') && (c <= '9')) || (c == '.'));
		if (c != EOF)
			ungetc(c,f);
		symstr[i] = 0;
		sscanf(symstr,"%f",&symval);
	/*	printf("NUM: %f\n",symval);*/
	}
	else
	{
		if (c == '{')
			symbrace++;
		else if (c == '}')
			symbrace--;
		symtype = SYM_ID;
		i=0;
		do {
			symstr[i++] = c;
			c = fgetc(f);
		} while ((c > 32) && (c < 127) && ((c < '0') || (c > '9')) && (c != '-'));
		if (c != EOF)
			ungetc(c,f);
		symstr[i] = 0;
	/*	printf("ID: %s\n",symstr);*/
	}
}

void loadan8file(char *name)
{
	FILE *f;
	int tmp,tmp2,tmp3;
	float fa,fb,fc;
	vec16 org;
	char mnames[16][16]; /* Material names */
	int mcols[16]; /* Material cols */
	int mmat; /* Max material */
	int mmcols[16]; /* Material cols, as used by a mesh */
	int mmmat;
	int vertbase;
	trucol c;
	nvert = npoly = vertbase = 0;
	f = fopen(name,"r");
	if (f == 0)
	{
		printf("Unable to open %s\n",name);
		exit(1);
	}
	/* Search for objects */
	do {
	getsym(f);
	if (symtype == SYM_EOF)
	{
		fclose(f);
		return;
	}
	else if ((symtype == SYM_ID) && (strcmp(symstr,"object") == 0))
	{
		/* Get materials */
		mmat = 0;
		tmp2 = 0;
		do {
			getsym(f);
			if (symtype == SYM_ID)
			{
				if (strcmp(symstr,"material") == 0)
				{
					while (symtype != SYM_STR)
						getsym(f);
					strcpy(mnames[mmat],symstr);
					tmp2 = 0;
				}
				else if ((strcmp(symstr,"rgb") == 0) && (tmp2 == 0))
				{
					do { getsym(f); } while (symtype != SYM_NUM);
					c.r = (int) symval; getsym(f);
					c.g = (int) symval; getsym(f);
					c.b = (int) symval;
					/* Convert to 8 bit */
					mcols[mmat++] = screen_24_2_s8(c); /* ... and expand to full word */
					tmp2 = 1; /* don't overwrite it */
				}
			}
		} while (strcmp(symstr,"mesh"));
		/* Now grab meshes */
		while (strcmp(symstr,"mesh") == 0)
		{
			org.x = org.y = org.z = 0;
			mmmat = 0;
			symbrace = 0;
			vertbase = nvert;
			do {
				do { getsym(f); } while (symtype != SYM_ID);
				if (strcmp(symstr,"origin") == 0)
				{
					getsym(f);
					getsym(f);
					getsym(f);
					org.x = f1616_FromFloat(symval);
					getsym(f);
					org.y = f1616_FromFloat(symval);
					getsym(f);
					org.z = f1616_FromFloat(symval);
				}
				else if (strcmp(symstr,"materialname") == 0)
				{
					mmcols[mmmat] = 0; /* Black */
					do { getsym(f); } while (symtype != SYM_STR);
					for (tmp=0;tmp<mmat;tmp++)
						if (strcmp(mnames[tmp],symstr) == 0)
							mmcols[mmmat] = mcols[tmp];
					mmmat++;
				}
				else if (strcmp(symstr,"points") == 0)
					while (symstr[0] != '}')
					{
						getsym(f); /* '(' */
						if (symstr[0] == '(')
						{
							getsym(f); fa = symval;
							getsym(f); fb = symval;
							getsym(f); fc = symval;
							verts[nvert].x = f1616_FromFloat(fc)+org.z;
							verts[nvert].y = f1616_FromFloat(fa)+org.x;
							verts[nvert++].z = f1616_FromFloat(fb)+org.y;
						}
					}
				else if (strcmp(symstr,"faces") == 0)
				{
					while (symstr[0] != '}')
					{
						while (symtype != SYM_NUM) getsym(f);
						polys[npoly].nsides = tmp = symval;
						getsym(f);
						getsym(f);
						tmp3 = (int) symval;
						polys[npoly].col = mmcols[(int) symval];
						getsym(f);
						do { getsym(f); } while (symstr[0] != '(');
						for (tmp3=0;tmp3<tmp;tmp3++)
						{
							do { getsym(f); } while (symstr[0] != '('); /* open bracket */
							getsym(f); polys[npoly].verts[tmp3] = symval + vertbase; /* vertex */
							getsym(f); /* texture or close bracket */
						}
						npoly++;
						do { getsym(f); } while ((symtype != SYM_NUM) && !((symtype == SYM_ID) && (symstr[0] == '}')));
					}
				}
			} while (symbrace > 0);
			getsym(f);
		} /* meshes */
	} /* object */
	} while (1);
}




void savefile(char *name)
{
	/* Save the file */
	int tmp,tmp2;
	FILE *f;
	f = fopen(name,"w");
	if (f == 0)
	{
		printf("Unable to save output\n");
		exit(1);
	}
	fprintf(f,"%d\n%d\n",nvert,npoly);
	for (tmp=0;tmp<nvert;tmp++)
		fprintf(f,"{%f,%f,%f}\n",f1616_ToFloat(verts[tmp].x),f1616_ToFloat(verts[tmp].y),f1616_ToFloat(verts[tmp].z));
	for (tmp=0;tmp<npoly;tmp++)
	{
		fprintf(f,"%d\n%d\n",polys[tmp].nsides,polys[tmp].col);
		for (tmp2=0;tmp2<polys[tmp].nsides;tmp2++)
			fprintf(f,"%d ",polys[tmp].verts[tmp2]);
		if (polys[tmp].nsides)
			fprintf(f,"\n");
	}
	fclose(f);
}

int drawonly = -2;

void drawmodel(vec16 org,vec16 ang)
{
	tmat16 t1,t2;
	vec16 tverts[MAXVEC]; /* Transformed verts */
	vec16 *tpoly[MAXVERT]; /* Current poly */
	vec16 tv;
	int tmp,tmp2;
	/* Generate tmat */
	/* Move to org then rotate */
	org.x = -org.x; /* Want to subtract location, not add */
	org.y = -org.y;
	org.z = -org.z;
	tmat16_ident(&t1);
	tmat16_translate(&t1,&t2,&org);
	tmat16_rotz(&t2,&t1,ang.z);
	tmat16_rotx(&t1,&t2,ang.x);
	tmat16_roty(&t2,&t1,ang.y);
	/* Transform verticies */
	for (tmp=0;tmp<nvert;tmp++)
	{
		tmat16_dotprod(&t1,&verts[tmp],&tv);
		/* Now swap axis */
		tverts[tmp].z = tv.x; /* Model X (i.e. forward) is screen z */
		tverts[tmp].x = -tv.y; /* Model Y (i.e. left) is screen x */
		tverts[tmp].y = -tv.z; /* Model Z (i.e. up) is screen Y */
	/*	printf("{%.2f,%.2f,%.2f} ",f1616_ToFloat(tverts[tmp].x),f1616_ToFloat(tverts[tmp].y),f1616_ToFloat(tverts[tmp].z));*/
	}
	/* Now draw */
	for (tmp=0;tmp<npoly;tmp++)
	{
		for (tmp2=0;tmp2<polys[tmp].nsides;tmp2++)
			tpoly[tmp2] = &tverts[polys[tmp].verts[tmp2]];
/*		tpoly[tmp2] = &tverts[polys[tmp].verts[0]];
		for (tmp2=0;tmp2<polys[tmp].nsides;tmp2++)
			span_addpoly2(2,&tpoly[tmp2],polys[tmp].col*0x1010101,0,1);*/
		span_id = tmp;
		if ((drawonly == -2) || (drawonly == tmp))
		span_addpoly(polys[tmp].nsides,tpoly,polys[tmp].col*0x1010101,0);
	}
}

#define MSTEP (f1616_FromInt(1))
#define ASTEP (f1616_FromInt(5))

int main(int argc,char **argv)
{
	vec16 org,ang,dir;
	int mx,my,tmp;
#ifdef SCREENBANKING
	void *framea,*frameb,*framec;
#else
	void *frame;
#endif
	int frames;
	_kernel_swi_regs regs;
#ifdef SCREENBANKING
	framea = frameb = framec = 0;
#else
	frame = 0;
#endif
	if (strcmp(argv[1],"-dfm") == 0)
	{
		loaddfmfile(argv[2]);
		if (argc == 4)
		{
			savefile(argv[3]);
			return 0;
		}
	}
	else if (strcmp(argv[1],"-an8") == 0)
	{
		loadan8file(argv[2]);
		if (argc == 4)
		{
			savefile(argv[3]);
			return 0;
		}
	}
	else
	{
		loadfile(argv[1]);
		if (argc == 3)
		{
			savefile(argv[2]);
			return 0;
		}
	}
	screen_oldmode(28);
#ifdef SCREENBANKING
	screen_createbanks(2);
	screen_setvdu(1);
	screen_setdisplay(2);
#endif
	screen_getdata();
	span_lines = screen.height+1;
	span_width = screen.width+1;
	org.x = f1616_FromInt(-100);
	org.y = org.z = 0;
	ang.x = ang.y = ang.z = 0;
	frames = 0;
	system("pointer 1");
	do {
		timer_start(0);
		screen_vsync();
#ifdef SCREENBANKING
		screen_swap();
#endif
		screen_getdata();
		_kernel_swi(0x100+31,&regs,&regs);
		_kernel_swi(0x100,&regs,&regs);
		_kernel_swi(0x100,&regs,&regs);
		span_buf = (void *) screen.vdu;
		span_id = -2;
		span_init(0);
/*		if (drawonly >= 0)
			amdl_on();*/
		drawmodel(org,ang);
/*		if (drawonly >= 0)
		{
			amdl_flush();
			amdl_off();
			exit(0);
		}*/
#ifdef SCREENBANKING
		if (framea)
		{
			span_swapset(framea);
			span_addrect(0,span_width-1,0,64,0,1);
			span_swapset(framea);
		}
		framea = span_do3(gp_screen_hline0,framea);
		framec = frameb;
		frameb = framea;
		framea = framec;
#else
		if (frame)
		{
			span_swapset(frame);
			span_addrect(0,span_width-1,0,64,0,1);
			span_swapset(frame);
		}
		frame = span_do3(gp_screen_hline0,frame);
#endif
		span_free(0);
		printf("{%.2f,%.2f,%.2f} by {%.2f,%.2f,%.2f}\n",
			f1616_ToFloat(org.x),
			f1616_ToFloat(org.y),
			f1616_ToFloat(org.z),
			f1616_ToFloat(ang.x),
			f1616_ToFloat(ang.y),
			f1616_ToFloat(ang.z));
		/* Debugging gumpf */
		_kernel_swi(OS_Mouse,&regs,&regs);
		mx = regs.r[0]/2;
		my = span_lines-(regs.r[1]/2);
#ifdef SCREENBANKING
		printf("%d,%d col=%d depth=%.4f poly=%d\n",mx,my,span_readcol(frameb,mx,my) & 255,f1616_ToFloat(span_readdepth(frameb,mx,my)),tmp=span_readid(frameb,mx,my));
#else
		printf("%d,%d col=%d depth=%.4f poly=%d\n",mx,my,span_readcol(frame,mx,my) & 255,f1616_ToFloat(span_readdepth(frame,mx,my)),tmp=span_readid(frame,mx,my));
#endif
		if (regs.r[2])
			drawonly = tmp;
		/* Handle input */
		dir.x = MSTEP;
		dir.y = 0;
		dir.z = 0;
		vec16_rotatex(&dir,ang.x);
		vec16_rotatey(&dir,-ang.y);
		vec16_rotatez(&dir,ang.z);
		if (keyboard_ispressed(KEY_UP))
			vec16_add(&org,&dir); /* Forwards */
		if (keyboard_ispressed(KEY_DOWN))
			vec16_sub(&org,&dir); /* Backwards */
		if (keyboard_ispressed(KEY_LEFT))
			ang.z+=ASTEP; /* Left */
		if (keyboard_ispressed(KEY_RIGHT))
			ang.z-=ASTEP; /* Right */
		if (keyboard_ispressed(KEY_PGUP))
			ang.y+=ASTEP; /* Up */
		if (keyboard_ispressed(KEY_PGDOWN))
			ang.y-=ASTEP; /* Down */
		timer_stop(0);
		frames++;
		printf("frames %d; average %.2ffps\n",frames,(double) (100*frames)/(timer_report(0)+1));
	} while (1);
	return 0;
}
