/**
 * Copyright 2013 JogAmp Community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of JogAmp Community.
 */
package com.jogamp.opengl.test.junit.jogl.demos.gl2.awt;

import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GL2ES1;
import com.jogamp.opengl.GL2GL3;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.awt.GLCanvas;
import com.jogamp.opengl.awt.GLJPanel;
import com.jogamp.opengl.fixedfunc.GLLightingFunc;
import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
import com.jogamp.opengl.fixedfunc.GLPointerFunc;
import javax.swing.JApplet;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.SwingConstants;

import com.jogamp.common.util.VersionUtil;
import com.jogamp.opengl.JoglVersion;
import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.texture.Texture;
import com.jogamp.opengl.util.texture.TextureIO;

/**
 * Bug 818: OSX GLJPanel [and GLCanvas] Crash
 * <pre>
 *   - NVIDIA GeForce GT 330M
 *     - GL_VENDOR: "NVIDIA Corporation"
 *     - GL_RENDERER: "NVIDIA GeForce GT 330M OpenGL Engine"
 *     - GL_VERSION: "2.1 NVIDIA-8.12.47 310.40.00.05f01"
 *   - Mac OSX 10.6.8
 * </pre>
 */
public class Bug818GLJPanelAndGLCanvasApplet extends JApplet {

    private static final long serialVersionUID = 1L;

    private Animator animatorCanvas;

    private Animator animatorPanel;

  public static JFrame frame;
  public static JPanel appletHolder;
  public static boolean isApplet = true;

  static public void main(final String args[]) {
    isApplet = false;

    final JApplet myApplet = new Bug818GLJPanelAndGLCanvasApplet();

    appletHolder = new JPanel();

    frame = new JFrame("Bug818GLJPanelApplet");
    frame.getContentPane().add(myApplet);

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(final WindowEvent e) {
        System.exit(0);
      }
    });

    try {
        javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
            public void run() {
                myApplet.init();
                frame.validate();
                frame.pack();
                frame.setVisible(true);
            } } );
    } catch( final Throwable throwable ) {
        throwable.printStackTrace();
    }

    myApplet.start();
  }


    @Override
    public void init() {

        final JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(2, 2));
        System.err.println("Pre  Orientation L2R: "+panel.getComponentOrientation().isLeftToRight());
        panel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
        System.err.println("Post Orientation L2R: "+panel.getComponentOrientation().isLeftToRight());
        setContentPane(panel);
        panel.add(new JLabel("GLJPanel", SwingConstants.CENTER));
        panel.add(new JLabel("GLCanvas", SwingConstants.CENTER));

        final GLJPanel gljPanel = new GLJPanel();
        gljPanel.addGLEventListener(new JOGLQuad(false));
        animatorPanel = new Animator(gljPanel);
        gljPanel.setPreferredSize(new Dimension(300, 300));
        panel.add(gljPanel);

        final GLCanvas glCanvas = new GLCanvas();
        glCanvas.addGLEventListener(new JOGLQuad(true));
        animatorCanvas = new Animator(glCanvas);
        glCanvas.setPreferredSize(new Dimension(300, 300));
        panel.add(glCanvas);
    }

    @Override
    public void start() {

        animatorCanvas.start();
        animatorCanvas.setUpdateFPSFrames(60, System.err);
        animatorPanel.start();
        animatorPanel.setUpdateFPSFrames(60, System.err);
    }

    @Override
    public void stop() {

        animatorCanvas.stop();
        animatorPanel.stop();
    }

    @Override
    public void destroy() {}

    /**
     * Self-contained example (within a single class only to keep it simple) displaying a rotating quad
     */
    static class JOGLQuad implements GLEventListener {

        private static final float[] VERTEX_DATA = {
            -1.0f, 1.0f, 0.0f,      // Top Left
            1.0f, 1.0f, 0.0f,       // Top Right
            1.0f, -1.0f, 0.0f,      // Bottom Right
            -1.0f, -1.0f, 0.0f      // Bottom Left
        };

        private static final float[] TEXCOORD_DATA = {
            0.0f, 1.0f,     // Top Left
            1.0f, 1.0f,     // Top Right
            1.0f, 0.0f,     // Bottom Right
            0.0f, 0.0f      // Bottom Left
        };

        private final FloatBuffer vertexBuf;

        private final FloatBuffer texCoordBuf;

        private int vertexVBO;

        private int texCoordVBO;

        private float rotateT = 0.0f;

        private final boolean canvas;

        private Texture texture;

        JOGLQuad(final boolean canvas) {

            this.canvas = canvas;

            ByteBuffer bb = ByteBuffer.allocateDirect(VERTEX_DATA.length * 4);
            bb.order(ByteOrder.nativeOrder());
            vertexBuf = bb.asFloatBuffer();
            vertexBuf.put(VERTEX_DATA);
            vertexBuf.rewind();

            bb = ByteBuffer.allocateDirect(TEXCOORD_DATA.length * 4);
            bb.order(ByteOrder.nativeOrder());
            texCoordBuf = bb.asFloatBuffer();
            texCoordBuf.put(TEXCOORD_DATA);
            texCoordBuf.rewind();
        }

        @Override
        public void init(final GLAutoDrawable glDrawable) {

            final GL2 gl = glDrawable.getGL().getGL2();

            System.err.println(VersionUtil.getPlatformInfo());
            System.err.println(JoglVersion.getGLInfo(gl, null, false /* withCapabilitiesAndExtensionInfo */).toString());

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

            final int[] tmp = new int[2];
            gl.glGenBuffers(tmp.length, tmp, 0);
            vertexVBO = tmp[0];
            texCoordVBO = tmp[1];

            gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vertexVBO);
            gl.glBufferData(GL.GL_ARRAY_BUFFER, VERTEX_DATA.length * 4, vertexBuf, GL.GL_STATIC_DRAW);
            gl.glBindBuffer(GL.GL_ARRAY_BUFFER, texCoordVBO);
            gl.glBufferData(GL.GL_ARRAY_BUFFER, TEXCOORD_DATA.length * 4, texCoordBuf, GL.GL_STATIC_DRAW);
            gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);

            try {
                final InputStream stream = getClass().getClassLoader().getResourceAsStream("com/jogamp/opengl/test/junit/jogl/util/texture/test-ntscN_3-01-160x90-90pct-yuv444-base.jpg");
                texture = TextureIO.newTexture(stream, true, TextureIO.JPG);
            } catch (final Exception exc) {
                exc.printStackTrace(System.err);
            }
        }

        @Override
        public void dispose(final GLAutoDrawable drawable) {

            final GL2 gl = drawable.getGL().getGL2();
            final int[] tmp = new int[] {vertexVBO, texCoordVBO};
            gl.glGenBuffers(tmp.length, tmp, 0);
        }

        @Override
        public void reshape(final GLAutoDrawable gLDrawable, final int x, final int y, final int width, final int height) {

            final GL2 gl = gLDrawable.getGL().getGL2();
            final float aspect = (float) width / (float) height;
            gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
            gl.glLoadIdentity();
            final float fh = 0.5f;
            final float fw = fh * aspect;
            gl.glFrustumf(-fw, fw, -fh, fh, 1.0f, 1000.0f);
            gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
            gl.glLoadIdentity();
        }

        @Override
        public void display(final GLAutoDrawable gLDrawable) {

            final GL2 gl = gLDrawable.getGL().getGL2();

            gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
            gl.glLoadIdentity();
            gl.glTranslatef(0.0f, 0.0f, -5.0f);

            // rotate about the three axes
            gl.glRotatef(rotateT, 1.0f, 0.0f, 0.0f);
            gl.glRotatef(rotateT, 0.0f, 1.0f, 0.0f);
            gl.glRotatef(rotateT, 0.0f, 0.0f, 1.0f);

            // set the color of the quad
            if (canvas) {
                gl.glColor3f(0.2f, 0.2f, 1.0f);
            } else {
                gl.glColor3f(1.0f, 0.2f, 0.2f);
            }

            if (texture != null) {
                texture.bind(gl);
                texture.enable(gl);
            } else {
                System.err.println("no texture");
            }

            // Draw A Quad
            gl.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
            gl.glEnableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY);
            gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vertexVBO);
            gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0);
            gl.glBindBuffer(GL.GL_ARRAY_BUFFER, texCoordVBO);
            gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, 0);
            gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
            gl.glDrawArrays(GL2GL3.GL_QUADS, 0, 4);
            gl.glDisableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY);
            gl.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY);

            if (texture != null) {
                texture.disable(gl);
            }

            // increasing rotation for the next iteration
            rotateT += 0.2f;
        }

    }
}

