import java.io.*;
import java.nio.FloatBuffer;
import java.util.ArrayList;

import javax.media.opengl.*;
import javax.media.opengl.glu.GLU;

import com.sun.opengl.util.FPSAnimator;
import com.sun.opengl.util.GLUT;
import com.sun.opengl.util.texture.*;


/**
 * CS400 3DMotionCapture
 * CMCAnimationThread
 * 
 * Display animation according to the data given from MarkerTracking class
 * Due to the I/O input slowness, this program always run after CMCVideoThread. 
 * To notify to CMCAnimation, you should update this program at the end of display function.
 * 
 * @author Kohei Shinkawa
 * @version 3-11-2010
 * */
public class CMCAnimationThread3D implements Runnable, GLEventListener {
	private Texture be, beb, bm, background;
	private GLU glu = new GLU();
	private GLUT glut = new GLUT();
	private CMCThreadInfo info; /*Shared wrapper class from CMCAnimationPanel*/
	private boolean loaded =  false;
	
	GLJPanel canvas; 
	double[][] coordsArray; 
	FPSAnimator animator;
	private BenderSkin skin;
	private float scale; /*variable to scale the objects so that they are in 1-0 3Ddimension*/
	private double zmin; /*Variable to level the floor up so that the foot looks like it's on the ground*/

	
	/**Constructor for the class.*/
	public CMCAnimationThread3D(double[][] coords, GLJPanel canvas, int fps, CMCThreadInfo info, float scale, double zmin){	
		this.info = info;
		this.canvas = canvas;
		this.scale = scale; 
		this.zmin = zmin;
		skin = new BenderSkin("Skin");

		animator = new FPSAnimator(this.canvas, fps);
		animator.setRunAsFastAsPossible(true);
		coordsArray = coords;
		canvas.addGLEventListener(this);
		canvas.requestFocus();
	}

	public void run() {
		/*Animator calls display at the constant rate*/
		animator.start();	
	}

	/**The method to be called repeatedly by fpsanimator*/
	public void display(GLAutoDrawable glDrawable) {

		if(info.isInit() && info.isReady()){
			/*If it's still in the initializing state do nothing*/
			final GL gl = glDrawable.getGL();

			gl.glLoadIdentity();
			gl.glMatrixMode(GL.GL_MODELVIEW);

			gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
			gl.glClear(GL.GL_COLOR_BUFFER_BIT);
			info.setInit(false);
			info.setReady(false);

		}
		else if(!info.isReady() || !info.isDone()){
			/*Wait for CMCVideoThread*/
		}

		else if((info.getCurFrame() > info.getEndFrame()-1 && !info.isRewind())|| (info.getCurFrame() <= info.getEndFrame()-1 && info.isRewind()) ) {
			/*Do nothing since it's end of frames*/ 			
		}

		else{
			/*Draw the current frame*/
			if(info.getCurFrame() >= coordsArray.length) info.setCurFrame(coordsArray.length-1);
			final GL gl = glDrawable.getGL();
			
			/*Load the picture once to get ready for the skin*/
			if(!loaded){
				try {
					be = TextureIO.newTexture(new File("Skin/be.gif"), true);
					beb = TextureIO.newTexture(new File("Skin/beb.gif"), true);
					bm = TextureIO.newTexture(new File("Skin/bm.gif"), true);
					background = TextureIO.newTexture(new File("Skin/New_New_York_Cityscape.jpg"), true);

					loaded = true;
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

			gl.glPushMatrix();
			
			/*Set up the Opengl views*/
			gl.glMatrixMode(GL.GL_PROJECTION);
			gl.glLoadIdentity();
			gl.glFrustum(-.2, .2, -.2, .2, 0.8, 8);

			gl.glMatrixMode(GL.GL_MODELVIEW);
			gl.glLoadIdentity();
			//(202.016, 217.434, 68.2965) is the center camera world-coordinates for the data we use
			glu.gluLookAt(202*scale, 217*scale, 68.2965*scale, 1.0/2, 1.0/2, 1.0/2, 0, 0, 1);
	
			/*If rotation is requested from CMCGui do it*/
			double[] rotate = info.getRotation();
			gl.glRotated(rotate[0], 1, 0,0);
			gl.glRotated(rotate[1], 0, 1,0);
			gl.glRotated(rotate[2], 0, 0,1);

			gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
			gl.glShadeModel(GL.GL_SMOOTH);

			/*Set lights*/
			float[] light_position = new float[]{100.0f,100.0f,100f,0};
			float[] light_position1 = {500f,500f,50f,0};
			float[] mat_ambient = 	{ .0f, .5f, .0f, .0f };
			float[] mat_ambient2 = { .7f, .0f, .0f, .0f };
			float[] mat_ambient3 = { .0f, .0f, .5f, .0f };		
			float[] mat_ambient4 = 	{ .3f, .3f, .3f, .0f };

			float[] mat_diffuse = { .2f, 0.2f, 0.2f, .5f };
			float[] mat_specular = { .2f, .2f, .2f, .2f };
			float[] mat_shininess = { 0.0f };

			float light_ambient[] =
			{ 0.3f, 0.3f, 0.3f, 1.0f };
			float light_diffuse[] =
			{ .7f, .7f, .7f, .7f };
			float light_specular[] =
			{ .7f, .7f, .7f, .7f };

			FloatBuffer lightPos = FloatBuffer.wrap(light_position);	    
			gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, light_ambient, 0);
			gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, light_diffuse, 0);
			gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, light_specular, 0);
			gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, lightPos);			
			lightPos = FloatBuffer.wrap(light_position1);
			gl.glLightfv(GL.GL_LIGHT1, GL.GL_AMBIENT, light_ambient, 0);
			gl.glLightfv(GL.GL_LIGHT1, GL.GL_DIFFUSE, light_diffuse, 0);
			gl.glLightfv(GL.GL_LIGHT1, GL.GL_SPECULAR, light_specular, 0);
			gl.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, lightPos);			
			gl.glLightModeli(GL.GL_LIGHT_MODEL_TWO_SIDE, GL.GL_TRUE);

			gl.glEnable(GL.GL_LIGHTING);
			gl.glEnable(GL.GL_DEPTH_TEST);
			gl.glEnable(GL.GL_NORMALIZE);
			gl.glEnable(GL.GL_LIGHT0);		
			gl.glEnable(GL.GL_LIGHT1);
			gl.glEnable(GL.GL_TEXTURE_2D);	    
			
			/*Draw background for Bender*/
			if(info.getSkin() == 2){
				TextureCoords coord = background.getImageTexCoords();
				background.bind();
				background.enable();

				gl.glPushMatrix();
				gl.glTranslated(0, 0, zmin*scale);
				gl.glBegin(GL.GL_QUADS);

				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, FloatBuffer.wrap(mat_ambient4));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, FloatBuffer.wrap(mat_diffuse));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, FloatBuffer.wrap(mat_specular));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, FloatBuffer.wrap(mat_shininess));

				gl.glTexCoord2d((coord.right()+coord.left())/2,coord.bottom());
				gl.glVertex3d(0f, 0f, 0);
				gl.glTexCoord2d((coord.right()+coord.left())/2,coord.top());
				gl.glVertex3d(0f, 0f, 3);
				gl.glTexCoord2d(coord.left(),coord.top());
				gl.glVertex3d(3f, 0f, 3);
				gl.glTexCoord2d(coord.left(), coord.bottom());
				gl.glVertex3d(3f, 0f, 0);

				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, FloatBuffer.wrap(mat_ambient4));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, FloatBuffer.wrap(mat_specular));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, FloatBuffer.wrap(mat_specular));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, FloatBuffer.wrap(mat_shininess));

				gl.glTexCoord2d((coord.right()+coord.left())/2,coord.bottom());
				gl.glVertex3d(0f, 0, 0);
				gl.glTexCoord2d(coord.right(),coord.bottom());
				gl.glVertex3d(0f, 3, 0);
				gl.glTexCoord2d(coord.right(),coord.top());
				gl.glVertex3d(0f, 3, 3);
				gl.glTexCoord2d((coord.right()+coord.left())/2,coord.top());
				gl.glVertex3d(0f, 0, 3);

				background.disable();
				gl.glEnd(); 

				gl.glBegin(GL.GL_QUADS);
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, FloatBuffer.wrap(mat_ambient4));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, FloatBuffer.wrap(mat_specular));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, FloatBuffer.wrap(mat_specular));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, FloatBuffer.wrap(mat_shininess));

				gl.glVertex3d(0, 0f, 0);
				gl.glVertex3d(2, 0f, 0);
				gl.glVertex3d(2, 2f, 0);
				gl.glVertex3d(0, 2f, 0);

				gl.glEnd(); 
				gl.glPopMatrix();
			}
			/*Draw background for other cases*/
			else{
				gl.glPushMatrix();
				gl.glTranslated(0, 0, zmin*scale);
				gl.glBegin(GL.GL_QUADS);

				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, FloatBuffer.wrap(mat_ambient3));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, FloatBuffer.wrap(mat_diffuse));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, FloatBuffer.wrap(mat_specular));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, FloatBuffer.wrap(mat_shininess));

				gl.glVertex3d(0f, 0f, 0);
				gl.glVertex3d(0f, 0f, 3);
				gl.glVertex3d(3f, 0f, 3);
				gl.glVertex3d(3f, 0f, 0);

				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, FloatBuffer.wrap(mat_ambient));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, FloatBuffer.wrap(mat_specular));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, FloatBuffer.wrap(mat_specular));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, FloatBuffer.wrap(mat_shininess));
				gl.glVertex3d(0f, 0, 0);
				gl.glVertex3d(0f, 3, 0);
				gl.glVertex3d(0f, 3, 3);
				gl.glVertex3d(0f, 0, 3);

				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, FloatBuffer.wrap(mat_ambient2));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, FloatBuffer.wrap(mat_specular));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, FloatBuffer.wrap(mat_specular));
				gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, FloatBuffer.wrap(mat_shininess));

				gl.glVertex3d(0, 0f, 0);
				gl.glVertex3d(2, 0f, 0);
				gl.glVertex3d(2, 2f, 0);
				gl.glVertex3d(0, 2f, 0);

				gl.glEnd(); 
				gl.glPopMatrix();

			}

			draw(gl);
			/*Get the current Model View Matrix and keep it in info*/
			FloatBuffer fa = FloatBuffer.wrap(new float[16]);
			gl.glGetFloatv(GL.GL_MODELVIEW, fa);
			info.setCurMatrix(fa.array());

			gl.glPopMatrix();		

			/*Move one forward/backward*/
			if(info.isPlay()){
				if(!info.isRewind()){ info.setCurFrame(info.getCurFrame() +1);}
				else if(info.getCurFrame() > 0) {info.setCurFrame(info.getCurFrame()- 1);}
			}
			/*Set flag for CMCVideoThread*/
			info.setReady(false);
		}	

	}

	/**All the draw** methods are variations of one to another
	 * Check  http://www.dbfinteractive.com/forum/index.php?topic=2050.0 for the algorithm*/
	public void drawSphere(GL gl, double[] p1, double[] p2){
		
		/*First axe*/
		double zx = p2[0] - p1[0];
		double zy = p2[1] - p1[1];
		double zz = p2[2] - p1[2];

		double len = Math.sqrt(zx*zx+zy*zy+zz*zz);
		zx = zx/len;
		zy = zy/len;
		zz = zz/len;

		/*Second axe*/
		double yx = zy;
		double yy = -zz;
		double yz = 0;

		double len2 = Math.sqrt(yx*yx+yy*yy);
		yx = yx/len2;
		yy = yy/len2;
		yz = yz/len2;

		/*Third axe*/
		double xx = yy*zz - yz*zy;
		double xy = yz*zx - yx*zz;
		double xz = yx*zy - yy*zx;

		double[] matrix= {xx, xy, xz, 0, yx, yy, yz, 0, zx, zy, zz, 0, 0,0,0,1};


		gl.glPushMatrix();
		gl.glColor3f(0f, 0f, 250f);
		gl.glTranslated((p2[0]+p1[0])/2, (p2[1]+p1[1])/2,(p2[2]+p1[2])/2);
		/*Apply the rotation matrix*/
		gl.glMultMatrixd(matrix, 0);
		glut.glutSolidSphere(len/2, 10, 10);
		gl.glEnd();
		gl.glPopMatrix();
	}

	/** Exactly the same as the previous drawCylinder except this takes ratio to scale the object*/
	public void drawCylinder(GL gl, double[] p1, double[] p2, double ratio){
		double zx = p2[0] - p1[0];
		double zy = p2[1] - p1[1];
		double zz = p2[2] - p1[2];

		double len = Math.sqrt(zx*zx+zy*zy+zz*zz);
		zx = zx/len;
		zy = zy/len;
		zz = zz/len;

		double yx = zy;
		double yy = -zz;
		double yz = 0;

		double len2 = Math.sqrt(yx*yx+yy*yy);
		yx = yx/len2;
		yy = yy/len2;
		yz = yz/len2;

		double xx = yy*zz - yz*zy;
		double xy = yz*zx - yx*zz;
		double xz = yx*zy - yy*zx;

		double[] matrix= {xx, xy, xz, 0, yx, yy, yz, 0, zx, zy, zz, 0, 0,0,0,1};

		gl.glPushMatrix();
		gl.glColor3f(0f, 0f, 250f);
		gl.glTranslated(p1[0],p1[1], p1[2]);
		gl.glMultMatrixd(matrix, 0);
		glut.glutSolidCylinder(ratio, len, 10, 10);
		gl.glEnd();
		gl.glPopMatrix();
	}
	
	/**Draw skin for Bender
	 * This method is just like drawCylinder as it generates his body part at the center of the world then transform it to the desired position*/
	public void drawSkin(GL gl, double[] p1, double[] p2, OBJ obj, double[] trans){
		ArrayList<double[]> vList = skin.getVList();
		ArrayList<float[]> vnList = skin.getVnList();
		double zx = p2[0] - p1[0];
		double zy = p2[1] - p1[1];
		double zz = p2[2] - p1[2];

		double len = Math.sqrt(zx*zx+zy*zy+zz*zz);
		zx = zx/len;
		zy = zy/len;
		zz = zz/len;

		double yx = zy;
		double yy = -zz;
		double yz = 0;

		double len2 = Math.sqrt(yx*yx+yy*yy);
		yx = yx/len2;
		yy = yy/len2;
		yz = yz/len2;

		double xx = yy*zz - yz*zy;
		double xy = yz*zx - yx*zz;
		double xz = yx*zy - yy*zx;
		double[] matrix= {xx, xy, xz, 0, yx, yy, yz, 0, zx, zy, zz, 0, 0,0,0,1};


		gl.glPushMatrix();

		gl.glTranslated(p1[0],p1[1], p1[2]);	

		gl.glMultMatrixd(matrix, 0);
		gl.glScaled(trans[0]*trans[4], trans[0]*trans[5], trans[0]*trans[6]);
		gl.glRotated(-45, 0, 0, 1);	
		gl.glScaled(len, len,len);

		gl.glTranslated(trans[1], trans[2], trans[3]);
		
		/*This rotation is here so that he faces straight forward*/
		gl.glRotated(90, 1, 0, 0);
		
		/*Draw particular part of Bender*/
		ArrayList<OBJPart> parts = obj.getParts();
		for(int i =0; i < parts.size(); i++){
			OBJPart part = parts.get(i);
			Texture image = null;
			/*If particular part contains a texture image, map it*/
			if(part.HasImage()){
				if(part.getImageName().compareTo("Skin/be.gif")==0) {
					image = be;
				}
				else if(part.getImageName().compareTo( "Skin/beb.gif") == 0) image = beb;
				else if(part.getImageName().compareTo("Skin/bm.gif")==0) image = bm;
				else{
				}
				gl.glEnable(GL.GL_TEXTURE_2D);	    
				image.bind();
				image.enable();
			}
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, part.getKa());
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, part.getKd());
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, part.getKs());
			gl.glMaterialf(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, part.getNs());
			ArrayList<int[]> faces = part.getFaces();
			ArrayList<int[]> normals = part.getVn();
			ArrayList<int[]> vts = part.getVt();

			for(int k =0; k < faces.size(); k++){
				int[] vertices = faces.get(k);
				int[] normal = normals.get(k);
				gl.glBegin(GL.GL_TRIANGLES);
				for(int j =0; j < vertices.length;j++){
					double[] vertex = vList.get(vertices[j]-1);
					float[] nV = vnList.get(normal[j]-1);
					if(part.HasImage()) gl.glTexCoord2fv(skin.getVtList().get(vts.get(k)[j]-1));
					gl.glNormal3f(nV[0], nV[1], nV[2]);
					gl.glVertex3d(vertex[0],vertex[1], vertex[2]);
				}
				gl.glEnd();

			}
			if(part.HasImage()){
				image.disable();
			}
		}
		gl.glPopMatrix();
	}
	
	/*The overloading method for arms part of Bender so that shoulders are slightly lower than the actual location*/
	public void drawSkinRotate(GL gl, double[] p1, double[] p2, double[] p3,OBJ obj, double[] trans){
		double[] temp = {p1[0], p1[1], p1[2]-(p1[2]-p3[2])/6};
		drawSkinRotate(gl, temp, p2, obj, trans);
	}
	
	/*Method to draw arms for Bender*/
	public void drawSkinRotate(GL gl, double[] p1, double[] p2, OBJ obj, double[] trans){
		ArrayList<double[]> vList = skin.getVList();
		ArrayList<float[]> vnList = skin.getVnList();
		double zx = p2[0] - p1[0];
		double zy = p2[1] - p1[1];
		double zz = p2[2] - p1[2];

		double len = Math.sqrt(zx*zx+zy*zy+zz*zz);
		zx = zx/len;
		zy = zy/len;
		zz = zz/len;

		double yx = zy;
		double yy = -zz;
		double yz = 0;

		double len2 = Math.sqrt(yx*yx+yy*yy);
		yx = yx/len2;
		yy = yy/len2;
		yz = yz/len2;

		double xx = yy*zz - yz*zy;
		double xy = yz*zx - yx*zz;
		double xz = yx*zy - yy*zx;
		double[] matrix= {xx, xy, xz, 0, yx, yy, yz, 0, zx, zy, zz, 0, 0,0,0,1};
		gl.glPushMatrix();

		gl.glTranslated(p1[0],p1[1], p1[2]);	

		gl.glMultMatrixd(matrix, 0);
		gl.glScaled(trans[0], trans[0], trans[0]);
		gl.glRotated(-45, 0, 0, 1);	
		gl.glScaled(len, len,len);

		gl.glTranslated(trans[1], trans[2], trans[3]);
		gl.glRotated(90, 1, 0, 0);
		gl.glRotated(-90, 0, 0, 1);

		ArrayList<OBJPart> parts = obj.getParts();
		for(int i =0; i < parts.size(); i++){
			OBJPart part = parts.get(i);
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, part.getKa());
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, part.getKd());
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, part.getKs());
			gl.glMaterialf(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, part.getNs());
			ArrayList<int[]> faces = part.getFaces();
			ArrayList<int[]> normals = part.getVn();

			for(int k =0; k < faces.size(); k++){
				int[] vertices = faces.get(k);
				int[] normal = normals.get(k);
				gl.glBegin(GL.GL_TRIANGLES);
				for(int j =0; j < vertices.length;j++){
					double[] vertex = vList.get(vertices[j]-1);
					float[] nV = vnList.get(normal[j]-1);
					gl.glNormal3f(-nV[0], -nV[1], nV[2]);
					gl.glVertex3d(vertex[0],vertex[1], vertex[2]);
				}
				gl.glEnd();

			}
		}
		gl.glPopMatrix();
	}
	
	/**Draw method for his body part
	 * Uses vector between his sholder as the second axe, so that body always face front*/
	public void drawSkin(GL gl, double[] rshol, double[] lshol, double[] rhip, double[] lhip, OBJ obj, double[] trans){
		ArrayList<double[]> vList = skin.getVList();
		ArrayList<float[]> vnList = skin.getVnList();
		double[] middleShol = {(rshol[0]+lshol[0])/2, (rshol[1]+lshol[1])/2,(rshol[2]+lshol[2])/2};
		double[] middleHip = {(rhip[0]+lhip[0])/2, (rhip[1]+lhip[1])/2,(rhip[2]+lhip[2])/2};
		double zx = middleShol[0] - middleHip[0];
		double zy = middleShol[1] - middleHip[1];
		double zz = middleShol[2] - middleHip[2];

		double len = Math.sqrt(zx*zx+zy*zy+zz*zz);
		zx = zx/len;
		zy = zy/len;
		zz = zz/len;

		double yx = rshol[0]-lshol[0];
		double yy = rshol[1]-lshol[1];
		double yz = rshol[2]-lshol[2];

		double len2 = Math.sqrt(yx*yx+yy*yy);
		yx = yx/len2;
		yy = yy/len2;
		yz = yz/len2;

		double xx = yy*zz - yz*zy;
		double xy = yz*zx - yx*zz;
		double xz = yx*zy - yy*zx;
		double[] matrix= {xx, xy, xz, 0, yx, yy, yz, 0, zx, zy, zz, 0, 0,0,0,1};
		gl.glPushMatrix();

		gl.glTranslated(middleHip[0],middleHip[1], middleHip[2]);
		gl.glMultMatrixd(matrix, 0);
		gl.glRotated(-80, 0, 0, 1);	

		gl.glScaled(trans[0], trans[0], trans[0]);
		gl.glScaled(zz*len, zz*len , zz*len);

		gl.glTranslated(trans[1], trans[2], trans[3]);
		gl.glRotated(90, 1, 0, 0);

		ArrayList<OBJPart> parts = obj.getParts();
		for(int i =0; i < parts.size(); i++){
			OBJPart part = parts.get(i);

			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, part.getKa());
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, part.getKd());
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, part.getKs());
			gl.glMaterialf(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, part.getNs());
			ArrayList<int[]> faces = part.getFaces();
			ArrayList<int[]> normals = part.getVn();

			for(int k =0; k < faces.size(); k++){
				int[] vertices = faces.get(k);
				int[] normal = normals.get(k);
				gl.glBegin(GL.GL_TRIANGLES);
				for(int j =0; j < vertices.length;j++){
					double[] vertex = vList.get(vertices[j]-1);
					float[] nV = vnList.get(normal[j]-1);
					gl.glNormal3f(nV[0], nV[1], nV[2]);
					gl.glVertex3d(vertex[0],vertex[1], vertex[2]);
				}
				gl.glEnd();

			}
		}
		gl.glPopMatrix();

	}
	/**Same as body drawing for bender, but this method draws solid box instead*/
	public void drawBody(GL gl, double[] rshol, double[] lshol, double[] rhip, double[] lhip){
		double[] middleShol = {(rshol[0]+lshol[0])/2, (rshol[1]+lshol[1])/2,(rshol[2]+lshol[2])/2};
		double[] middleHip = {(rhip[0]+lhip[0])/2, (rhip[1]+lhip[1])/2,(rhip[2]+lhip[2])/2};
		double zx = middleShol[0] - middleHip[0];
		double zy = middleShol[1] - middleHip[1];
		double zz = middleShol[2] - middleHip[2];

		double len = Math.sqrt(zx*zx+zy*zy+zz*zz);
		zx = zx/len;
		zy = zy/len;
		zz = zz/len;

		double yx = rshol[0]-lshol[0];
		double yy = rshol[1]-lshol[1];
		double yz = rshol[2]-lshol[2];

		double len2 = Math.sqrt(yx*yx+yy*yy);
		yx = yx/len2;
		yy = yy/len2;
		yz = yz/len2;

		double xx = yy*zz - yz*zy;
		double xy = yz*zx - yx*zz;
		double xz = yx*zy - yy*zx;
		double[] matrix= {xx, xy, xz, 0, yx, yy, yz, 0, zx, zy, zz, 0, 0,0,0,1};
		gl.glPushMatrix();


		gl.glTranslated(middleHip[0],middleHip[1], middleHip[2]);
		gl.glMultMatrixd(matrix, 0);
		gl.glScaled(Math.abs(rshol[0]-lshol[0]), Math.abs(rshol[1]-lshol[1]), Math.abs(middleShol[2] - middleHip[2]));

		gl.glBegin(GL.GL_QUADS);
		// Front Face
		gl.glVertex3f(0.5f, 0.5f, 0f);	
		gl.glVertex3f(-0.5f, 0.5f, 0f);	
		gl.glVertex3f(-0.5f, 0.5f, 1f);
		gl.glVertex3f(0.5f, 0.5f, 1f);	
		// Side Face
		gl.glVertex3f(0.5f, 0.5f, 0f);	
		gl.glVertex3f(0.5f, -0.5f, 0f);	
		gl.glVertex3f(0.5f, -0.5f, 1f);
		gl.glVertex3f(0.5f, 0.5f, 1f);	

		gl.glVertex3f(0.5f, -0.5f, 0f);	
		gl.glVertex3f(-0.5f, -0.5f, 0f);	
		gl.glVertex3f(-0.5f, -0.5f, 1f);
		gl.glVertex3f(0.5f, -0.5f, 1f);	
		// back face
		gl.glVertex3f(-0.5f, -0.5f, 0f);	
		gl.glVertex3f(-0.5f, 0.5f, 0f);	
		gl.glVertex3f(-0.5f, 0.5f, 1f);
		gl.glVertex3f(-0.5f, -0.5f, 1f);	

		//Top face
		gl.glVertex3f(0.5f, 0.5f, 1f);	
		gl.glVertex3f(-0.5f, 0.5f, 1f);	
		gl.glVertex3f(-0.5f, -0.5f, 1f);
		gl.glVertex3f(0.5f, -0.5f, 1f);	

		//Bottom face
		gl.glVertex3f(0.5f, 0.5f, 0f);	
		gl.glVertex3f(-0.5f, 0.5f, 0f);	
		gl.glVertex3f(-0.5f, -0.5f, 0f);
		gl.glVertex3f(0.5f, -0.5f, 0f);	
		gl.glEnd();		

		gl.glPopMatrix();
	}

	/**Draw the current frame to the panel*/
	public void draw(GL gl){
		gl.glPushMatrix();
		gl.glScalef(scale, scale, scale);
		int curFrame = info.getCurFrame();


		/*
		 * The arrangement of coords Array
		 * head
		 * left shoulder
		 * right shoulder
		 * right elbow
		 * left elbow
		 * right hand
		 * left hand
		 * left hip
		 * right hip
		 * right knee
		 * left knee
		 * right foot
		 * left foot*/

		double[] head = {coordsArray[curFrame][0], coordsArray[curFrame][1], coordsArray[curFrame][2]}; 
		double[] rshol = {coordsArray[curFrame][3], coordsArray[curFrame][4], coordsArray[curFrame][5]}; 
		double[] lshol = {coordsArray[curFrame][6], coordsArray[curFrame][7], coordsArray[curFrame][8]}; 
		double[] rmidarm = {coordsArray[curFrame][9], coordsArray[curFrame][10], coordsArray[curFrame][11]}; 
		double[] lmidarm = {coordsArray[curFrame][12], coordsArray[curFrame][13], coordsArray[curFrame][14]}; 
		double[] rhand = {coordsArray[curFrame][15], coordsArray[curFrame][16], coordsArray[curFrame][17]}; 
		double[] lhand = {coordsArray[curFrame][18], coordsArray[curFrame][19], coordsArray[curFrame][20]}; 
		double[] rhip = {coordsArray[curFrame][21], coordsArray[curFrame][22], coordsArray[curFrame][23]}; 
		double[] lhip = {coordsArray[curFrame][24], coordsArray[curFrame][25], coordsArray[curFrame][26]}; 
		double[] rknee = {coordsArray[curFrame][27], coordsArray[curFrame][28], coordsArray[curFrame][29]}; 
		double[] lknee = {coordsArray[curFrame][30], coordsArray[curFrame][31], coordsArray[curFrame][32]}; 
		double[] rfoot = {coordsArray[curFrame][33], coordsArray[curFrame][34], coordsArray[curFrame][35]}; 
		double[] lfoot = {coordsArray[curFrame][36], coordsArray[curFrame][37], coordsArray[curFrame][38]};
		double[] middle = {(rshol[0]+lshol[0])/2, (rshol[1]+lshol[1])/2,(rshol[2]+lshol[2])/2};

		gl.glColor3f(200f, 200f, 200f); 

		if(info.getSkin() == 2){
			/*Daw Bender*/
			drawSkin(gl, middle, head, skin.getHead(), skin.getHeadMatrix());
			drawSkin(gl, rhip, rknee, skin.getMidleg(), skin.getMidLegMatrix());
			drawSkin(gl, rfoot, rknee, skin.getFoot(), skin.getFootMatrix());
			drawSkin(gl, lhip, lknee, skin.getMidleg(), skin.getMidLegMatrix());
			drawSkin(gl, lfoot, lknee, skin.getFoot(), skin.getFootMatrix());
			drawSkinRotate(gl, rshol, rmidarm, rhip, skin.getMidarm(), skin.getMidArmMatrix());
			drawSkinRotate(gl, lshol, lmidarm, lhip, skin.getMidarm(), skin.getMidArmMatrix());
			drawSkinRotate(gl, rmidarm, rhand,  skin.getHand(), skin.getHandMatrix());
			drawSkinRotate(gl, lmidarm, lhand,  skin.getHand(), skin.getHandMatrix());
			drawSkin(gl, rshol, lshol, rhip, lhip, skin.getBody(), skin.getBodyMatrix());
		}

		else if(info.getSkin() == 1){
			/*Draw Simple Version (Which is a bunch of polygons)*/
			float[] mat_ambient = { 1f, 1f, 1f, 1.f };
			float[] mat_diffuse = { .2f, 0.2f, 0.2f, .5f };
			float[] mat_specular = { .2f, .2f, .2f, .2f };
			float[] mat_shininess = { 1.0f };
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, FloatBuffer.wrap(mat_ambient));
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, FloatBuffer.wrap(mat_diffuse));
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, FloatBuffer.wrap(mat_specular));
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, FloatBuffer.wrap(mat_shininess));
			drawSphere(gl, middle, head);
			drawCylinder(gl, rshol, rmidarm, 1.8);
			drawCylinder(gl, lshol, lmidarm, 1.8);
			drawCylinder(gl, rmidarm, rhand,1.8);
			drawCylinder(gl, lmidarm, lhand, 1.8);
			drawCylinder(gl, rhip, rknee, 1.8);
			drawCylinder(gl, rknee, rfoot, 1.8);
			drawCylinder(gl, lhip, lknee, 1.8);
			drawCylinder(gl, lknee, lfoot, 1.8);
			drawBody(gl, rshol, lshol, rhip, lhip);
		}

		else{
			/*Draw default, that is lines and points only*/
			float[] mat_ambient = { 1f, 1f, 1f, 1.f };
			float[] mat_diffuse = { .2f, 0.2f, 0.2f, .5f };
			float[] mat_specular = { .2f, .2f, .2f, .2f };
			float[] mat_shininess = { 1.0f };
			gl.glDisable(GL.GL_LIGHTING);
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, FloatBuffer.wrap(mat_ambient));
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, FloatBuffer.wrap(mat_diffuse));
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, FloatBuffer.wrap(mat_specular));
			gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, FloatBuffer.wrap(mat_shininess));

			gl.glColor3f(0f, 0f, 0f); 

			gl.glPointSize(5.0f);
			gl.glBegin(GL.GL_POINTS);

			gl.glColor3f(0f, 0f, 0f); 
			gl.glVertex3d(head[0], head[1],head[2]);
			gl.glVertex3d(rshol[0], rshol[1],rshol[2]);
			gl.glVertex3d(lshol[0], lshol[1],lshol[2]);
			gl.glVertex3d(rmidarm[0], rmidarm[1],rmidarm[2]);
			gl.glVertex3d(lmidarm[0], lmidarm[1],lmidarm[2]);
			gl.glVertex3d(rhand[0], rhand[1],rhand[2]);
			gl.glVertex3d(lhand[0], lhand[1],lhand[2]);
			gl.glVertex3d(rhip[0], rhip[1],rhip[2]);
			gl.glVertex3d(lhip[0], lhip[1],lhip[2]);
			gl.glVertex3d(rknee[0], rknee[1],rknee[2]);
			gl.glVertex3d(lknee[0], lknee[1],lknee[2]);
			gl.glVertex3d(rfoot[0], rfoot[1],rfoot[2]);
			gl.glVertex3d(lfoot[0], lfoot[1],lfoot[2]);
			gl.glEnd();	

			gl.glLineWidth(2.0f);

			gl.glBegin(GL.GL_LINES);		
			/*Head to neck*/
			gl.glVertex3d(head[0], head[1],head[2]);
			gl.glVertex3d((rshol[0]+lshol[0])/2, (rshol[1]+lshol[1])/2,(rshol[2]+lshol[2])/2);

			/*Right arm*/
			gl.glVertex3d(rshol[0], rshol[1],rshol[2]);
			gl.glVertex3d(rmidarm[0], rmidarm[1],rmidarm[2]);

			gl.glVertex3d(rhand[0], rhand[1],rhand[2]);
			gl.glVertex3d(rmidarm[0], rmidarm[1],rmidarm[2]);

			/*Left Arm*/
			gl.glVertex3d(lshol[0], lshol[1],lshol[2]);
			gl.glVertex3d(lmidarm[0], lmidarm[1],lmidarm[2]);

			gl.glVertex3d(lhand[0], lhand[1],lhand[2]);
			gl.glVertex3d(lmidarm[0], lmidarm[1],lmidarm[2]);

			/*Right leg*/
			gl.glVertex3d(rhip[0], rhip[1],rhip[2]);
			gl.glVertex3d(rknee[0], rknee[1],rknee[2]);

			gl.glVertex3d(rfoot[0], rfoot[1],rfoot[2]);
			gl.glVertex3d(rknee[0], rknee[1],rknee[2]);

			/*Left leg*/
			gl.glVertex3d(lhip[0], lhip[1],lhip[2]);
			gl.glVertex3d(lknee[0], lknee[1],lknee[2]);

			gl.glVertex3d(lfoot[0], lfoot[1],lfoot[2]);
			gl.glVertex3d(lknee[0], lknee[1],lknee[2]);

			gl.glEnd();

			/*Draw body*/
			gl.glBegin(GL.GL_LINE_LOOP);
			gl.glVertex3d(lshol[0], lshol[1],lshol[2]);
			gl.glVertex3d(rshol[0], rshol[1],rshol[2]);
			gl.glVertex3d(rhip[0], rhip[1],rhip[2]);
			gl.glVertex3d(lhip[0], lhip[1],lhip[2]);
			gl.glEnd();
		}

		gl.glPopMatrix();



	}	
	public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) {}

	public void init(GLAutoDrawable glDrawable) {
	}

	public void reshape(GLAutoDrawable arg0, int arg1, int arg2, int arg3,int arg4) {}

}
