// This final example shows the vase from ClosdedSurface.java and SplineFileTexture.java
// with a spotted texture from an image file. The image file is spots.ppm

	
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.media.opengl.*;
import javax.media.opengl.glu.*;
import javax.media.opengl.awt.GLCanvas;
import com.jogamp.opengl.util.*;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.FloatBuffer;

import javax.swing.event.*;



public class SplineFileTextureSpots implements GLEventListener, ActionListener, ChangeListener {
	
	public static void main(String[] args) {
		new SplineFileTextureSpots();
	}
	
	private int INITIAL_WIDTH=800;
	private int INITIAL_HEIGHT=800;
	
	JButton quitButton, spinButton;
	private GLCanvas canvas;
	private GL2 gl;
	private GLU glu;
	private boolean spinning = false;
	private float viewerX = 10f;
	private float viewerY  = 2f;
	private float viewerZ = 5f;
	private JSlider xSlider, ySlider, zSlider;
	private float theta = 0f;
	
	private float lightAmbient[] = {0f, 0f, 0f, 1f};
	private float lightDiffuse[] = {0.9f, 0.9f, 0.9f, 1f};
	private float lightSpecular[] = {1f, 1f, 1f, 1f};
	
	private float lightPos[] = {30f, 5f, 10f, 1f};
	
	private float materialDiffuse[] = {1f, 1f, 1f, 1f};
	private float materialSpecular[] = {0.2f, 0.2f, 0.2f, 1f};
	
	private float pseudoCircle[][] = {
			{-1f, 0f, 0f}, {-1f, 0.5f, 0f}, {-0.5f, 1f, 0f}, {0f, 1f, 0f},
			{0.5f, 1f, 0f}, {1f, 0.5f, 0f}, {1f, 0f, 0f}, {1f, -0.5f, 0f},
			{0.5f, -1f, 0f}, {0f, -1f, 0f}, {-0.5f, -1f, 0f}, {-1f, -0.5f, 0f},
			{-1f, 0f, 0f}
	};
	
	private int ROWS;
	private int COLS;
	private float Max;
	
	private int texID[]= new int[1];
	private float buffer[];  // for the texturemap
	private FloatBuffer scaledBuffer;
	
	private float buffer1[]; // for the spline
	
	int count = 0;
	
	public SplineFileTextureSpots() {
			GLProfile glp=GLProfile.getDefault();
			GLCapabilities caps = new GLCapabilities(glp);
			canvas = new GLCanvas(caps);
			canvas.addGLEventListener(this);

			JFrame frame = new JFrame("SPLINE FILE TEXTURE SPOTS");

			frame.setSize(INITIAL_WIDTH, INITIAL_HEIGHT);
			frame.setLayout(new BorderLayout());
			JPanel north = new JPanel( new BorderLayout());
			
			JPanel firstMainRow = new JPanel( new FlowLayout(FlowLayout.LEADING));
			
			quitButton = new JButton( "Quit");
			quitButton.addActionListener(this);
			firstMainRow.add(quitButton);
			spinButton = new JButton( "Spin");
			spinButton.addActionListener(this);
			firstMainRow.add(spinButton);
			
			north.add(firstMainRow, BorderLayout.NORTH);
			
			JPanel secondMainRow = new JPanel(new GridLayout(3, 1));
			
			JPanel row1 = new JPanel(new BorderLayout());
			xSlider = newSlider(row1, -100, 100, 25, "Light Position X");
			xSlider.setValue( (int)(lightPos[0]));
			secondMainRow.add(row1);
			JPanel row2 = new JPanel(new BorderLayout());
			ySlider = newSlider(row2, -100, 100, 25, "Light Position Y");
			ySlider.setValue( (int)(lightPos[1]));
			secondMainRow.add(row2);
			JPanel row3 = new JPanel(new BorderLayout());
			zSlider = newSlider(row3, -100, 100, 25, "Ligt Position Z");
			zSlider.setValue( (int)(lightPos[2]));
			secondMainRow.add(row3);
			
			north.add(secondMainRow, BorderLayout.CENTER);
			
	
			frame.add(north, BorderLayout.NORTH);
			JPanel myCanvas = new JPanel(new GridLayout(1,1));
			myCanvas.add(canvas);
			frame.add(myCanvas, BorderLayout.CENTER);
			
			frame.setVisible(true);

			FPSAnimator animator = new FPSAnimator(canvas, 60);
			animator.start(); 
	}
	
	// This assumes the parent is a panel using BorderLayout.
	JSlider newSlider(JPanel parent, int min, int max, int step, String label) {
		JSlider S = new JSlider(min, max);
		S.setMajorTickSpacing(step);
		S.setPaintTicks(true);
		S.setPaintLabels(true); 
		S.addChangeListener(this);
		JLabel name = new JLabel(label);
		parent.add(name, BorderLayout.WEST); 
		parent.add(S, BorderLayout.CENTER);
		return S;
	}

	public void actionPerformed(ActionEvent event) {
			if (event.getSource() == quitButton)
				System.exit(0);
			else if (event.getSource() == spinButton)
				spinning = !spinning;

	}
	
	public void stateChanged(ChangeEvent e) {
		if (e.getSource() == xSlider) {
			lightPos[0] = xSlider.getValue();
		}
		else if (e.getSource() == ySlider) {
			lightPos[1] = ySlider.getValue();
		}
		else if (e.getSource() == zSlider) {
			lightPos[2] = zSlider.getValue();
		}

	}


	public void display(GLAutoDrawable drawable) {
		update();
		render(drawable);
	}

	private void update() {		
		if (spinning)
			theta += 1f;
	}

	
	private void render(GLAutoDrawable drawable) {
		float Diffuse[] = {0.2f, 0.2f, 1f};
		
		gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
		gl.glMatrixMode(GL2.GL_MODELVIEW);
		gl.glLoadIdentity();
		glu.gluLookAt(viewerX, viewerY, viewerZ, 0f, 0f, 5f, 0f, 0f, 1f);
		gl.glLightfv(GL2.GL_LIGHT0,  GL2.GL_POSITION, lightPos, 0);

		gl.glPushMatrix();
		
		gl.glRotatef(theta, 0f, 0f, 1f);
		gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_DIFFUSE, materialDiffuse, 0);
		drawPatches();
		gl.glPopMatrix();
	}
	
	private void drawPatches() {
		float texel[] = {0f, 0f, 1f, 0f, 0f, 1f, 1f, 1f};// this is a 2x2 array with (0,0) in upper left,
														 // (1,0) in upper right, (0, 1) in lower left
														 // and (1, 1)in lower right
		gl.glEnable(GL2.GL_MAP2_TEXTURE_COORD_2);
		gl.glBindTexture(GL2.GL_TEXTURE_2D, texID[0]);
		for (int row = 0; row < 2; row++) 
			for (int patch = 0; patch <4; patch++) {
				gl.glMap2f(GL2.GL_MAP2_VERTEX_3, 0f,1f, 3, 4, 0f, 1f, 39, 4, FloatBuffer.wrap(buffer1, row*13*3*3+patch*9, 13*3*7-row*13*3*3-patch*9));
				gl.glMap2f(GL2.GL_MAP2_TEXTURE_COORD_2, 0f, 1f, 2, 2, 0f, 1f, 4, 2, FloatBuffer.wrap(texel));
				gl.glMapGrid2f(20, 0f, 1f, 20, 0f, 1f);
				gl.glEvalMesh2(GL2.GL_FILL, 0, 20, 0, 20);
			}
		gl.glDisable(GL2.GL_MAP2_TEXTURE_COORD_2);
	}
	


	public void dispose(GLAutoDrawable drawable) {
		// put the cleanup code here
		
	}

	public void init(GLAutoDrawable drawable) {
		gl = drawable.getGL().getGL2();
		glu = new GLU();
		gl.glEnable(GL2.GL_DEPTH_TEST);
		gl.glMatrixMode(GL2.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluPerspective(60f, 1f, 0.5f, 200f); 
		gl.glMatrixMode(GL2.GL_MODELVIEW);
		gl.glLoadIdentity();
		glu.gluLookAt(viewerX, viewerY, viewerZ, 0f, 0f, 5f, 0f, 0f, 1f);
		gl.glClearColor(1f,  1f, 0f, 1f);
		gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
		
		gl.glEnable(GL2.GL_LIGHTING);
		gl.glEnable(GL2.GL_LIGHT0);
		gl.glLightfv(GL2.GL_LIGHT0,  GL2.GL_AMBIENT, lightAmbient, 0);
		gl.glLightfv(GL2.GL_LIGHT0,  GL2.GL_DIFFUSE, lightDiffuse, 0);
		gl.glLightfv(GL2.GL_LIGHT0,  GL2.GL_SPECULAR, lightSpecular, 0);
		gl.glLightfv(GL2.GL_LIGHT0,  GL2.GL_POSITION, lightPos, 0);

		
		gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_SPECULAR, materialSpecular, 0);
	
		gl.glEnable(GL2.GL_AUTO_NORMAL);
		gl.glEnable(GL2.GL_MAP2_VERTEX_3);
		gl.glEnable(GL2.GL_NORMALIZE);
		
		makeTextureMapFromFile();

		gl.glEnable(GL2.GL_TEXTURE_2D);
		gl.glGenTextures(1, texID, 0);
		gl.glBindTexture(GL2.GL_TEXTURE_2D, texID[0]);
		
		gl.glTexImage2D(GL2.GL_TEXTURE_2D, 0, GL2.GL_RGB, COLS, ROWS, 0, GL2.GL_RGB, GL2.GL_FLOAT, FloatBuffer.wrap(buffer));
		gl.glTexParameterf(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_S, GL2.GL_REPEAT);
		gl.glTexParameterf(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_T, GL2.GL_REPEAT);
		gl.glTexParameterf(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_NEAREST);
		gl.glTexParameterf(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_NEAREST);
		gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_MODULATE);

		buildVase();

	}
	
	public void skipToEndOfLine(FileInputStream input) throws IOException {
		int c = 0;
		while (c != '\n') {
			c = input.read();
		}
	}
	
	boolean isDigit( int c) {
		return ( (c >= '0') && (c <= '9'));
	}
	
	int DigitValue(int c) {
		return c-'0';
	}
	
	int ReadInt(FileInputStream input) throws IOException{
		int value = 0;
		int c = input.read();
		while (isDigit(c)) {
			value *= 10;
			value += DigitValue(c);
			c = input.read();
		}
		return value;
	}
	
	public void makeTextureMapFromFile(){
		String text="";
		try {
			FileInputStream input = new FileInputStream("spots.ppm");
			int c1 = input.read();
			int c2 = input.read();
			if ((c1 != 'P') && (c2 != '6'))
				throw new IOException( "Not a  PPM file");
			skipToEndOfLine(input);
			COLS = ReadInt(input);
			ROWS = ReadInt(input);
			Max = ReadInt(input);
//			System.out.printf( "Rows %d Cols %d Max %f\n", ROWS, COLS, Max);
			buffer = new float[ROWS*COLS*3];
			int red; int green; int blue;
			for (int i = 0; i < ROWS; i++ )
				for (int j = 0; j < COLS; j++) {
					red = input.read();
					buffer[i*COLS*3+j*3] = red/Max;
					green = input.read();
					buffer[i*COLS*3+j*3+1] = green/Max;
					blue = input.read();
					buffer[i*COLS*3+j*3+2] = blue/Max;
				}
// The following should scale the image so its dimensions are powers of 2, but
// the gluScaleImage call gets a "function not implemented" exception.
//			scaledBuffer = FloatBuffer.wrap(new float[256*256*3]);
//			glu.gluScaleImage(GL2.GL_RGB, COLS, ROWS, GL2.GL_FLOAT, FloatBuffer.wrap(buffer), 256, 256, GL2.GL_FLOAT, scaledBuffer);
			
		}
		catch (FileNotFoundException e) {
			System.out.println("oops");
		}
		catch (IOException e1) {
		}
	}
	public boolean isEven(int x) {
		if (x%2 == 0)
			return true;
		else
			return false;
	}
	
	public void assignColor( float dest[], int i, int j, int colorR, int colorG, int colorB){
		dest[i*COLS*3+j*3]=(byte)colorR;
		dest[i*COLS*3+j*3+1]=(byte)colorG;
		dest[i*COLS*3+j*3+2]=(byte)colorB;
	}


	public void buildVase() {
		float radius[] = {1f, 1f, 1.5f, 1.5f, 2f, 2.5f, 2.5f};
		buffer1 = new float[7*13*3];
		for (int i = 0; i < 7; i++)
			for (int j = 0; j < 13; j++) {
				for (int k = 0; k < 2; k++)
					buffer1[i*13*3+j*3+k] = pseudoCircle[j][k]*radius[i];
				buffer1[i*13*3+j*3+2] = 8-i;
			}
	}
			

	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
		// this is called when the window is resized
		gl.glViewport(0, 0, width, height);
		float aspect = width*1.0f/height;
		gl.glMatrixMode(GL2.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluPerspective(60f, aspect, 0.5f, 200f); 
		gl.glMatrixMode(GL2.GL_MODELVIEW);
		
		
	}
	
}
