#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <zlib.h>

#ifdef _PSP
#include<pspctrl.h>
#include<pspgu.h>
#include<pspgum.h>
#include "GUblit.h"
#endif
#include "camera.h"
#include "vert.h"

struct Material {
	char name[64];
	unsigned long color;
} material[32];

int nextMaterial;

struct Item item[16];

int nextItem;

float from[3]={-600,120,0},to[3]={106,115,0},up[3]={0,1,0};

void loadItem(char *fname)
{
	char path[256];
	Vertex3DCP *vert=0;

	nextMaterial=0;

	printf("read materials for '%s'\n",fname);
	sprintf(path,"data/intro/%s.mtl",fname);
//	gzFile file=gzopen(path,"r");
	FILE *file=fopen(path,"r");
		strcpy(item[nextItem].name,fname);
	if(file) {
		// Read in the materials.
		char line[256];
		line[255]=0;
		int mat=nextMaterial;

//		while( gzgets(file,line,255) ) {
		while( fgets(line,255,file) ) {
			char cmd[64];

			sscanf(line,"%s",cmd);
			if(strchr(line,'\r')) strchr(line,'\r')[0]=0;
			if(strchr(line,'\n')) strchr(line,'\n')[0]=0;

			if(strcmp(cmd,"newmtl")==0 ) {
				mat=nextMaterial;
				nextMaterial++;
				strcpy(material[mat].name,line+7);
			} else if( strcmp(cmd,"Kd")==0) {
				float rf=0,gf=0,bf=0;
				unsigned long r,g,b;
				sscanf(line,"Kd%f%f%f",&rf,&gf,&bf);
				r=rf*255;
				g=gf*255;
				b=bf*255;
				material[mat].color=(r)|(g<<8)|(b<<16)|(255<<24);
				printf("%s is %d/%d/%d\n",material[mat].name,(int)r,(int)g,(int)b);
			}

		}
		fclose(file);
//		gzclose(file);
		printf("read %d materials for %s\n",nextMaterial,fname);
	}

	printf("read obj for '%s'\n",fname);
	sprintf(path,"data/intro/%s.obj",fname);
	//sprintf(path,"data/intro/%s.obj.gz",fname);
//	file=gzopen(path,"r");
	file=fopen(path,"r");

	if(file) {
		// Count the verts.
		char line[256];
		line[255]=0;
		int i;
		int v=0;
		int f=0;

//		while( gzgets(file,line,255) ) {
		while( fgets(line,255,file) ) {
//printf("Pass 1: %s",line);			
			switch(line[0]) {
			case 'v':
				if(line[1]==' ') v++;
				break;
			case 'f':
				f+=3;
				break;
			}
		}
		printf("Found %d verts, and %d faces.\n",v,f);
		strcpy(item[nextItem].name,fname);
		item[nextItem].polyCount=f;
		item[nextItem].poly=calloc(sizeof(short),f);
		item[nextItem].vertCount=v;
		vert=calloc(sizeof(Vertex3DCP),v);
		item[nextItem].vert=calloc(sizeof(Vertex3DCNP),f);
		for(i=0;i<16;i++) {
			item[nextItem].matrix[i]=(i%4)==(i/4)?1:0;
		}
		//gzclose(file);
		fclose(file);
		file=0;
	}
//	file=gzopen(path,"r");
	file=fopen(path,"r");
	if(file) {
		// actual verts
		char line[256];
		line[255]=0;
		int v=0;
		int gv=0;
		int f=0;
		int m=0;
		float min[3]={0,0,0},max[3]={0,0,0};

//		while( gzgets(file,line,255) ) {
		while( fgets(line,255,file)) {
			float x,y,z;
			int f1,f2,f3;
			int n1,n2,n3;
			char name[64];

			if(sscanf(line,"v%f%f%f",&x,&y,&z)==3) {
				if(x<min[0]) min[0]=x;
				if(y<min[1]) min[1]=y;
				if(z<min[2]) min[2]=z;
				if(x>max[0]) max[0]=x;
				if(y>max[1]) max[1]=y;
				if(z>max[2]) max[2]=z;
				vert[v].x=x;
				vert[v].y=y;
				vert[v].z=z;
				vert[v].color=0xff00ffff;
				v++;
				//if(v>item[nextItem].vertCount) {
				//	printf("At indexed vert %d of %d\n",v,item[nextItem].vertCount);
				//}
			} else if(line[0]=='g') {
				//gv=v;
				//printf("Got group at %d (%d)\n",v,gv);
			} else if(sscanf(line,"usemtl %s",name)>0) {
				for(m=0;m<nextMaterial;m++) {
					if(strcmp(name,material[m].name)==0) break;
				}
				if(m==nextMaterial) {
					printf("Material error.  Couldn't find %s\n",name);
					m=0;	// bad file
				}
				//printf("Applying material %s (%d) at %d-%d\n",name,m,gv,v);
				while(gv<v) {
					vert[gv++].color=material[m].color;
				}
			} else if(sscanf(line,"f%d%d%d",&f1,&f2,&f3)==3 ||
				sscanf(line,"f%d//%d%d//%d%d//%d",&f1,&n1,&f2,&n2,&f3,&n3)==6) {
				item[nextItem].vert[f].x=vert[f1-1].x;
				item[nextItem].vert[f].y=vert[f1-1].y;
				item[nextItem].vert[f].z=vert[f1-1].z;
				item[nextItem].vert[f].color=vert[f1-1].color;
				item[nextItem].poly[f++]=f1-1;
				item[nextItem].vert[f].x=vert[f2-1].x;
				item[nextItem].vert[f].y=vert[f2-1].y;
				item[nextItem].vert[f].z=vert[f2-1].z;
				item[nextItem].vert[f].color=vert[f2-1].color;
				item[nextItem].poly[f++]=f2-1;
				item[nextItem].vert[f].x=vert[f3-1].x;
				item[nextItem].vert[f].y=vert[f3-1].y;
				item[nextItem].vert[f].z=vert[f3-1].z;
				item[nextItem].vert[f].color=vert[f3-1].color;
				item[nextItem].poly[f++]=f3-1;
				float v1[3],v2[3];
				float nx,ny,nz;
				v1[0]=item[nextItem].vert[f-2].x-item[nextItem].vert[f-3].x;
				v1[1]=item[nextItem].vert[f-2].y-item[nextItem].vert[f-3].y;
				v1[2]=item[nextItem].vert[f-2].z-item[nextItem].vert[f-3].z;
				v2[0]=item[nextItem].vert[f-1].x-item[nextItem].vert[f-3].x;
				v2[1]=item[nextItem].vert[f-1].y-item[nextItem].vert[f-3].y;
				v2[2]=item[nextItem].vert[f-1].z-item[nextItem].vert[f-3].z;
				// A x B = <Ay*Bz - Az*By, Az*Bx - Ax*Bz, Ax*By - Ay*Bx>
				nx=v1[1]*v2[2]-v1[2]*v2[1];
				ny=v1[2]*v2[0]-v1[0]*v2[2];
				nz=v1[0]*v2[1]-v1[1]*v2[0];
				float nw=sqrtf(nx*nx+ny*ny+nz*nz);
				if(nw==0) nw=1;
				nx=nx/nw;
				ny=ny/nw;
				nz=nz/nw;
				item[nextItem].vert[f-3].nx=nx;
				item[nextItem].vert[f-3].ny=ny;
				item[nextItem].vert[f-3].nz=nz;
				item[nextItem].vert[f-2].nx=nx;
				item[nextItem].vert[f-2].ny=ny;
				item[nextItem].vert[f-2].nz=nz;
				item[nextItem].vert[f-1].nx=nx;
				item[nextItem].vert[f-1].ny=ny;
				item[nextItem].vert[f-1].nz=nz;
				//printf("f %d,%d,%d\n",f1,f2,f3);
				//item[nextItem].vert[f1].color=material[m].color;
				//item[nextItem].vert[f2].color=material[m].color;
				//item[nextItem].vert[f3].color=material[m].color;
				fflush(stdout);
			}
		}
		fclose(file);
//		gzclose(file);
		printf("Min: %.2f,%.2f,%.2f; Max: %.2f,%.2f,%.2f\n",min[0],min[1],min[2],max[0],max[1],max[2]);
		//to[0]=(max[0]+min[0])/2;
		//to[1]=(max[1]+min[1])/2;
		//to[2]=(max[2]+min[2])/2;
		printf("To: %.2f,%.2f,%.2f\n",to[0],to[1],to[2]);
		free(vert);
		vert=0;
		free(item[nextItem].poly);
		item[nextItem].poly=0;
		nextItem++;
	}
}

Vertex3DCNP *subdivVerts(Vertex3DCNP *in,int *inCount,float maxSide)
{
	// itterate through the verts, until there are none with sides longer than max
	printf("In: %d verts (%d poly)\n",*inCount,(*inCount)/3);
	int i;
	int extra=1;
	int maxsq=maxSide*maxSide;
	while(extra) {
		int vertCount=*inCount;
		extra=0;
		for(i=0;i<vertCount;i+=3) {
			float lensq[3];
			int j;
			for(j=0;j<3;j++) {
				int from=i+j;
				int to=i+((j+1)%3);
				
				float dx,dy,dz;
				dx=in[from].x-in[to].x;
				dy=in[from].y-in[to].y;
				dz=in[from].z-in[to].z;
				lensq[j]=dx*dx+dy*dy+dz*dz;
			}
			if(lensq[0]>maxsq || lensq[1]>maxsq || lensq[2]>maxsq) extra++;
		}
		printf("subdividing %d polygons in this pass.\n",extra);
		//printf("in was: %08x; ",(int)in);
		Vertex3DCNP *out=in;
		in=realloc(in,sizeof(Vertex3DCNP)*(vertCount+extra*3));
		//printf("in is: %08x; ",(int)in);
		if(!in) {
			printf("Crashed...out of memory during a subdiv.\n");
			return out;
		}
		int pos=vertCount;
		float maxlensq=0;
		for(i=0;i<vertCount;i+=3) {
			float lensq[3];
			int j;
			for(j=0;j<3;j++) {
				int from=i+j;
				int to=i+((j+1)%3);
				
				float dx,dy,dz;
				dx=in[from].x-in[to].x;
				dy=in[from].y-in[to].y;
				dz=in[from].z-in[to].z;
				lensq[j]=dx*dx+dy*dy+dz*dz;
				if(lensq[j]>maxlensq) {
					maxlensq=lensq[j];
				}
			}
			if(lensq[0]>maxsq || lensq[1]>maxsq || lensq[2]>maxsq) {
				// divide longest side.
				int side=2;
				if(lensq[0]>lensq[1] && lensq[0]>lensq[2]) {
					side=0;
				} else if(lensq[1]>lensq[2]) {
					side=1;
				} else {
					side=2;
				}
				int from=side;
				int to=((side+1)%3);
				in[pos]=in[i];
				in[pos+1]=in[i+1];
				in[pos+2]=in[i+2];
				in[i+to].x=(in[i+from].x+in[i+to].x)/2.0f;
				in[i+to].y=(in[i+from].y+in[i+to].y)/2.0f;
				in[i+to].z=(in[i+from].z+in[i+to].z)/2.0f;
				in[pos+from]=in[i+to];
				pos+=3;
			}
		}
		*inCount=pos;
	}
	printf("Out: %d verts (%d poly)\n",*inCount,(*inCount)/3);
	return in;
}


void loadItems()
{
	if(nextItem>0) return;	// Only once.
	drawSubwayLoading();
	//loadItem("subwaystation");
	loadItem("station");
	loadItem("trainnodoor");
	loadItem("leftdoor");
	loadItem("rightdoor");
	//item[0].vert=subdivVerts(item[0].vert,&item[0].polyCount,100);
	optimizeItem(&item[0]);
	if(nextItem>3) {
		//item[0].vert=subdivVerts(item[0].vert,&item[0].polyCount,100);
		item[1].vert=subdivVerts(item[1].vert,&item[1].polyCount,100);
		item[2].vert=subdivVerts(item[2].vert,&item[2].polyCount,100);
		item[3].vert=subdivVerts(item[3].vert,&item[3].polyCount,100);
	}
}

void freeItems()
{
	int i;
	for(i=0;i<nextItem;i++) {
		if(item[i].vert) free(item[i].vert);
		item[i].vert=0;
		if(item[i].poly) free(item[i].poly);
		item[i].poly=0;
		freeOcttree(item[i].oct);
		item[i].oct=0;
	}
	nextItem=0;
	freeCamera();
}

int getItemCount()
{
	return nextItem;
}

void initItems()
{
	printf("initing subway\n");
	initCamera();
	loadCameraPath("data/intro/walk.ase");
	cameraDoPath();
#ifdef _PSP
	sceGumMatrixMode(GU_PROJECTION);
	sceGumLoadIdentity();
	sceGumPerspective(45.0f,16.0f/9.0f,2.0f,1500.0f);
	sceGumMatrixMode(GU_VIEW);	
	sceGumLoadIdentity();
	sceGumMatrixMode(GU_MODEL);
	sceGumLoadIdentity();
	sceGuDepthRange(65535,0);
	sceGuDepthFunc(GU_GEQUAL);
	//sceGuDepthMask(GU_FALSE);
#endif
	//insertMessage("Loading subway station....",0,(0x80<<24)+(0x80<<8),4000);
}

void drawItems()
{
	int i;

	//printf("drawing subway\n");
#ifdef _PSP

	sceGuClearColor(0xff0f0f00);
	sceGuClearDepth(0);
	sceGuClear(GU_COLOR_BUFFER_BIT|GU_DEPTH_BUFFER_BIT);
	sceGuDisable(GU_TEXTURE_2D);
	sceGuDisable(GU_BLEND);
	sceGuEnable(GU_DEPTH_TEST);
	//sceGuDisable(GU_DEPTH_TEST);
	//sceGuDisable(GU_CULL_FACE);
	sceGuDisable(GU_ALPHA_TEST);
	//sceGuDisable(GU_CLIP_PLANES);

	sceGuEnable(GU_LIGHTING);
	sceGuEnable(GU_LIGHT0);
	sceGuEnable(GU_LIGHT1);
	sceGuEnable(GU_LIGHT2);
	ScePspFVector3 key={-1,-1,-1},fill={1,0,-1},back={0,-1,1};
	sceGuLight( 0, GU_DIRECTIONAL, GU_DIFFUSE_AND_SPECULAR, &key);
	sceGuLightColor( 0, GU_DIFFUSE, 0xffffffff);
	sceGuLightColor( 0, GU_SPECULAR, 0xffffffff);
	sceGuLightAtt(0,0.0f,1.0f,0.0f);
	sceGuLight( 1, GU_DIRECTIONAL, GU_DIFFUSE_AND_SPECULAR, &fill);
	sceGuLightColor( 1, GU_DIFFUSE, 0xff202020);
	sceGuLightColor( 1, GU_SPECULAR, 0xff000000);
	sceGuLightAtt(1,0.0f,1.0f,0.0f);
	sceGuLight( 2, GU_DIRECTIONAL, GU_DIFFUSE_AND_SPECULAR, &back);
	sceGuLightColor( 1, GU_DIFFUSE, 0xff808080);
	sceGuLightColor( 1, GU_SPECULAR, 0xff000000);
	sceGuLightAtt(2,0.0f,1.0f,0.0f);
	sceGuSpecular(12.0f);
	sceGuAmbient(0xff202020);
	sceGuAmbientColor(0xffffffff);

	sceGumMatrixMode(GU_VIEW);
	sceGumLoadIdentity();
	sceGumLookAt((ScePspFVector3 *)from,(ScePspFVector3 *)to,(ScePspFVector3 *)up);
	sceGumMatrixMode(GU_MODEL);
	sceGumLoadIdentity();
	sceGuFrontFace(GU_CCW);
	//sceGuFrontFace(GU_CW);
#endif
	for(i=0;i<nextItem;i++) {
#ifdef _PSP
		//printf("Drawing item %d\n",i);
		sceGumMatrixMode(GU_MODEL);
		sceGumPushMatrix();
		sceGumMultMatrix((ScePspFMatrix4 *)item[i].matrix);
#if 0
		sceGumDrawArray(GU_TRIANGLES,GU_VERTEX_32BITF|GU_COLOR_8888|GU_INDEX_16BIT|GU_TRANSFORM_3D,item[i].polyCount,item[i].poly,item[i].vert);
#else
		int j=0;
		while(j<item[i].polyCount) {
			int count=30720;
			if(j+count>item[i].polyCount) count=item[i].polyCount-j;
			//printf("Drawing %d polys\n",count);
			//sceGumDrawArray(GU_TRIANGLES,GU_VERTEX_32BITF|GU_NORMAL_32BITF|GU_COLOR_8888|GU_INDEX_16BIT|GU_TRANSFORM_3D,count,item[i].poly+j,item[i].vert);
			sceGumDrawArray(GU_TRIANGLES,GU_VERTEX_32BITF|GU_NORMAL_32BITF|GU_COLOR_8888|GU_TRANSFORM_3D,count,0,item[i].vert+j);
			j=j+count;
		}
		//drawOcttree(item[i].oct);
		drawTreeClipped(item[i].oct,50);
#endif
		sceGumPopMatrix();
#else
		drawOcttree(item[i].oct);
#endif
	}
#ifdef _PSP
	sceGuDisable(GU_LIGHTING);
	sceGuFrontFace(GU_CW);
	sceGuEnable(GU_TEXTURE_2D);
	sceGuEnable(GU_BLEND);
#endif
	//printf("drew subway\n");
}

int updateItems(long elapsed)
{
	static float theta=0;
	static float phi=3.1415926598;		//GU_PI;
	float dist=0;

	if(elapsed<1) elapsed=1;
	if(elapsed>1000) return 0;

#define ITEMFACTOR 92
#ifdef _PSP
	SceCtrlData pad;

	sceCtrlReadBufferPositive(&pad,1);

	if(pad.Buttons&PSP_CTRL_UP) theta+=0.15f*elapsed/ITEMFACTOR;
	if(pad.Buttons&PSP_CTRL_DOWN) theta-=0.15f*elapsed/ITEMFACTOR;
	if(pad.Buttons&PSP_CTRL_LEFT) phi-=0.15f*elapsed/ITEMFACTOR;
	if(pad.Buttons&PSP_CTRL_RIGHT) phi+=0.15f*elapsed/ITEMFACTOR;
	if(theta>GU_PI/2-0.1f) theta=GU_PI/2-0.1f;
	if(theta<-GU_PI/2+0.1f) theta=-GU_PI/2+0.1f;
	if(pad.Buttons&PSP_CTRL_CROSS) dist+=10.0f*elapsed/ITEMFACTOR;
	if(pad.Buttons&PSP_CTRL_TRIANGLE) dist-=10.0f*elapsed/ITEMFACTOR;
	if(pad.Lx<100) {
		item[1].matrix[14]-=(float)(pad.Lx-100)*elapsed/ITEMFACTOR;
		item[2].matrix[14]-=(float)(pad.Lx-100)*elapsed/ITEMFACTOR;
		item[3].matrix[14]-=(float)(pad.Lx-100)*elapsed/ITEMFACTOR;
	}
	if(pad.Lx>156) {
		item[1].matrix[14]-=(float)(pad.Lx-156)*elapsed/ITEMFACTOR;
		item[2].matrix[14]-=(float)(pad.Lx-156)*elapsed/ITEMFACTOR;
		item[3].matrix[14]-=(float)(pad.Lx-156)*elapsed/ITEMFACTOR;
	}
	if(pad.Buttons&PSP_CTRL_SQUARE) {
		if(item[3].matrix[14]>item[1].matrix[14]-20) {
			item[3].matrix[14]-=10.0f*elapsed/ITEMFACTOR;
			item[2].matrix[14]+=10.0f*elapsed/ITEMFACTOR;
		}
	}
	if(pad.Buttons&PSP_CTRL_CIRCLE) {
		if(item[3].matrix[14]<item[1].matrix[14]) {
			item[3].matrix[14]+=10.0f*elapsed/ITEMFACTOR;
			item[2].matrix[14]-=10.0f*elapsed/ITEMFACTOR;
			if(item[3].matrix[14]>item[1].matrix[14]) {
				item[3].matrix[14]=item[1].matrix[14];
				item[2].matrix[14]=item[1].matrix[14];
			}
		}
	}
	//if(pad.Lx<100 || pad.Lx>156) {
		//printf("subway at: %.2f,%.2f\n",item[1].matrix[12],item[1].matrix[14]);
	//}
	while(pad.Buttons&PSP_CTRL_START) {
		sceCtrlReadBufferPositive(&pad,1);
		
		if(!(pad.Buttons&PSP_CTRL_START)) return 1;
	}
	if(pad.Buttons&PSP_CTRL_SELECT) { // screenshot.
		char buf[64];
		static int cap=1;
		sprintf(buf,"lastcallsubway%d.png",cap++);
		savePngImage(buf,(u32 *)(0x04000000),480,272,512,0);
	}
#endif

#if 1
	float dx=cosf(theta)*cosf(phi);
	float dy=sinf(theta);
	float dz=cosf(theta)*sinf(phi);
	float wx=cosf(phi)*dist;
	float wz=sinf(phi)*dist;
	to[0]+=wx;
	//to[1]+=dy*dist;
	to[2]+=wz;
	//printf("%.2f/%.2f -> (%.2f,%.2f,%.2f)\n",theta,phi,dx,dy,dz);
	float weight,weight1;
	weight=0.1f*elapsed/ITEMFACTOR;
	weight1=1-weight;
	from[0]=weight1*(from[0]+wx)+weight*(to[0]-dx*30);
	from[1]=weight1*from[1]+weight*(to[1]-dy*30);
	from[2]=weight1*(from[2]+wz)+weight*(to[2]-dz*30);
#else
	cameraUpdate(elapsed);
	if(cameraActive()==0) {
		printf("Done path.\n");
		return 1;
	}
	printf("Path contunues...\n");
#endif
//	printf("%.2f %.2f %.2f: ",from[0],from[1],from[2]);
//	printf("to %.2f %.2f %.2f\n",to[0],to[1],to[2]);
	return 0;
}

