/**
 * package for classes used to load 3d models
 */
package simogl.model.modelloader;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import com.sun.opengl.util.texture.Texture;

import simogl.geometry.Face;
import simogl.geometry.MeshVA;
import simogl.geometry.Point3D;
import simogl.geometry.Triangle;
import simogl.texture.TextureCoordinate;

/**
 * The MD2Importer can be used to load models in Quake2 file format 
 * 
 * @author Malte Mathiszig
 */
public class MD2Importer 
{
	private static float scaleFactor = 0.05f;
	
	/**
	 * Loads a model from a md2 file and makes a mesh of it. 
	 * 
	 * @param file md2 model file from the resource folder
	 * @param keyframe       number of the keyframe that ought to be imported
	 * @param preferredSkin  number of the skin that ought to be imported
	 * @param texture the texture for the model
	 * @param scale a scale factor that effects the size of the model
	 * @return A vertex array mesh of the model
	 */
	public static MeshVA importModel(File file, int keyframe, int preferredSkin, Texture texture, float scale) 
	{
		scaleFactor = scale;
		
		MeshVA mesh = new MeshVA();		
		
		try 
		{			
			FileInputStream fileStream = new FileInputStream(file);
			MD2InputStream  md2 = new MD2InputStream(fileStream);			
			
			// read header information
			String idp2 =  md2.readString(4); // (*)			
			int version = md2.readInt(); // (*)			
			int texWidth = md2.readInt();
			int texHeight = md2.readInt();
			int frameSize = md2.readInt(); // (*)
			 
			int numTextures = md2.readInt();			
			int numVertices = md2.readInt();
			int numTexCoords = md2.readInt();
			int numTriangles = md2.readInt();
			int numGLCommands = md2.readInt(); // (*)
			int numKeyframes = md2.readInt();			
			 
			int texOffset = md2.readInt();
			int uvOffset = md2.readInt();
			int triangleOffset = md2.readInt(); // (*)
			int keyframeOffset = md2.readInt(); // (*)
			int glCmdOffset = md2.readInt(); // (*)
			int eofOffset = md2.readInt(); // (*)
			
			// read texture name
			String textureName = null;
			int bytesToSkip = uvOffset - texOffset -1;
			for (int i = 0; i<numTextures; i++)	{
				String skinName = md2.readString();				
				bytesToSkip -= skinName.length();
				if (i == preferredSkin) textureName = skinName;
			}
			if (bytesToSkip > 0) md2.skip(bytesToSkip);
			
			// read texture coordinates
			TextureCoordinate[] textureCoords = new TextureCoordinate[numTexCoords];
			for (int i = 0; i<numTexCoords; i++) {
				float u = (float) md2.readShort() / (float) texWidth;
				float v = (float) md2.readShort() / (float) texHeight;
				textureCoords[i] = new TextureCoordinate(u,v);
			}

			// read triangles
			short[][] triangleVertices = new short[numTriangles][3];
			short[][] triangleCoords = new short[numTriangles][3];
			for (int i = 0; i<numTriangles; i++) {
				triangleVertices[i][0] = md2.readShort();
				triangleVertices[i][1] = md2.readShort();
				triangleVertices[i][2] = md2.readShort();
				triangleCoords[i][0] = md2.readShort();
				triangleCoords[i][1] = md2.readShort();
				triangleCoords[i][2] = md2.readShort();
			}
			

			// read frames
			Point3D[][] vertices = new Point3D[numVertices][numKeyframes];
			Point3D[][] normalIndices = new Point3D[numVertices][numKeyframes];
			for (int i = 0; i<numKeyframes; i++) 
			{
				
				// read transformations
				float scaleZ = md2.readFloat();
				float scaleX = md2.readFloat();
				float scaleY = md2.readFloat();
				Point3D scaleVector = new Point3D(scaleX, scaleY, scaleZ);				
				
				float transZ = md2.readFloat();
				float transX = md2.readFloat();
				float transY = md2.readFloat();
				Point3D translateVector = new Point3D(transX, transZ, transY);
				
				// read frame name
				String frameName = md2.readString(); // (*)
				//System.out.println(frameName);
				md2.skip(15 - frameName.length());
				
				// read and transform vertices
				for (int j = 0; j<numVertices; j++) {
					float z = -md2.read();
					float x = md2.read();
					float y = md2.read();
					vertices[j][i] = new Point3D(x,y,z);
					
					//vertices[j][i].add(translateVector);
					vertices[j][i].setX(vertices[j][i].getX() * scaleVector.getX());
					vertices[j][i].setY(vertices[j][i].getY() * scaleVector.getY());
					vertices[j][i].setZ(vertices[j][i].getZ() * scaleVector.getZ());					
					vertices[j][i].multiply(scaleFactor);
					
					int normalID = md2.read();
					/*
					normalIndices[j][i] = new Point3D(
							MD2Normals.data[normalID][0],
							MD2Normals.data[normalID][1],
							MD2Normals.data[normalID][2]);
					*/
				}
			}
						
			mesh.setTexture(texture);

			// create triangles
			Point3D p1 = null;
			Point3D p2 = null;
			Point3D p3 = null;
			Triangle triangle1 = null;
			Triangle triangle2 = null;
			TextureCoordinate texCoordT1V1 = null;
			TextureCoordinate texCoordT1V2 = null;
			TextureCoordinate texCoordT1V3 = null;
			TextureCoordinate texCoordT2V1 = null;
			TextureCoordinate texCoordT2V2 = null;
			TextureCoordinate texCoordT2V3 = null;
			Face face = null;
			
			for (int i=0; i<numTriangles; i++)
			{				
				p1 = vertices[triangleVertices[i][2]][keyframe];
				p2 = vertices[triangleVertices[i][1]][keyframe];
				p3 = vertices[triangleVertices[i][0]][keyframe];
				
				texCoordT1V1 = textureCoords[triangleCoords[i][2]];
				texCoordT1V2 = textureCoords[triangleCoords[i][1]];
				texCoordT1V3 = textureCoords[triangleCoords[i][0]];
				
				triangle1 = new Triangle(p1,p2,p3);
				//triangle1.setNormal(normalIndices[triangleVertices[i][0]][keyframe]);
				//triangle1.setNormal(normalIndices[triangleVertices[i][1]][keyframe]);
				//triangle1.setNormal(normalIndices[triangleVertices[i][2]][keyframe]);

				if (i<numTriangles)i++; // increment counter to next triangle
									
				p1 = vertices[triangleVertices[i][2]][keyframe];
				p2 = vertices[triangleVertices[i][1]][keyframe];				
				p3 = vertices[triangleVertices[i][0]][keyframe];
				
				texCoordT2V1 = textureCoords[triangleCoords[i][2]];
				texCoordT2V2 = textureCoords[triangleCoords[i][1]];
				texCoordT2V3 = textureCoords[triangleCoords[i][0]];				
				
				triangle2 = new Triangle(p1,p2,p3);	
				//triangle2.setNormal(normalIndices[triangleVertices[i][0]][keyframe]);
				//triangle2.setNormal(normalIndices[triangleVertices[i][1]][keyframe]);
				//triangle2.setNormal(normalIndices[triangleVertices[i][2]][keyframe]);
				
				
				face = new Face(triangle1,triangle2);				
				face.getTriangle1().setTexCoordsVertex1(texCoordT1V1.getS(),texCoordT1V1.getT());
				face.getTriangle1().setTexCoordsVertex2(texCoordT1V2.getS(),texCoordT1V2.getT());
				face.getTriangle1().setTexCoordsVertex3(texCoordT1V3.getS(),texCoordT1V3.getT());
				face.getTriangle2().setTexCoordsVertex1(texCoordT2V1.getS(),texCoordT2V1.getT());
				face.getTriangle2().setTexCoordsVertex2(texCoordT2V2.getS(),texCoordT2V2.getT());
				face.getTriangle2().setTexCoordsVertex3(texCoordT2V3.getS(),texCoordT2V3.getT());
								
				mesh.addFace(face);			
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}	
		mesh.createVertexArray();
		
		return mesh;
	}
}