import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.media.opengl.*;

import com.sun.opengl.util.*;


public class SpinningOpenBox extends JFrame implements GLEventListener, 
													 MouseListener, 
													 MouseMotionListener {
	public static final long serialVersionUID = 0;
	public static final Dimension PREFERRED_FRAME_SIZE = new Dimension(600, 600);
	private float view_rotx = 0.0f, view_roty = 0.0f, view_rotz = 0.0f;
	private int box;
	private float angle = 0.0f;

	private int prevMouseX, prevMouseY;
//	private boolean mouseRButtonDown = false;


	/** Constructor.
	*/
	public SpinningOpenBox() {
		// init JFrame
		super ("SpinningOpenBox");
	}

	/** We'd like to be 400x400, please.
	*/
	public Dimension getPreferredSize(){
		return PREFERRED_FRAME_SIZE;
	}

	/*
	* METHODS DEFINED BY GLEventListener
	*/
	
	/** Called by drawable to initiate drawing. 
	*/
	public void display(GLAutoDrawable drawable) {
		angle += 2.0f;
		
		GL gl = drawable.getGL();
		
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
		
		gl.glPushMatrix();
		gl.glRotatef(view_rotx, 1.0f, 0.0f, 0.0f);
		gl.glRotatef(view_roty, 0.0f, 1.0f, 0.0f);
		gl.glRotatef(view_rotz, 0.0f, 0.0f, 1.0f);
		
		gl.glPushMatrix();
		gl.glTranslatef(0.0f, 0.0f, -3.0f);
		gl.glRotatef(angle, 0.0f, 0.0f, 1.0f);
		gl.glCallList(box);
		gl.glPopMatrix();
		
		gl.glPopMatrix();		
	}
	
	/** Called by drawable to indicate mode or device has changed.
	*/
	public void displayChanged(GLAutoDrawable drawable, 
		boolean modeChanged, 
		boolean deviceChanged){}
		
	/** Called after OpenGL is init'ed
	*/
	public void init(GLAutoDrawable drawable) {
		
		GL gl = drawable.getGL();
		
		float pos[] = { 10.0f, 5.0f, 10.0f, 1.0f };
		float direction[] = {0.0f - pos[0], 0.0f - pos[1], 0.0f - pos[2] };
		float len = (float) Math.sqrt(direction[0] * direction[0] + 
				                      direction[1] * direction[1] + 
				                      direction[2] * direction[2]);
		direction[0] /= len;
		direction[1] /= len;
		direction[2] /= len;
		float black[] = {0.0f, 0.0f, 0.0f, 1.0f} ;
		float darkgray [] = {0.2f, 0.2f, 0.2f, 1.0f };
		float gray[] = { 0.6f, 0.6f, 0.6f, 1.0f };
		float white[] = { 1.0f, 1.0f, 1.0f, 1.0f};
		float red[] = { 0.7f, 0.1f, 0.0f, 1.0f };
		float green[] = { 0.0f, 0.8f, 0.2f, 1.0f };
		float blue[] = { 0.2f, 0.2f, 1.0f, 1.0f };
		float yellow[] = {0.7f, 0.7f, 0.0f, 1.0f };
		
		/* Enable features */
//		gl.glEnable(GL.GL_CULL_FACE);				
		gl.glEnable(GL.GL_LIGHTING); 
		gl.glEnable(GL.GL_LIGHT0);  
		gl.glEnable(GL.GL_DEPTH_TEST);
		
		/* Select shading model */
		gl.glShadeModel(GL.GL_SMOOTH); 
		
		/* Select lighting Model */
		gl.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, gray, 0); 
		gl.glLightModelf(GL.GL_LIGHT_MODEL_LOCAL_VIEWER, GL.GL_TRUE);
		gl.glLightModelf(GL.GL_LIGHT_MODEL_TWO_SIDE, GL.GL_TRUE);
		
		/* Configure source light */
		gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, pos, 0);
		gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, black, 0);
		gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, white, 0);
		gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, white, 0);
	
		/* Set material and construct spherical sector */
		box = gl.glGenLists(1);
		gl.glNewList(box, GL.GL_COMPILE);
		/* Set material */
		gl.glMaterialfv(GL.GL_BACK, GL.GL_AMBIENT, darkgray, 0);
		gl.glMaterialfv(GL.GL_BACK, GL.GL_DIFFUSE, darkgray, 0);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, red, 0);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, red, 0);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, white, 0);
		gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, 128.0f);
		
		openBox(gl, 6);
		gl.glEndList();
		
		drawable.addMouseListener(this);
		drawable.addMouseMotionListener(this);
	}
	
	/** Called to indicate the drawing surface has been moved and/or resized.
	*/
	public void reshape(GLAutoDrawable drawable, 
		                int x, int y, 
		                int width, int height){
		
		GL gl = drawable.getGL();
		
		float h = (float)height / (float)width;            
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		gl.glFrustum(-1.0f, 1.0f, -h, h, 5.0f, 60.0f);
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();
		gl.glTranslatef(0.0f, 0.0f, -40.0f);
	}
	
    /*
     * OUR HELPER METHODS
     */
	
	/** Constructs a sphere 
	 */
	public static void openBox(GL gl, float scale){
		float v1[] = { scale * -0.5f, scale *  0.5f, scale *  0.5f};
		float v2[] = { scale * -0.5f, scale * -0.5f, scale *  0.5f};
		float v3[] = { scale *  0.5f, scale * -0.5f, scale *  0.5f};
		float v4[] = { scale *  0.5f, scale *  0.5f, scale *  0.5f};
		float v5[] = { scale * -0.5f, scale *  0.5f, scale * -0.5f};
		float v6[] = { scale * -0.5f, scale * -0.5f, scale * -0.5f};
		float v7[] = { scale *  0.5f, scale * -0.5f, scale * -0.5f};
		float v8[] = { scale *  0.5f, scale *  0.5f, scale * -0.5f};


		gl.glBegin(GL.GL_POLYGON);
	    gl.glNormal3f(0.0f, 0.0f, 1.0f);
		gl.glVertex3fv(v1, 0);
	    gl.glNormal3f(0.0f, 0.0f, 1.0f);
		gl.glVertex3fv(v2, 0);
	    gl.glNormal3f(0.0f, 0.0f, 1.0f);
		gl.glVertex3fv(v3, 0);
	    gl.glNormal3f(0.0f, 0.0f, 1.0f);
		gl.glVertex3fv(v4, 0);
		gl.glEnd();
		

		gl.glBegin(GL.GL_POLYGON);
	    gl.glNormal3f(0.0f, 0.0f, -1.0f);
		gl.glVertex3fv(v5, 0);
	    gl.glNormal3f(0.0f, 0.0f, -1.0f);
		gl.glVertex3fv(v8, 0);
	    gl.glNormal3f(0.0f, 0.0f, -1.0f);
		gl.glVertex3fv(v7, 0);
	    gl.glNormal3f(0.0f, 0.0f, -1.0f);
		gl.glVertex3fv(v6, 0);
		gl.glEnd();
		
		gl.glBegin(GL.GL_POLYGON);
	    gl.glNormal3f(0.0f, 1.0f, 0.0f);
		gl.glVertex3fv(v1, 0);
	    gl.glNormal3f(0.0f, 1.0f, 0.0f);
		gl.glVertex3fv(v4, 0);
	    gl.glNormal3f(0.0f, 1.0f, 0.0f);
		gl.glVertex3fv(v8, 0);
	    gl.glNormal3f(0.0f, 1.0f, 0.0f);
		gl.glVertex3fv(v5, 0);
		gl.glEnd();
		
		gl.glBegin(GL.GL_POLYGON);
	    gl.glNormal3f(0.0f, -1.0f, 0.0f);
		gl.glVertex3fv(v2, 0);
	    gl.glNormal3f(0.0f, -1.0f, 0.0f);
		gl.glVertex3fv(v6, 0);
	    gl.glNormal3f(0.0f, -1.0f, 0.0f);
		gl.glVertex3fv(v7, 0);
	    gl.glNormal3f(0.0f, -1.0f, 0.0f);
		gl.glVertex3fv(v3, 0);
		gl.glEnd();
		

	}
	
	
	// Methods required for the implementation of MouseListener
	public void mouseEntered(MouseEvent e){}
	public void mouseExited(MouseEvent e){}

	public void mousePressed(MouseEvent e){
		prevMouseX = e.getX();
		prevMouseY = e.getY();
		if ((e.getModifiers() & MouseEvent.BUTTON3_MASK) != 0){
	//		mouseRButtonDown = true;
		}
	}
    
	public void mouseReleased(MouseEvent e){
		if ((e.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) {
//			mouseRButtonDown = false;
		}
	}
    
	public void mouseClicked(MouseEvent e){}
    
	// Methods required for the implementation of MouseMotionListener
	public void mouseDragged(MouseEvent e){
		int x = e.getX();
		int y = e.getY();
		Dimension size = e.getComponent().getSize();

		float thetaY = 360.0f * ( (float)(x-prevMouseX)/(float)size.width);
		float thetaX = 360.0f * ( (float)(prevMouseY-y)/(float)size.height);
    
		prevMouseX = x;
		prevMouseY = y;

		view_rotx += thetaX;
		view_roty += thetaY;
	}
    
	public void mouseMoved(MouseEvent e){}
	
    /** main creates and shows a Gears-JFrame and starts animator
     */
	public static void main(String[] args){

		SpinningOpenBox g = new SpinningOpenBox();    
		// get a GLCanvas
		GLCanvas canvas = new GLCanvas(new GLCapabilities());
		// add a GLEventListener, which will get called when the
		// canvas is resized or needs a repaint
		canvas.addGLEventListener(g);
		// now add the canvas to the JFrame.  Note we use BorderLayout.CENTER
		// to make the canvas stretch to fill the container (ie, the frame)
		g.getContentPane().add(canvas, BorderLayout.CENTER);
		final Animator animator = new Animator(canvas);
		g.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				// Run this on another thread than the AWT event queue to
				// make sure the call to Animator.stop() completes before
				// exiting
				new Thread(new Runnable() {
					public void run() {
						animator.stop();
						System.exit(0);
					}
				}).start();
			}
		});
    
		animator.start(); 
		g.pack();
		g.setVisible(true);

	}
}

