import glmodel.GL_3DS_Importer;
import glmodel.GL_Mesh;
import glmodel.GL_Triangle;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.glu.GLU;
import org.lwjgl.opengl.glu.Sphere;

public class Pong extends Game_Skeleton
{
	int n = 10; //number of paddles
	int r = 150;
	float delta = 0;
	float pi = (float) 3.1415926;
	Paddle paddle[] = new Paddle[n];
	Ball ball;
	float Vp = 0.004f;
	/**sound**/
    int s1;	
    float[] sPos1 = {400f, 300f, 100f};
    int playflag = 0;
    
    private GL_Mesh obj;
    private GL_Mesh obj1;
    private GL_Mesh obj2;
    private GL_Mesh obj3;
    /*public static float[][] modelViewMatrix = new float[4][4];
    public static float[][] projectionMatrix = new float[4][4];
    public static int[]     viewport = new int[4];*/

    // Handle for a display list
    int drumDisplayList = -1;
    GLMaterial material = new GLMaterial();
    GLMaterial cmaterial = new GLMaterial();
    GLMaterial imaterial = new GLMaterial();
    GLMaterial lmaterial = new GLMaterial();
    GLMaterial bmaterial = new GLMaterial();
    int drumTextureHandle = 0;
    int ballTextureHandle = 0;
    
    // color of overall scene lighting
    float ambient[] = { .2f, .2f, .2f, 1f }; 

    // color of light source
    float lightDiffuse[]  = { .8f, .8f, .8f, 1f }; // direct light 
    float lightSpecular[] = { 1f, 1f, 1f, 1f }; // highlight 
    float lightAmbient[]  = { .5f, .5f, .5f, 1f }; // scattered light

    // light position: if last value is 0, then this describes light direction.  
    // If 1, then light position.
    float lightPosition[] = { 0f, 300f, 400, 0f };

    //body material
    float mtlDiffuse[]    = { .1f, 0.1f, 0.7f, 1f }; 
    float mtlAmbient[]    = { .2f, 0.2f, 0.2f, 1f }; 
    float mtlSpecular[]   = { .3f, 0.3f, 0.5f, 1f }; 
    float mtlEmissive[]   = { 0f, 0f, 0f, 1f};
    float mtlShininess    = 10f;
    //hat material
    float cmtlDiffuse[]    = { .3f, 0.3f, 0.3f, 1f };
    float cmtlAmbient[]    = { .3f, 0.3f, 0.3f, 1f };
    float cmtlSpecular[]   = { .5f, 0.5f, 0.5f, 1f };
    float cmtlEmissive[]   = { 0f, 0f, 0f, 1f};
    float cmtlShininess    = 127f;
    //eyes material
    float imtlDiffuse[]    = { .8f, 0.8f, 0.8f, 1f };
    float imtlAmbient[]    = { .1f, 0.1f, 0.1f, 1f };
    float imtlSpecular[]   = { 0f, 0f, 0f, 1f };
    float imtlEmissive[]   = { 0f, 0f, 0f, 1f};
    float imtlShininess    = 1f;
    //leg material
    float lmtlDiffuse[]    = { .3f, 0.1f, 0.3f, 1f };
    float lmtlAmbient[]    = { .5f, 0.4f, 0.5f, 1f }; 
    float lmtlSpecular[]   = { .5f, 0.2f, 0.5f, 1f }; 
    float lmtlEmissive[]   = { 0f, 0f, 0f, 1f};
    float lmtlShininess    = 1f;
    //ball material
    float bmtlDiffuse[]    = { .3f, 0.2f, 0.2f, 1f };  
    float bmtlAmbient[]    = { .8f, 0.5f, 0.5f, 1f };  
    float bmtlSpecular[]   = { .5f, 0.3f, 0.3f, 1f };  
    float bmtlEmissive[]   = { 0.5f, 0f, 0f, 1f};  
    float bmtlShininess    = 100f;
    
    

	
	public static void main(String args[])
	{
		(new Pong()).run();
	}

	public Pong()
	{
		super();
	}

	public void init() throws Exception
	{
		super.init();
		GLImage textureImg = new GLImage("images/grass.jpg");
		drumTextureHandle = makeTexture(textureImg);
		ballTextureHandle = makeTexture(textureImg);
		makeTextureMipMap(textureImg);
		
        GL_3DS_Importer import3DS = new GL_3DS_Importer();
        GL_3DS_Importer import3DS1 = new GL_3DS_Importer();
        GL_3DS_Importer import3DS2 = new GL_3DS_Importer();
        GL_3DS_Importer import3DS3 = new GL_3DS_Importer();
        
        InputStream in3ds = FILE.getInputStream("models/body1.3DS"); 
        InputStream in3ds1 = FILE.getInputStream("models/hat1.3DS"); 
        InputStream in3ds2 = FILE.getInputStream("models/legs1.3DS"); 
        InputStream in3ds3 = FILE.getInputStream("models/eyes1.3DS"); 
        
        obj = import3DS.importFromStream(in3ds);
        obj1 = import3DS1.importFromStream(in3ds1);
        obj2 = import3DS2.importFromStream(in3ds2);
        obj3 = import3DS3.importFromStream(in3ds3);
	        
        material.setSurfaceColorLit(mtlDiffuse);
        material.setSurfaceColorShadow(mtlAmbient);
        material.setReflectionColor(mtlSpecular);
        material.setShininess(mtlShininess);
        material.setGlowColor(mtlEmissive);
        
        cmaterial.setSurfaceColorLit(cmtlDiffuse);
        cmaterial.setSurfaceColorShadow(cmtlAmbient);
        cmaterial.setReflectionColor(cmtlSpecular);
        cmaterial.setShininess(cmtlShininess);
        cmaterial.setGlowColor(cmtlEmissive);
        
        imaterial.setSurfaceColorLit(imtlDiffuse);
        imaterial.setSurfaceColorShadow(imtlAmbient);
        imaterial.setReflectionColor(imtlSpecular);
        imaterial.setShininess(imtlShininess);
        imaterial.setGlowColor(imtlEmissive);
        
        lmaterial.setSurfaceColorLit(lmtlDiffuse);
        lmaterial.setSurfaceColorShadow(lmtlAmbient);
        lmaterial.setReflectionColor(lmtlSpecular);
        lmaterial.setShininess(lmtlShininess);
        lmaterial.setGlowColor(lmtlEmissive);
        
        bmaterial.setSurfaceColorLit(bmtlDiffuse);
        bmaterial.setSurfaceColorShadow(bmtlAmbient);
        bmaterial.setReflectionColor(bmtlSpecular);
        bmaterial.setShininess(bmtlShininess);
        bmaterial.setGlowColor(bmtlEmissive);
        
	        drumDisplayList = GL11.glGenLists(1); // allocate 1 display list
	        GL11.glNewList(drumDisplayList, GL11.GL_COMPILE); // start building the list
	        
	        //body
	        material.apply();
	        renderMesh(obj,drumTextureHandle);          // glVertex() commands will go into list, not to screen
	        
	        GL11.glPushMatrix();
	        GL11.glRotatef(90,1,0,0);
	        GL11.glPushMatrix();
	        GL11.glTranslatef(0f,13f,0f);
	        cmaterial.apply();
	        renderMesh(obj1,drumTextureHandle);//hat
	        GL11.glPopMatrix();
	        
	        GL11.glPushMatrix();
	        GL11.glTranslatef(-6f,-15f,8f);
	        lmaterial.apply();
	        renderMesh(obj2,drumTextureHandle);//legs
	        GL11.glPopMatrix();
	        
	        GL11.glPushMatrix();
	        GL11.glTranslatef(-5f,0f,11f);
	        imaterial.apply();
	        renderMesh(obj3,drumTextureHandle);//eyes
	        GL11.glPopMatrix();
	        
	        GL11.glPopMatrix();
	        GL11.glEndList();   
		
	        
	        setLight( GL11.GL_LIGHT1, lightDiffuse, lightAmbient, lightSpecular, lightPosition );

	        setAmbientLight(ambient);
	        
	        GL11.glClearColor(.3f, .3f, .4f, 1);
	        
	        // blending
	        GL11.glEnable(GL11.GL_BLEND);
	        GL11.glEnable(GL11.GL_LIGHTING);
	        GL11.glEnable(GL11.GL_DEPTH_TEST);
	        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); 
	        
	        // Force normals to length 1
	        GL11.glEnable(GL11.GL_NORMALIZE);
	        
	        // Draw specular highlghts on top of textures (GL12.GL_SINGLE_COLOR to reset)
	        GL11.glLightModeli(GL12.GL_LIGHT_MODEL_COLOR_CONTROL, GL12.GL_SEPARATE_SPECULAR_COLOR );
	        
		SoundScape.create();      // init sound environment
        SoundScape.setReferenceDistance(5);
        s1 = SoundScape.makeSoundSource( SoundScape.loadSoundData("sounds/bomb.wav") );

		for(int i=0;i<n;i++){
			float angle = 2*pi*(i+1)/n;
			paddle[i] = new Paddle();
			paddle[i].setLocation(angle,delta, r, 0);
		}
		ball = new Ball();
		ball.start();
	}
	
	int drawflash = 0;
	float bombred = 0;

	public void render()
	{
		//super.render();
//		 SOUND: set listener at camera position
		float a = 1;
        float[] d = getDirection();
        SoundScape.setListenerPosition(400f, 300f, 700f);
        SoundScape.setListenerOrientation(d[0], d[1], d[2],   // we're facing this way
                                          0f, 1f, 0f);        // Y is "up"

		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT |GL11.GL_DEPTH_BUFFER_BIT); 
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glLoadIdentity();
		
        GLU.gluLookAt(
                400, cameraY, cameraZ,   // eye position
                400f, 300f, -5f,    // target to look at
                0f, 1f, 0f);   // which way is up
		//super.render();

		for(int i = 0; i<n;i++)
		{
			float angle = 2*pi*(i+1)/n;
			paddle[i].setLocation(angle,delta, r, 0);
			ball.checkForCollisions(paddle);
			if(ball.collide == 1){
				drawflash = 20;}
			if(drawflash > 0){
				GL11.glPushMatrix();
		        GL11.glTranslatef(ball.sx,ball.sy,0);
		        for(int p = 0; p<20; p++){
		        GL11.glColor3f(1f,.2f,.2f);
		        GL11.glScalef(1.02f,1.02f,1.02f);
		        GL11.glCallList(drumDisplayList);}
		        GL11.glPopMatrix();
		        drawflash--;
			}
			if(ball.collide == 1&& playflag == 0){
				
				playflag = 1;
				ball.collide = 0;
			}
			GL11.glPushMatrix();
			{
				//System.out.println("paddle"+i+"angle"+(angle+delta)/pi*180);
				GL11.glTranslatef(paddle[i].locX, paddle[i].locY, paddle[i].locZ);
				GL11.glRotatef((angle+delta)/pi*180, 0, 0, 1);
				GL11.glColor3f(.5f,.5f,.5f);
				//paddle[i].render();
				//GL11.glCallList(drumDisplayList);  // render mesh
				GL11.glRotatef(-90, 0,0,1);
				//GL11.glRotatef(180,0,0,1);
		    	//GL11.glScalef(.5f,.5f,.5f);
				GL11.glScalef(a,a,a);
		    	GL11.glCallList(drumDisplayList); 
			}
			GL11.glPopMatrix();

			//else {SoundSpace.stop(s1);}
			bmaterial.apply();
			ball.render();
			if(playflag == 1){
				//sPos1 = {paddle[i].locX, ball.locY, 100f};
				//float[] sPos1 = {400f, 300f, 100f};
		        SoundScape.setSoundPosition(s1, sPos1);
		        SoundScape.setLoop(s1,false);
		        SoundScape.play(s1);
		        if(!SoundScape.isPlaying){playflag = 0;}

			}
		}
	}

	public void process_keyboard()
	{
		super.process_keyboard();
		/* Check input for player one (on the left) */
		if(Keyboard.isKeyDown(Keyboard.KEY_Q))
			delta += Vp;
		if(Keyboard.isKeyDown(Keyboard.KEY_A))
			delta += -Vp;
		for(int i = 0; i < Keyboard.getNumKeyboardEvents(); i++)
		{
			/* Get the next key typed/buffered */
			Keyboard.next();
			/* If key "SPACE" has been pressed, start the game */
			if(Keyboard.getEventKey() == Keyboard.KEY_SPACE &&
					Keyboard.getEventKeyState())
			{
				ball.start();
			}
			/* If key "ESCAPE" has been pressed, exit the game */
			if(Keyboard.getEventKey() == Keyboard.KEY_ESCAPE &&
					Keyboard.getEventKeyState())
			{
				done = true;
			}
		}
	}
	
	float tileFactorHoriz = 1f;
    float tileFactorVert = 1f;
	
	public void renderMesh(GL_Mesh o, int textureHandle)
    {
        GL_Triangle t;
        GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
        GL11.glBegin(GL11.GL_TRIANGLES);
        for (int j = 0; j < o.triangles.length; j++) { // draw all triangles in object
            t = o.triangles[j];

            GL11.glTexCoord2f(tileFactorHoriz*t.uvw1.x, tileFactorVert*t.uvw1.y);
            GL11.glNormal3f(t.norm1.x, t.norm1.y, t.norm1.z);
            GL11.glVertex3f( (float)t.p1.pos.x, (float)t.p1.pos.y, (float)t.p1.pos.z);

            GL11.glTexCoord2f(tileFactorHoriz*t.uvw2.x, tileFactorVert*t.uvw2.y);
            GL11.glNormal3f(t.norm2.x, t.norm2.y, t.norm2.z);
            GL11.glVertex3f( (float)t.p2.pos.x, (float)t.p2.pos.y, (float)t.p2.pos.z);

            GL11.glTexCoord2f(tileFactorHoriz*t.uvw3.x, tileFactorVert*t.uvw3.y);
            GL11.glNormal3f(t.norm3.x, t.norm3.y, t.norm3.z);
            GL11.glVertex3f( (float)t.p3.pos.x, (float)t.p3.pos.y, (float)t.p3.pos.z);
        }
        GL11.glEnd();
    }
    
    /**
     * Make a vector that points in direction camera is looking.  Will use
     * this to set direction of Sound Listener.
     * @return xyz direction as float[3]
     */
    public float[] getDirection()
    {
        float[] direction = {
            //(float) -Math.sin(cameraRotation * piover180),
            //(float) -Math.cos(cameraRotation * piover180),
           0f,0f,0f
        };
        return direction;
    }
    public static InputStream getInputStream(String filename) {
        InputStream in = null;
        try {
            in = new FileInputStream(filename);
        }
        catch (IOException ioe) {
            System.out.println("GLApp.getInputStream (" + filename + "): " + ioe);
            if (in != null) {
                try {
                    in.close();
                    in = null;
                }
                catch (Exception e) {}
            }
        }
        catch (Exception e) {
            System.out.println("GLApp.getInputStream (" + filename + "): " + e);
        }
        return in;
    }
    /**
     * Create a texture from the given image.
     */
    public static int makeTexture(GLImage textureImg)
    {
        if ( textureImg == null ) {
            return 0;
        }
        else {
            return makeTexture(textureImg.pixelBuffer, textureImg.w, textureImg.h);
        }
    }
    /**
     * Create a texture from the given pixels in RGBA format.  Set the texture
     * to repeat in both directions and use LINEAR for magnification.
     * @return  the texture handle
     */
    public static int makeTexture(ByteBuffer pixels, int w, int h)
    {
        // get a new empty texture
        int textureHandle = allocateTexture();
        // 'select' the new texture by it's handle
        GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
        // set texture parameters
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); //GL11.GL_NEAREST);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); //GL11.GL_NEAREST);
        // Create the texture from pixels
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, w, h, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
        return textureHandle;
    }
    public static int allocateTexture()
    {
        IntBuffer textureHandle = allocInts(1);
        GL11.glGenTextures(textureHandle);
        return textureHandle.get(0);
    }

    public static final int SIZE_INT = 4;
    
    public static IntBuffer allocInts(int howmany) {
        return ByteBuffer.allocateDirect(howmany * SIZE_INT).order(ByteOrder.nativeOrder()).asIntBuffer();
    }
    
    public static int makeTextureMipMap(GLImage textureImg)
    {
        int ret = GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, 4, textureImg.w,
                          textureImg.h, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, textureImg.getPixelsRGBA());
        if (ret != 0) {
            System.out.println("GLApp.makeTextureMipMap(): Error occured while building mip map, ret=" + ret);
        }
        //Assign the mip map levels and texture info
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_NEAREST);
        GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
        return ret;
    }

    public static void setLight(int GLLightHandle, float[] color, float[] position )
    {
        float[] ambientLight = {color[0]/4f, color[1]/4f, color[2]/4f, color[3]/4f};
        FloatBuffer lightColor = allocFloats(color);
        FloatBuffer ambientColor = allocFloats(ambientLight);
        FloatBuffer ltPosition = allocFloats(position);
        GL11.glLight(GLLightHandle, GL11.GL_DIFFUSE, lightColor);   // color of the direct illumination
        GL11.glLight(GLLightHandle, GL11.GL_SPECULAR, lightColor);  // color of the highlight (same as direct light)
        GL11.glLight(GLLightHandle, GL11.GL_AMBIENT, ambientColor); // color of the scattered light (darker)
        GL11.glLight(GLLightHandle, GL11.GL_POSITION, ltPosition);
        GL11.glEnable(GLLightHandle);	// Enable the light (GL_LIGHT1 - 7)
    }

    /**
     * Set the color of a 'positional' light (a light that has a specific
     * position within the scene).  <BR>
     *<BR>
     * Params:<BR>
     * 		an OpenGL light number (GL11.GL_LIGHT1),<BR>
     * 		'Diffuse': color of direct light from this source,<BR>
     * 		'Ambient': color of scattered light from this source <BR>
     * 		'Specular': color of this light reflected off a surface,<BR>
     * 		position.<BR>
     */
    public static void setLight( int GLLightHandle,
            float[] diffuseLightColor, 	float[] ambientLightColor, 
    		float[] specularLightColor, float[] position )
    {
            FloatBuffer ltDiffuse = allocFloats(diffuseLightColor);
            FloatBuffer ltAmbient = allocFloats(ambientLightColor);
            FloatBuffer ltSpecular = allocFloats(specularLightColor);
            FloatBuffer ltPosition = allocFloats(position);
            GL11.glLight(GLLightHandle, GL11.GL_DIFFUSE, ltDiffuse);   // color of the direct illumination
            GL11.glLight(GLLightHandle, GL11.GL_AMBIENT, ltAmbient);   // color of the reflected light
            GL11.glLight(GLLightHandle, GL11.GL_SPECULAR, ltSpecular); // color of the highlight (same as direct light)
            GL11.glLight(GLLightHandle, GL11.GL_POSITION, ltPosition);
            GL11.glEnable(GLLightHandle);	// Enable the light (GL_LIGHT1 - 7)
            //GL11.glLightf(GLLightHandle, GL11.GL_QUADRATIC_ATTENUATION, .005F);    // how light beam drops off
    }

	/**
	 * Set the color of the Global Ambient Light.  Affects all objects in
	 * scene regardless of their placement.
	 */
	public static void setAmbientLight(float[] ambientLightColor) {
		FloatBuffer ltAmbient = allocFloats(ambientLightColor);
		GL11.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, ltAmbient);
	}
	
	public static final int SIZE_FLOAT = 4;

    public static FloatBuffer allocFloats(int howmany) {
        return ByteBuffer.allocateDirect(howmany * SIZE_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
    }

    public static FloatBuffer allocFloats(float[] floatarray) {
        FloatBuffer fb = ByteBuffer.allocateDirect(floatarray.length * SIZE_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
        fb.put(floatarray).flip();
        return fb;
    }
    
    public static void renderSphere() {
        Sphere s = new Sphere();       // an LWJGL class for drawing sphere
        s.setOrientation(GLU.GLU_OUTSIDE);  // normals point outwards
        s.setTextureFlag(true);           // generate texture coords
        s.draw(6, 12, 12);              // run GL commands to draw sphere
    }


}


