package forum;

import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.FloatBuffer;
import java.util.Random;

import com.jogamp.common.nio.Buffers;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.awt.GLCanvas;
import com.jogamp.opengl.util.Animator;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
public class Main extends GLCanvas implements GLEventListener {

	private Random random = new Random();

	// VBO variables

	int[] vbo = new int[] { -1 };

	int maxNumberOfPoints = 1000000; // GL_POINTS
	int pointVertexSize = 3; // X,Y,Z
	int numberOfPointsAdded = 0;

	int pointOffset = 0;
	int increaseRate = 1000;

	// ===================================================
	// VBO related functions
	// ===================================================

	private void initVBO(GL2 gl) {

		gl.glGenBuffers(1, vbo, 0);
		gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, vbo[0]);
		gl.glBufferData(GL2.GL_ARRAY_BUFFER, Buffers.SIZEOF_FLOAT * maxNumberOfPoints * pointVertexSize, null,
				GL2.GL_DYNAMIC_DRAW);

		gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);

	}

	private void updateVBO(GL2 gl) {

		gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, vbo[0]);

		FloatBuffer floatBuffer = Buffers.newDirectFloatBuffer(increaseRate * 3);

		for (int i = 0; i < increaseRate; i++) {

			floatBuffer.put(random.nextFloat() + random.nextInt(1000)); // X
			// coordinate
			floatBuffer.put(random.nextFloat() + random.nextInt(1000)); // Y
			// coordinate
			floatBuffer.put(0);

			pointOffset += 3 * Buffers.SIZEOF_FLOAT;
			numberOfPointsAdded += 1;
		}

		floatBuffer.rewind();

		gl.glBufferSubData(GL2.GL_ARRAY_BUFFER, pointOffset, increaseRate * Buffers.SIZEOF_FLOAT * 3, floatBuffer);

		gl.glUnmapBuffer(GL2.GL_ARRAY_BUFFER);
		gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);

	}

	private void renderVBO(GL2 gl) {
		gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, vbo[0]);

		gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
		gl.glVertexPointer(3, GL2.GL_FLOAT, 0, 0l);

		gl.glDrawArrays(GL2.GL_POINTS, 0, numberOfPointsAdded);

		gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
		gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);

	}

	private void deleteVBO(GL2 gl) {

		gl.glDeleteBuffers(1, vbo, 0);

	}

	// ===================================================
	// OpenGL Callback Functions
	// ===================================================
	@Override
	public void init(GLAutoDrawable drawable) {
		GL2 gl = drawable.getGL().getGL2();

		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		gl.glClearDepth(1.0f);
		gl.glEnable(GL2.GL_DEPTH_TEST);
		gl.glDepthFunc(GL2.GL_LEQUAL);
		gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL2.GL_NICEST);
		gl.glShadeModel(GL2.GL_SMOOTH);

		initVBO(gl);
	}

	@Override
	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
		GL2 gl = drawable.getGL().getGL2();

		gl.glViewport(0, 0, width, height);

		gl.glMatrixMode(GL2.GL_PROJECTION);
		gl.glLoadIdentity();
		gl.glOrthof(0, 1000, 0, 1000, -1, 1);

		gl.glMatrixMode(GL2.GL_MODELVIEW);
		gl.glLoadIdentity();
	}

	@Override
	public void display(GLAutoDrawable drawable) {

		GL2 gl = drawable.getGL().getGL2();

		gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
		gl.glLoadIdentity();

		gl.glColor3f(1f, 0f, 0f);
		updateVBO(gl);
		renderVBO(gl);

		if (System.currentTimeMillis() - lastTimeInMillis > 1000) {
			System.out.println("FPS: " + fpsCounter);

			fpsCounter = 0;
			lastTimeInMillis = System.currentTimeMillis();
		}
		fpsCounter++;

	}

	@Override
	public void dispose(GLAutoDrawable drawable) {
		GL2 gl = drawable.getGL().getGL2();
		deleteVBO(gl);
	}

	// ===================================================
	// Main
	// ===================================================

	private static String TITLE = "JOGL 2.0 Setup (GLCanvas)";
	private static final int CANVAS_WIDTH = 320;
	private static final int CANVAS_HEIGHT = 240;
	private static final int FPS = 60;

	private int fpsCounter = 0;
	private long lastTimeInMillis = System.currentTimeMillis();

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {

				GLCanvas canvas = new Main();
				canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));

				final Animator animator = new Animator(canvas);
				animator.setRunAsFastAsPossible(false);

				final JFrame frame = new JFrame();
				frame.getContentPane().add(canvas);
				frame.addWindowListener(new WindowAdapter() {
					@Override
					public void windowClosing(WindowEvent e) {
						new Thread() {
							@Override
							public void run() {
								if (animator.isStarted())
									animator.stop();
								System.exit(0);
							}
						}.start();
					}
				});
				frame.setTitle(TITLE);
				frame.pack();
				frame.setVisible(true);
				animator.start();
			}
		});
	}

	public Main() {
		this.addGLEventListener(this);
	}

}