import java.awt.*;
import java.awt.event.*;
import java.awt.*;
import java.applet.*;
import java.io.*;
import java.lang.*;
import java.util.*;
import java.math.*;
import java.awt.image.*;

class Model
{
	public ModelName modelNames = new ModelName();
	public Md2File md2 = new Md2File(modelNames.currentJmdl());
	public JMd2Aux auxFile = new JMd2Aux(modelNames.currentJaux());
	
	public int actionNumber = 0;
	public int actionMax = auxFile.numAnims;
	
	int startFrame = auxFile.anims[actionNumber].animBeginFrame;
	int endFrame = auxFile.anims[actionNumber].animBeginFrame;
	
	int frame1 = startFrame;
	int frame2 = startFrame + 1;
	
	float angleX = 0;
	float angleY = 0;
	float angleZ = 0;
	
	boolean angleChanged = true;
	
	float ScaleX1 = 1;
	float ScaleY1 = 1;
	float ScaleZ1 = 1;
	
	float ScaleX2 = 1;
	float ScaleY2 = 1;
	float ScaleZ2 = 1;
	
	float TranslateX1 = 0;
	float TranslateY1 = 0;
	float TranslateZ1 = 0;
	
	float TranslateX2 = 0;
	float TranslateY2 = 0;
	float TranslateZ2 = 0;
	
	int timestep = 0;
	int totaltime = 5;
	

	float scalePX = 5F, scalePY = 5F;
	float transPX = 250, transPY = 250;
	float centreX,centreY,centreZ;
	
	
	public void reset() {
		actionNumber = 0;
		actionMax = auxFile.numAnims;
		
		startFrame = auxFile.anims[actionNumber].animBeginFrame;
		endFrame = auxFile.anims[actionNumber].animEndFrame;
		
		
		frame1 = startFrame;
		frame2 = startFrame + 1;
		if(frame2 > endFrame)
		{
			frame2 = frame1;
		}
		angleX = 0;
		angleY = 0;
		angleZ = 0;
		
		angleChanged = true;
		
		ScaleX1 = 1;
		ScaleY1 = 1;
		ScaleZ1 = 1;
		
		ScaleX2 = 1;
		ScaleY2 = 1;
		ScaleZ2 = 1;
		
		TranslateX1 = 0;
		TranslateY1 = 0;
		TranslateZ1 = 0;
		
		TranslateX2 = 0;
		TranslateY2 = 0;
		TranslateZ2 = 0;
		
		timestep = 0;
		totaltime = 5;
		
		float minY = 0,maxY = 0,minZ = 0,maxZ = 0,minX = 0,maxX = 0;
		
		for(int i=0; i < md2.head.data[8]; i++) 
		{
			float curX = md2.vertices[frame1][md2.triangles[i].index_xyz[0]].v[0];
			float curY = md2.vertices[frame1][md2.triangles[i].index_xyz[0]].v[1];
			float curZ = md2.vertices[frame1][md2.triangles[i].index_xyz[0]].v[2];
			if(i == 0)
			{
				minY = maxY = curY;
				minZ = maxZ = curZ;
				minX = maxX = curX;
			}
			else
			{
				if(curY < minY)
				{
					minY = curY;
				}
				if(curY > maxY)
				{
					maxY = curY;
				}
				if(curZ < minZ)
				{
					minZ = curZ;
				}
				if(curZ > maxZ)
				{
					maxZ = curZ;
				}
				if(curX < minX)
				{
					minX = curX;
				}
				if(curX > maxX)
				{
					maxX = curX;
				}
			}
		}
		centreX = (maxX + minX)/2;
		centreY = (maxY + minY)/2;
		centreZ = (maxZ + minZ)/2;
		// 48 ,40 ,347 + 48 ,529 + 40
		float scX = md2.frames[frame1].scale[0];
		float scY = md2.frames[frame1].scale[1];
		float scZ = md2.frames[frame1].scale[2];
		float TrX = md2.frames[frame1].translate[0];
		float TrY = md2.frames[frame1].translate[1];
		float TrZ = md2.frames[frame1].translate[2];
		transPX = 347 - (((maxY-minY)+TrY) * scY * scalePX);
		transPX /= 2;
		transPY = 529 - (((maxZ-minZ)+TrZ) * scZ * scalePY);
		transPY /= 2;
	}
	
	// int startFrame = 0;
	// int endFrame = md2.fileFormat.num_frames - 1;
	
	Transform Tstart = new Transform();
	Transform Tend = new Transform();
	
	int bufferX = 400, bufferY = 470;
	BufferedImage offScreenBuffer = new BufferedImage(bufferX,bufferY,BufferedImage.TYPE_INT_RGB);
	
	float projectX(float x, float z,float y) 
	{
		// Has to return X coordinate of screen
		return y;
	}
	
	float projectY(float y, float z,float x) 
	{
		// Has to return Y coordinate of screen
		return z;
	}
	
	
	
	void drawTriangle(float x1,float y1,float z1,
			float x2,float y2,float z2,
			float x3,float y3,float z3,Graphics g)
	{
		
		float xx1 = projectX(x1,z1,y1) * scalePX + transPX;
		float yy1 = projectY(y1,z1,x1) * scalePY + transPY;
		
		float xx2 = projectX(x2,z2,y2) * scalePX + transPX;
		float yy2 = projectY(y2,z2,x2) * scalePY + transPY;
		
		float xx3 = projectX(x3,z3,y3) * scalePX + transPX;
		float yy3 = projectY(y3,z3,x3) * scalePY + transPY;
		
		g.drawLine((int)xx1,529-(int)yy1,(int)xx2,529-(int)yy2);
		g.drawLine((int)xx2,529-(int)yy2,(int)xx3,529-(int)yy3);
		g.drawLine((int)xx3,529-(int)yy3,(int)xx1,529-(int)yy1);
		
	}
	
	public void setAngleX(int i)
	{
		/*Tstart.rotateX(angle);
		Tend.rotateX(angle);*/
		angleX += i * 0.1;
		if(angleX >= (float)(2 * 3.14159265358))
			angleX = 0;
		angleChanged = true;
	}
	
	public void setAngleY(int i)
	{
		/*Tstart.rotateY(angle);
		Tend.rotateY(angle);*/
		angleY += i * 0.1;
		if(angleY >= (float)(2 * 3.14159265358))
			angleY = 0;
		angleChanged = true;
	}
	
	public void setAngleZ(int i)
	{
		/*Tstart.rotateZ(angle);
		Tend.rotateZ(angle);*/
		angleZ += i * 0.1;
		if(angleZ >= (float)(2 * 3.14159265358))
			angleZ = 0;
		angleChanged = true;
	}
	
	public void setScaleSrc(float x,float y,float z)
	{
		Tstart.Scale(x,y,z);
	}
	
	public void setScaleDest(float x,float y,float z)
	{
		Tend.Scale(x,y,z);
	}
	
	public void setTranSrc(float x,float y,float z)
	{
		Tstart.Translate(x,y,z);
	}
	
	public void setTranDest(float x,float y,float z)
	{
		Tstart.Translate(x,y,z);
	}
	
	public void Draw(Graphics g,boolean play)
	{
		if(play)timestep ++;
		if(timestep == totaltime)
		{
			timestep = 0;
			if(frame2 == endFrame)
			{
				frame1 = startFrame;
				frame2 = frame1 + 1;
				if(frame2 > endFrame)
				{
					frame2 = frame1;
				}
			}
			else
			{
				frame1 ++;
				frame2 ++;
			}
		}
		boolean change = false;
		if(ScaleX1 != md2.frames[frame1].scale[0])
		{
			change = true;
			ScaleX1 = md2.frames[frame1].scale[0];
		}
		if(ScaleY1 != md2.frames[frame1].scale[1])
		{
			change = true;
			ScaleY1 = md2.frames[frame1].scale[1];
		}
		if(ScaleZ1 != md2.frames[frame1].scale[2])
		{
			change = true;
			ScaleZ1 = md2.frames[frame1].scale[2];
		}
		if(TranslateX1 != md2.frames[frame1].translate[0])
		{
			change = true;
			TranslateX1 = md2.frames[frame1].translate[0];
		}
		if(TranslateY1 != md2.frames[frame1].translate[1])
		{
			change = true;
			TranslateY1 = md2.frames[frame1].translate[1];
		}
		if(TranslateZ1 != md2.frames[frame1].translate[2])
		{
			change = true;
			TranslateZ1 = md2.frames[frame1].translate[2];
		}
		
		if(change || angleChanged)
		{
			// System.out.println(timestep);
			Tstart.Initialize();
			Tstart.Translate(TranslateX1,TranslateY1,TranslateZ1);
			Tstart.Scale(ScaleX1,ScaleY1,ScaleZ1);
			Tstart.Translate(-1*(centreX + TranslateX1)*ScaleX1,-1*(centreY + TranslateY1)*ScaleY1,-1*(centreZ + TranslateZ1)*ScaleZ1);
			Tstart.rotateX(angleX);
			Tstart.rotateY(angleY);
			Tstart.rotateZ(angleZ);
			Tstart.Translate((centreX + TranslateX1)*ScaleX1,(centreY + TranslateY1)*ScaleY1,(centreZ + TranslateZ1)*ScaleZ1);
		}
		
		change = false;
		if(ScaleX2 != md2.frames[frame2].scale[0])
		{
			change = true;
			ScaleX2 = md2.frames[frame2].scale[0];
		}
		if(ScaleY2 != md2.frames[frame2].scale[1])
		{
			change = true;
			ScaleY2 = md2.frames[frame2].scale[1];
		}
		if(ScaleZ2 != md2.frames[frame2].scale[2])
		{
			change = true;
			ScaleZ2 = md2.frames[frame2].scale[2];
		}
		if(TranslateX2 != md2.frames[frame2].translate[0])
		{
			change = true;
			TranslateX2 = md2.frames[frame2].translate[0];
		}
		if(TranslateY2 != md2.frames[frame2].translate[1])
		{
			change = true;
			TranslateY2 = md2.frames[frame2].translate[1];
		}
		if(TranslateZ2 != md2.frames[frame2].translate[2])
		{
			change = true;
			TranslateZ2 = md2.frames[frame2].translate[2];
		}
		
		if(change || angleChanged)
		{
			// System.out.println(timestep);
			Tend.Initialize();
			Tend.Translate(TranslateX2,TranslateY2,TranslateZ2);
			//System.out.println(ScaleX2 + "  " + ScaleY2 + "  " + ScaleZ2);
			Tend.Scale(ScaleX2,ScaleY2,ScaleZ2);
			Tend.Translate(-1*(centreX + TranslateX2)*ScaleX2,-1*(centreY + TranslateY2)*ScaleY2,-1*(centreZ + TranslateZ2)*ScaleZ2);
			Tend.rotateX(angleX);
			Tend.rotateY(angleY);
			Tend.rotateZ(angleZ);
			Tend.Translate((centreX + TranslateX2)*ScaleX2,(centreY + TranslateY2)*ScaleY2,(centreZ + TranslateZ2)*ScaleZ2);
		}

		float t = (float)timestep / (float)totaltime;
		angleChanged = false;
		
		
		Graphics offScreenGraphics = offScreenBuffer.getGraphics();
		
		offScreenGraphics.setColor(new Color(255,255,255));
		offScreenGraphics.fillRect(0,0,bufferX,bufferY);
		offScreenGraphics.setColor(new Color(0,0,0));
		
						
		
		for(int i=0; i < md2.head.data[8]; i++) 
		{
			/*Now transform the points and get the 2 final points only if some data is changed*/
			Tstart.transformPt((md2.vertices[frame1][md2.triangles[i].index_xyz[0]].v[0]),
				(md2.vertices[frame1][md2.triangles[i].index_xyz[0]].v[1]),
				(md2.vertices[frame1][md2.triangles[i].index_xyz[0]].v[2]));
			
			float x11 = Tstart.getX();
			float y11 = Tstart.getY();
			float z11 = Tstart.getZ();
					
			Tend.transformPt((md2.vertices[frame2][md2.triangles[i].index_xyz[0]].v[0]),
				(md2.vertices[frame2][md2.triangles[i].index_xyz[0]].v[1]),
				(md2.vertices[frame2][md2.triangles[i].index_xyz[0]].v[2]));
			
			float x12 = Tend.getX();
			float y12 = Tend.getY();
			float z12 = Tend.getZ();
			
					
			Tstart.transformPt((md2.vertices[frame1][md2.triangles[i].index_xyz[1]].v[0]),
				(md2.vertices[frame1][md2.triangles[i].index_xyz[1]].v[1]),
				(md2.vertices[frame1][md2.triangles[i].index_xyz[1]].v[2]));
			
			float x21 = Tstart.getX();
			float y21 = Tstart.getY();
			float z21 = Tstart.getZ();
			
			Tend.transformPt((md2.vertices[frame2][md2.triangles[i].index_xyz[1]].v[0]),
				(md2.vertices[frame2][md2.triangles[i].index_xyz[1]].v[1]),
				(md2.vertices[frame2][md2.triangles[i].index_xyz[1]].v[2]));
					
			float x22 = Tend.getX();
			float y22 = Tend.getY();
			float z22 = Tend.getZ();
			
			Tstart.transformPt((md2.vertices[frame1][md2.triangles[i].index_xyz[2]].v[0]),
				(md2.vertices[frame1][md2.triangles[i].index_xyz[2]].v[1]),
				(md2.vertices[frame1][md2.triangles[i].index_xyz[2]].v[2]));
					
			float x31 = Tstart.getX();
			float y31 = Tstart.getY();
			float z31 = Tstart.getZ();
					
			Tend.transformPt((md2.vertices[frame2][md2.triangles[i].index_xyz[2]].v[0]),
				(md2.vertices[frame2][md2.triangles[i].index_xyz[2]].v[1]),
				(md2.vertices[frame2][md2.triangles[i].index_xyz[2]].v[2]));
					
			float x32 = Tend.getX();
			float y32 = Tend.getY();
			float z32 = Tend.getZ();
			
			
			float x1 = x11 * (1 -t) + x12 * t;
			float y1 = y11 * (1 -t) + y12 * t;
			float z1 = z11 * (1 -t) + z12 * t;
			
			float x2 = x21 * (1 -t) + x22 * t;
			float y2 = y21 * (1 -t) + y22 * t;
			float z2 = z21 * (1 -t) + z22 * t;
			
			float x3 = x31 * (1 -t) + x32 * t;
			float y3 = y31 * (1 -t) + y32 * t;
			float z3 = z31 * (1 -t) + z32 * t;
			
			drawTriangle(x1,y1,z1,x2,y2,z2,x3,y3,z3,offScreenGraphics);
			// drawTriangle(x1,y1,z1,x2,y2,z2,x3,y3,z3,g);
		} 
		
		
		g.setClip(48 ,40 ,347 + 48 ,529 + 40);
		// Md2.md2Frame.imageUpdate(offScreenBuffer,0,48 ,40 ,347,529);
		g.drawImage(offScreenBuffer,48,45,null);
		g.setClip(0,0,800,600);
		
	}
	
	void repaintModel(boolean playOrPause) {
		
		// Md2.md2Frame.repaint(48 ,40 ,347 + 48 ,529 + 40);
		try {
			
			Graphics g = Md2.md2Frame.getGraphics();
			Draw(g,playOrPause);
		}
		catch(Exception e) {
			// Just shut up all the exceptions that try to get outta here
			// System.out.println("Caught an exception : " + e);
		}
	}
}