package object;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Scanner;

import javax.media.opengl.GL;

import devUtil.Vector3;

public class Model {
	
	private ArrayList<Vertex> geometricVerticies; // v
	private ArrayList<Vertex> textureVerticies; // vt
	private ArrayList<Vertex> vertexNormals; // vn
	
	private ArrayList<Face> faces; // f
	
	private boolean hasNormals;
	private boolean hasTextures;
	
	public Model() {
		
		geometricVerticies = new ArrayList<Vertex>();
		textureVerticies = new ArrayList<Vertex>();
		vertexNormals = new ArrayList<Vertex>();
		
		faces = new ArrayList<Face>();
		
		setSize(1, 1, 1);
		setColor(.5f, .5f, .5f);
		
		hasNormals = true;
		hasTextures = true;
	}
	
	public void setMode(boolean hasNormals, boolean hasTextures) {
		this.hasNormals = hasNormals;
		this.hasTextures = hasTextures;
	}
	
	private double xMin = Double.MAX_VALUE;
	private double xMax = Double.MIN_VALUE;
	
	private double yMin = Double.MAX_VALUE;
	private double yMax = Double.MIN_VALUE;
	
	private double zMin = Double.MAX_VALUE;
	private double zMax = Double.MIN_VALUE;
	
	public void parseObjectFile(InputStream file) {
		
		@SuppressWarnings("resource")
		Scanner sc = new Scanner(file);
		
		ArrayList<String> faceList = new ArrayList<String>();
		
		while(sc.hasNextLine()) {
			
			String line = sc.nextLine();
			
			if(line.indexOf("v ") != -1) {
				
				line = line.substring(line.indexOf(" "));
				
				float z = Float.parseFloat(line.substring(line.lastIndexOf(" ")));
				
				if(z < zMin)
					zMin = z;
				
				if(z > zMax)
					zMax = z;
				
				line = line.substring(0, line.lastIndexOf(" "));
				float y = Float.parseFloat(line.substring(line.lastIndexOf(" ")));
				
				if(y < yMin)
					yMin = y;
				
				if(y > yMax)
					yMax = y;
				
				line = line.substring(0, line.lastIndexOf(" "));
				float x = Float.parseFloat(line.substring(line.lastIndexOf(" ")));
				
				if(x < xMin)
					xMin = x;
				
				if(x > xMax)
					xMax = x;
				
				geometricVerticies.add(new Vertex(x, y, z));
			}
			
			if(line.indexOf("vt ") != -1 && hasTextures) {
				
				line = line.substring(line.indexOf(" ") + 1);
				
				float z = Float.parseFloat(line.substring(line.lastIndexOf(" ")));
				
				line = line.substring(0, line.lastIndexOf(" "));
				float y = Float.parseFloat(line.substring(line.lastIndexOf(" ")));
				
				line = line.substring(0, line.lastIndexOf(" "));
				float x = Float.parseFloat(line);
				
				textureVerticies.add(new Vertex(x, y, z));
			}
			
			if(line.indexOf("vn ") != -1 && hasNormals) {
				
				line = line.substring(line.indexOf(" ") + 1);
				
				float z = Float.parseFloat(line.substring(line.lastIndexOf(" ")));
				
				line = line.substring(0, line.lastIndexOf(" "));
				float y = Float.parseFloat(line.substring(line.lastIndexOf(" ")));
				
				line = line.substring(0, line.lastIndexOf(" "));
				float x = Float.parseFloat(line);
				
				vertexNormals.add(new Vertex(x, y, z));
			}
			
			if(line.indexOf("f ") != -1) {
				line = line.substring(line.indexOf(" ") + 1);
				faceList.add(line);
			}
		}
		
		for(int i = 0; i < faceList.size(); i++) {
			
			String face = faceList.get(i);
			face = face.trim() + " ";
			
			ArrayList<String> verticies = new ArrayList<String>();
			
			while(face.indexOf(" ") != -1) {
				verticies.add(face.substring(0, face.indexOf(" ")));
				face = face.substring(face.indexOf(" ") + 1);
			}
			
			ArrayList<Vertex> geometry = new ArrayList<Vertex>();
			ArrayList<Vertex> texture = new ArrayList<Vertex>();
			ArrayList<Vertex> normals = new ArrayList<Vertex>();
			
			for(int v = 0; v < verticies.size(); v++) {
				
				String vertex = verticies.get(v) + " ";
				
				if(vertex.indexOf("/") != -1) {
					
					if(!vertex.substring(0, vertex.indexOf("/")).equals("")) {
					
						int geometricVertex = Integer.parseInt(vertex.substring(0, vertex.indexOf("/")));
						geometry.add(geometricVerticies.get(geometricVertex - 1));
					}
					
					vertex = vertex.substring(vertex.indexOf("/") + 1);
					
					if(hasTextures) {
						
						if(!vertex.substring(0, vertex.indexOf("/")).equals("")) {
						
							int textureVertex = Integer.parseInt(vertex.substring(0, vertex.indexOf("/")));
							texture.add(textureVerticies.get(textureVertex - 1));
						}
					}
					
					vertex = vertex.substring(vertex.indexOf("/") + 1);
					
					if(hasNormals) {
						
						if(!vertex.equals("")) {
		
							int vertexNormal = Integer.parseInt(vertex.substring(0, vertex.indexOf(" ")));
							normals.add(vertexNormals.get(vertexNormal - 1));
						}
					}
				}
				
				else {
					int geometricVertex = Integer.parseInt(vertex.substring(0, vertex.indexOf(" ")));
					geometry.add(geometricVerticies.get(geometricVertex - 1));
				}
			}
			
			faces.add(new Face(geometry, texture, normals));
			faces.get(faces.size() - 1).setMode(hasNormals, hasTextures);
		}
		
		setSize((float) (xMax - xMin), (float) (yMax - yMin), (float) (zMax - zMin));
	}
	
	// rendering
	
	float red;
	float green;
	float blue;
	
	public void setColor(float red, float green, float blue) {
		this.red = red;
		this.green = green;
		this.blue = blue;
	}
	
	float xSize;
	float ySize;
	float zSize;
	
	float xPosition;
	float yPosition;
	float zPosition;
	
	float xRotation;
	float yRotation;
	float zRotation;
	
	public void render(GL gl) {
		
		gl.glTranslatef(xPosition, yPosition, zPosition);
		
		gl.glRotatef(xRotation, 1, 0, 0);
		gl.glRotatef(yRotation, 0, 1, 0);
		gl.glRotatef(zRotation, 0, 0, 1);
		
		gl.glColor3f(red, green, blue);
		
		for(int i = 0; i < faces.size(); i++) {
			
			faces.get(i).setDimensions(xSize, ySize, zSize,
					Math.abs((xMax - xMin)),
					Math.abs((yMax - yMin)),
					Math.abs((zMax - zMin)));
			
			faces.get(i).render(gl);
		}
		
		gl.glColor3f(1, 1, 1);
		
		gl.glRotatef(-xRotation, 1, 0, 0);
		gl.glRotatef(-yRotation, 0, 1, 0);
		gl.glRotatef(-zRotation, 0, 0, 1);
		
		gl.glTranslatef(-xPosition, -yPosition, -zPosition);
	}
	
	public void setSize(float xSize, float ySize, float zSize) {
		this.xSize = xSize;
		this.ySize = ySize;
		this.zSize = zSize;
	}
	
	public void setSize(Vector3 size) {
		xSize = (float) size.getX();
		ySize = (float) size.getY();
		zSize = (float) size.getZ();
	}
	
	public void setXSize(float xSize) {
		this.xSize = xSize;
	}
	
	public void setYSize(float ySize) {
		this.ySize = ySize;
	}
	
	public void setZSize(float zSize) {
		this.zSize = zSize;
	}
	
	public void setPosition(float xPosition,
			float yPosition,
			float zPosition) {
		
		this.xPosition = xPosition;
		this.yPosition = yPosition;
		this.zPosition = zPosition;
	}
	
	public void setPosition(Vector3 position) {
		xPosition = (float) position.getX();
		yPosition = (float) position.getY();
		zPosition = (float) position.getZ();
	}
	
	public void setXPosition(float xPosition) {
		this.xPosition = xPosition;
	}
	
	public void setYPosition(float yPosition) {
		this.yPosition = yPosition;
	}
	
	public void setZPosition(float zPosition) {
		this.zPosition = zPosition;
	}
	
	public void setRotation(float xRotation,
			float yRotation,
			float zRotation) {
		
		this.xRotation = xRotation;
		this.yRotation = yRotation;
		this.zRotation = zRotation;
	}
	
	public void setRotation(Vector3 rotation) {
		xRotation = (float) rotation.getX();
		yRotation = (float) rotation.getY();
		zRotation = (float) rotation.getZ();
	}
	
	public void setXRotation(float xRotation) {
		this.xRotation = xRotation;
	}
	
	public void setYRotation(float yRotation) {
		this.yRotation = yRotation;
	}
	
	public void setZRotation(float zRotation) {
		this.zRotation = zRotation;
	}
	
	// getters
	
	// size getters
	
	public Vector3 getSize() {
		return new Vector3(xSize, ySize, zSize);
	}
	
	public float getXSize() {
		return xSize;
	}
	
	public float getYSize() {
		return ySize;
	}
	
	public float getZSize() {
		return zSize;
	}
	
	// position getters
	
	public Vector3 getPosition() {
		return new Vector3(xPosition, yPosition, zPosition);
	}
	
	public float getXPosition() {
		return xPosition;
	}
	
	public float getYPosition() {
		return yPosition;
	}
	
	public float getZPosition() {
		return zPosition;
	}
	
	// rotation getters
	
	public Vector3 getRotation() {
		return new Vector3(xRotation, yRotation, zRotation);
	}
	
	public float getXRotation() {
		return xRotation;
	}
	
	public float getYRotation() {
		return yRotation;
	}
	
	public float getZRotation() {
		return zRotation;
	}
}

class Vertex {
	
	private float x;
	private float y;
	private float z;
	
	public Vertex(float x, float y, float z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}
	
	public float getX() {
		return x;
	}
	
	public float getY() {
		return y;
	}
	
	public float getZ() {
		return z;
	}
	
	public String toString() {
		return x + ", " + y + ", " + z;
	}
}

@SuppressWarnings("unused")
class Face {
	
	private ArrayList<Vertex> geometricVerticies;
	private ArrayList<Vertex> textureVerticies;
	private ArrayList<Vertex> vertexNormals;
	
	private double xSize;
	private double ySize;
	private double zSize;
	
	private double xRange;
	private double yRange;
	private double zRange;
	
	private boolean hasNormals;
	private boolean hasTextures;
	
	public Face(ArrayList<Vertex> geometricVerticies,
			ArrayList<Vertex> textureVerticies,
			ArrayList<Vertex> vertexNormals) {
		
		this.geometricVerticies = geometricVerticies;
		this.textureVerticies = textureVerticies;
		this.vertexNormals = vertexNormals;
	}
	
	public void setMode(boolean hasNormals, boolean hasTextures) {
		this.hasNormals = hasNormals;
		this.hasTextures = hasTextures;
	}
	
	public void setDimensions(
			double xSize, double ySize, double zSize,
			double xRange, double yRange, double zRange) {
		
		this.xSize = xSize;
		this.ySize = ySize;
		this.zSize = zSize;
		
		this.xRange = xRange;
		this.yRange = yRange;
		this.zRange = zRange;
	}
	
	public void render(GL gl) {
		
		gl.glBegin(GL.GL_POLYGON);
		
		for(int i = 0; i < geometricVerticies.size(); i++) {
			
			float xScale = (float) (xSize / xRange);
			float yScale = (float) (ySize / yRange);
			float zScale = (float) (zSize / zRange);
			
			if(hasNormals)
				gl.glNormal3f(vertexNormals.get(i).getX(),
						vertexNormals.get(i).getY(),
						vertexNormals.get(i).getZ());
			
			gl.glVertex3f(geometricVerticies.get(i).getX() * xScale,
					geometricVerticies.get(i).getY() * yScale,
					geometricVerticies.get(i).getZ() * zScale);
		}
		
		gl.glEnd();
	}
	
	public ArrayList<Vertex> getGeometricVerticies() {
		return geometricVerticies;
	}
}