import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.media.opengl.*;

import com.sun.opengl.util.*;


public class SpinningSector extends JFrame implements GLEventListener, 
													 MouseListener, 
													 MouseMotionListener {
	public static final long serialVersionUID = 0;
	private static final int SPHERICAL_SECTOR = 0;
	private static final int HEMISPHERE = 1;
	private static final int SPHERE = 3;
	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 sector;
	private float angle = 0.0f;

	private int prevMouseX, prevMouseY;
//	private boolean mouseRButtonDown = false;


	/** Constructor.
	*/
	public SpinningSector() {
		// init JFrame
		super ("SpinningSector");
	}

	/** 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 += 1.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.glRotatef(angle, 0.0f, 0.0f, 1.0f);
		gl.glCallList(sector);
		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[] = { 40.0f, 40.0f, 00.0f, 1.0f };
		
		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.1f, 0.1f, 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_DEPTH_TEST);
		gl.glEnable(GL.GL_LIGHTING); 
		gl.glEnable(GL.GL_LIGHT0);  

		/* Select shading model */
		gl.glShadeModel(GL.GL_SMOOTH); 
		
		/* Select lighting Model */
		gl.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, white, 0); 
		gl.glLightModelf(GL.GL_LIGHT_MODEL_LOCAL_VIEWER, GL.GL_TRUE);
		gl.glLightModelf(GL.GL_LIGHT_MODEL_TWO_SIDE, GL.GL_FALSE);
		
		/* 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 */
		sector = gl.glGenLists(1);
		gl.glNewList(sector, GL.GL_COMPILE);
		/* Set material */
		gl.glMaterialfv(GL.GL_BACK, GL.GL_AMBIENT, yellow, 0);
		gl.glMaterialfv(GL.GL_BACK, GL.GL_DIFFUSE, red, 0);
		gl.glMaterialfv(GL.GL_BACK, GL.GL_SPECULAR, gray, 0);
		gl.glMaterialf(GL.GL_BACK, GL.GL_SHININESS, 100.0f);
		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, 100.0f);
		
		buildSector(gl, 6.0f, 8, SPHERE);
		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, -20.0f);
	}
	
    /*
     * OUR HELPER METHODS
     */
	
	/** Constructs a sphere 
	 */
	public static void buildSector(GL gl, float radius, int res, int type){
		float n1[] = new float[3];
		float n2[] = new float[3];
		float n3[] = new float[3];
		float n4[] = new float[3];
		float dteta = (float) Math.PI / (2.0f * (float) res);
		float dphi =  (float) Math.PI / (2.0f * (float) res);
		int tetaSteps;
		int phiSteps;
		
		switch(type) {
			case SPHERICAL_SECTOR:
				tetaSteps = 2 * res;
				phiSteps = res;
			break;
			case HEMISPHERE:
				tetaSteps = 2 * res;
				phiSteps = 2 * res;
			break;
			case SPHERE:
				tetaSteps = 4 * res;
				phiSteps= 2 * res;
			break;
			default:
				tetaSteps = 2 * res;
				phiSteps = res;
		}
		
		for(int i = 0; i < tetaSteps; i++)
			for(int j = 0; j < phiSteps; j++) {
				gl.glBegin(GL.GL_POLYGON);
				n1[0] = (float) Math.cos(i * dteta) * (float) Math.sin(j * dphi);
				n1[1] = (float) Math.sin(i * dteta) * (float) Math.sin(j * dphi);
				n1[2] = (float) Math.cos(j * dphi);
				n2[0] = (float)Math.cos(i * dteta) * (float) Math.sin((j + 1) * dphi);
				n2[1] = (float)Math.sin(i * dteta) * (float) Math.sin((j + 1) * dphi);
				n2[2] = (float)Math.cos((j + 1) * dphi);
				n3[0] = (float)Math.cos((i + 1) * dteta) * (float) Math.sin((j + 1) * dphi);
				n3[1] = (float)Math.sin((i + 1) * dteta) * (float) Math.sin((j + 1) * dphi);
				n3[2] = (float)Math.cos((j + 1) * dphi);
				n4[0] = (float)Math.cos((i + 1) * dteta) * (float) Math.sin(j * dphi);
				n4[1] = (float)Math.sin((i + 1) * dteta) * (float) Math.sin(j * dphi);
				n4[2] = (float)Math.cos(j * dphi);
				gl.glNormal3fv(n1, 0);
				gl.glVertex3f(radius * n1[0], radius * n1[1], radius * n1[2]);
				gl.glNormal3fv(n2, 0);
				gl.glVertex3f(radius * n2[0], radius * n2[1], radius * n2[2]);
				gl.glNormal3fv(n3, 0);
				gl.glVertex3f(radius * n3[0], radius * n3[1], radius * n3[2]);
				gl.glNormal3fv(n4, 0);
				gl.glVertex3f(radius * n4[0], radius * n4[1], radius * n4[2]);
				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){

		SpinningSector g = new SpinningSector();    
		// 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);

	}
}

