import com.hermetica.magician.*;
import com.hermetica.util3d.*;
import java.awt.*;
import java.net.*;
import java.lang.*;

/** Implements the GLComponentListener for the OpenGL Illumination DemoScene.
 *
 *          R.J.L. August 1998.
 *
 *              Extended to a more complex scene (cube, cylinder, sphere, cone)
 *              July, 1999. RJL.
 */

class DemoScene implements GLEventListener {

    static final double FOVDEGS = 45;   // Default field of view (degrees)
    static final double FOV = FOVDEGS * Math.PI / 180;  // Ibid (radians)

    // Vertex, face and normal data for the cube
    static final double[][] vertices = {{0,0,0}, // left, down, back
       					{1,0,0}, // right, down, back
    					{0,1,0}, // left, up, back
    					{1,1,0}, // right, up, back
    					{0,0,1}, // left, down, front
    					{1,0,1}, // right, down, front
    					{0,1,1}, // left, up, front
    					{1,1,1}};// right,up, front
    static final double[][] normals = {{0,0,-1}, // normal for back face
    					{0,0,1}, // normal for front face
    					{0,1,0}, // normal for top face
        				{0,-1,0},// normal for bottom face
        				{-1,0,0},// normal for left face
        				{1,0,0}};// normal for right face
    // order of vertices important, otherwise backface culling
    // will just do it wrong
    static final int[][] faces = {{0,2,3,1},  // back
    				  {4,5,7,6},  // front
       				  {2,6,7,3},  // top
    				  {4,0,1,5},  // bottom
    				  {6,2,0,4},  // left
	  			      {1,3,7,5}}; // right
    /* The OpenGL state machine */
    private CoreGL gl_ = new CoreGL();
    private CoreGLU glu_ = new CoreGLU();
    GLParameterSource paramSource;
    GLComponent glc;

    /* DemoScene current rotation */
    // not used in Demo
    float[][] rotation = {{1,0,0,0},
    			  {0,1,0,0},
    			  {0,0,1,0},
    			  {0,0,0,1}};

    /** Drawable dimensions */
    int width = 100, height = 100;  // meaningless defaults to keep compiler happy

    /* Texture variables */
    static final int numTextureMaps=4;
    static int[] textureNames=new int[numTextureMaps];
    static Texture text = null;
    
    static final int TEX_WIDTH=32;
    static final int TEX_HEIGHT=32;
    static final byte[] DARK_GREY = {64, 64, 64, (byte) 255};
    static final byte[] LIGHT_GREY = {(byte) 192, (byte) 192, (byte) 192, (byte) 255};
    static byte[][][] chequerboardTexture = new byte[TEX_WIDTH][TEX_HEIGHT][4];

    // Generates a 2 square x 2 square chequerboard image
    private void generateTextureImage() {
	for (int i = 0; i < TEX_HEIGHT; i++)
		for (int j = 0; j < TEX_WIDTH; j++) {
			if ((i > TEX_HEIGHT / 2) == (j > TEX_WIDTH / 2))
				chequerboardTexture[i][j] = DARK_GREY;
			else
				chequerboardTexture[i][j] = LIGHT_GREY;
		}
    }

    /* load texture maps*/
    private void setupTextureMap() {
    String[] textFileNames={"water01.jpg","stone44.jpg","metal15.jpg"};
    gl_.glGenTextures(numTextureMaps, textureNames);
    
    /* load images as textures */
    int i;
    for (i=0;i<2;i++) {
    	Image img = Toolkit.getDefaultToolkit().getImage(textFileNames[i]);
	text = new Texture(img, glc);
   	while (text.isValid() == false) {
    	try {
		Thread.currentThread().sleep(100);
	    } catch (Exception e) {
		System.out.println("Exception: " + e.getMessage());
		e.printStackTrace();
	}
    
    int textWidth=text.getWidth();
    int textHeight=text.getHeight();
    byte[] textByteArray=text.getTexture();
	
    gl_.glBindTexture(GL.GL_TEXTURE_2D, textureNames[i]);
    gl_.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
    gl_.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
    gl_.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
    gl_.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
    gl_.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);	
    gl_.glTexImage2D(GL.GL_TEXTURE_2D,0,
	             GL.GL_RGBA,
		     textWidth,
		     textHeight,0,
		     GL.GL_RGBA,
		     GL.GL_UNSIGNED_BYTE,
	             textByteArray);
    // Alternative for glTexImage2D:
/*  glu_.gluBuild2DMipmaps(GL.GL_TEXTURE_2D, 4,
				text.getWidth(),
				text.getHeight(),
				GL.GL_RGBA, GL.GL_UNSIGNED_BYTE,
				textByteArray);
*/
    }}

    /* last texture: Checkerboard */
    gl_.glBindTexture(GL.GL_TEXTURE_2D, textureNames[numTextureMaps-1]);
    gl_.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
    gl_.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
    gl_.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
    gl_.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
    gl_.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA,
			TEX_WIDTH, TEX_HEIGHT, 0, GL.GL_RGBA,
			GL.GL_UNSIGNED_BYTE, chequerboardTexture);
    gl_.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
    }

    public DemoScene(GLParameterSource paramSource, GLComponent glc) {
        this.paramSource = paramSource;
        this.glc = glc;
    }

    /** Initialization stuff */
    public void initialize( GLDrawable component ) {
      gl_.glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
      gl_.glShadeModel( GL.GL_SMOOTH );
      gl_.glEnable(GL.GL_DEPTH_TEST);
      generateTextureImage();
      setupTextureMap();
      //getTextureMaps(component);
    }

    /** Handles viewport resizing */
    public void reshape( GLDrawable component, int x, int y, int width, int height ) {
        /** Setup the viewport */
        gl_.glViewport( component, x, y, width, height );
        this.width = width; this.height = height;
    }

    /** Renders a DemoScene */
    public void display( GLDrawable component ) {

        gl_.glClear( GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT );
        gl_.glShadeModel(paramSource.getShadingModel());

        // Set up projection and viewing (with model rotatable by mouse)
        gl_.glMatrixMode( GL.GL_PROJECTION );
        gl_.glLoadIdentity();
        double zoom = paramSource.getZoomFactor();
        double fovActual = 2 * Math.atan(Math.tan(FOV / 2) / zoom) * 180.0 / Math.PI;
        glu_.gluPerspective( (float) fovActual, (float) width / height, 0.5f, 20.0f );
        gl_.glMatrixMode( GL.GL_MODELVIEW );

        gl_.glLoadIdentity();
        glu_.gluLookAt( 0.0f, 0.5f, 8.0f, 0.0f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f );
        gl_.glMultMatrixf(rotation);

        // Set up lighting
        float intens = paramSource.getLightIntensity();
        float ambient = paramSource.getAmbientLightLevel();
        float[] lightColour = {intens, intens, intens, 1.0f};
        float[] ambientColour = {ambient, ambient, ambient, 1.0f};
        float[] black = {0,0,0,1};
        gl_.glLightModeli(GL.GL_LIGHT_MODEL_TWO_SIDE, GL.GL_TRUE);
        gl_.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, ambientColour);
        gl_.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, black);
        gl_.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, lightColour);
        gl_.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, lightColour);
        gl_.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, paramSource.getLightPosition());
        gl_.glEnable(GL.GL_LIGHT_MODEL_LOCAL_VIEWER);
        gl_.glEnable(GL.GL_LIGHTING);
        gl_.glEnable(GL.GL_LIGHT0);
        gl_.glEnable(GL.GL_NORMALIZE);

        // Set up scene, starting with "ground" polygon.
        float[] groundColour = {0.2f, 0.4f, 0.2f, 1.0f};
        float[] groundSpecular = {0,0,0,1.0f};
        gl_.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, groundColour);
        gl_.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, groundSpecular);
        gl_.glMaterialfv(GL.GL_FRONT, GL.GL_SHININESS, groundSpecular);

        gl_.glEnable(GL.GL_TEXTURE_2D);
 		gl_.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE);
		gl_.glBindTexture(GL.GL_TEXTURE_2D, textureNames[0]);

        float[][] groundSq = {{-5,0,5}, {5,0,5},{5,0,-5},{-5,0,-5}};
        // number of iterations of the texture on the object
        float[][] groundTexCoord = {{0,5},{5,5},{5,0},{0,0}};
        float[] vertical = {0f, 1f, 0f};
        gl_.glBegin(GL.GL_POLYGON);
        gl_.glNormal3fv(vertical);
        for (int i=0;i<4;i++) {
            gl_.glTexCoord2fv(groundTexCoord[i]);
	    gl_.glVertex3fv(groundSq[i]);
        }
        gl_.glEnd();
       
        float[] lightPos=paramSource.getLightPosition();
	int tess = paramSource.getNumTessellations();

	// Settings for the material
        float[] matAmbient = paramSource.getMaterialAmbient();
        float[] matDiffuse = paramSource.getMaterialDiffuse();
        float[] matSpecular = paramSource.getMaterialSpecular();
        float[] matShininess = {paramSource.getShininess()};
        gl_.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, matAmbient);
        gl_.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, matDiffuse);
        gl_.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, matSpecular);
        gl_.glMaterialfv(GL.GL_FRONT, GL.GL_SHININESS, matShininess);
        gl_.glEnable(GL.GL_TEXTURE_2D);
 	gl_.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE);
	gl_.glBindTexture(GL.GL_TEXTURE_2D, textureNames[1]);
	
	// Now the the cylinder
        gl_.glRotated(180,0,0,1);
        gl_.glRotated(90, 1, 0, 0);
        glu_.gluCylinder(glu_.gluNewQuadric(), 1, 1, 0.5, tess, 1);
        gl_.glTranslatef(0,0,0.5f);
        glu_.gluCylinder(glu_.gluNewQuadric(), 0.6, 0.3, 3, tess, 1);
        gl_.glTranslatef(0,0,3.4f);       
        glu_.gluSphere(glu_.gluNewQuadric(), 0.5, tess, tess);

 
 /*     gl_.glPushMatrix();
        gl_.glRotated(180, 0, 1, 0);
        glu_.gluDisk(glu_.gluNewQuadric(), 0, 0.4, tess, 1);
	gl_.glPopMatrix();
        gl_.glTranslatef(0, 0, 0.8f);
        glu_.gluDisk(glu_.gluNewQuadric(), 0, 0.4, tess, 1); 
        gl_.glPopMatrix();

        gl_.glTranslatef(-2,0,0);
        gl_.glPushMatrix();
//	glu_.gluCylinder(glu_.gluNewQuadric(), 0.4, 0, 0.8, tess, 5);
//	gl_.glRotated(180, 0, 1, 0);
//      glu_.gluDisk(glu_.gluNewQuadric(), 0, 0.4, tess, 1);
        gl_.glPopMatrix();
*/
        gl_.glDisable(GL.GL_TEXTURE_2D);

//        glu_.gluSphere(glu_.gluNewQuadric(), 0.4, tess, tess);

 
}

	

    /** Returns a GL pipeline from the listener */
    public GL getGL() {
        return gl_;
    }
    // The following method is not part of the GLComponentListener interface,
    // but is specific to the DemoScene.

    /** Set rotation of colour cube. Called by virtual trackball handler */
    public void setRotation(float[][] rotationMatrix) {
        rotation = rotationMatrix;
    }
    
    // Draw cube of given size, centred at origin.
    // Cube material is whatever is set up at the time. 
    private void drawCube(double size) {
        gl_.glPushMatrix();
        gl_.glScaled(size, size, size);
        gl_.glTranslatef(-0.5f,-0.5f,-0.5f);
        for (int i=0; i < 6; i++) {
            gl_.glBegin(GL.GL_POLYGON);
            gl_.glNormal3dv(normals[i]);
            for (int j=0; j<faces[i].length; j++)
                gl_.glVertex3dv(vertices[faces[i][j]]);
            gl_.glEnd();
        }
        gl_.glPopMatrix();
    }

}