// This code example is created for educational purpose
// by Thorsten Thormaehlen (contact: www.thormae.de).
// It is distributed without any warranty.

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import javax.imageio.ImageIO;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.GLProfile;
import com.jogamp.opengl.awt.GLCanvas;
import com.jogamp.opengl.glu.GLU;
import javax.swing.JFrame;

import com.jogamp.opengl.util.FPSAnimator;

class Renderer {

  private GLU glu = new GLU();

  public float t = -90.0f;
  public int wrapS = GL2.GL_REPEAT;
  public int wrapT = GL2.GL_REPEAT;
  public int magFilter = GL2.GL_LINEAR;
  public int minFilter = GL2.GL_LINEAR_MIPMAP_LINEAR;
  public int selectedTexID = 0;
  
  
  private int texID0 = 0;
  private int texID1 = 0;

  public void init(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2(); // get the OpenGL 2 graphics context
    gl.glEnable(GL2.GL_DEPTH_TEST);
    texID0 = loadTexture(d, "checker_texture_512.png");
    texID1 = loadTexture(d, "checker_texture_32.png");
  }

  public void resize(GLAutoDrawable d, int w, int h) {
    GL2 gl = d.getGL().getGL2(); // get the OpenGL 2 graphics context
    gl.glViewport(0, 0, w, h);
    gl.glMatrixMode(GL2.GL_PROJECTION);
    gl.glLoadIdentity();
    glu.gluPerspective (30.0, (float)w/(float)h, 0.1, 50.0);
  }

  public void display(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context
    gl.glClearColor(0.7f, 0.7f, 0.7f, 0.0f);
    gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);

    gl.glMatrixMode(GL2.GL_MODELVIEW);
    gl.glLoadIdentity();
    // set camera
    glu.gluLookAt(8.0, -2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
    // draw scene
    gl.glRotatef(t, 0.0f, 0.0f, 1.0f);
    drawTexturedPlane(d);
  }

  // returns a valid textureID on success, otherwise 0
  private int loadTexture(GLAutoDrawable d, String filename) {
    GL2 gl = d.getGL().getGL2(); // get the OpenGL 2 graphics context

    int width;
    int height;
    int level = 0;
    int border = 0;

    try{
      // open file
      FileInputStream fileInputStream = new FileInputStream(new File(filename));

      // read image
      BufferedImage bufferedImage = ImageIO.read(fileInputStream);
      fileInputStream.close();

      width = bufferedImage.getWidth();
      height = bufferedImage.getHeight();

      // convert image to ByteBuffer
      int[] pixelIntData = new int[width * height];
      bufferedImage.getRGB(0, 0, width, height, pixelIntData, 0, width);
      ByteBuffer buffer = ByteBuffer.allocateDirect(pixelIntData.length * 4);
      buffer.order(ByteOrder.nativeOrder());
	  // Unpack the data, each integer into 4 bytes of the ByteBuffer.
	  // Also we need to vertically flip the image because the image origin
	  // in OpenGL is the lower-left corner.
	  for(int y=0; y < height; y++) {
		int k = (height-1-y) * width;
		for(int x=0; x < width; x++) {
			buffer.put((byte)(pixelIntData[k]>>> 16));
			buffer.put((byte)(pixelIntData[k]>>> 8));
			buffer.put((byte)(pixelIntData[k]));
			buffer.put((byte)(pixelIntData[k]>>> 24));
			k++;
		}
      }
      buffer.rewind();

      // data is aligned in byte order
      gl.glPixelStorei(GL2.GL_UNPACK_ALIGNMENT, 1);

      //request textureID
      final int[] textureID = new int[1];
      gl.glGenTextures( 1, textureID, 0);

      // bind texture
      gl.glBindTexture(GL2.GL_TEXTURE_2D, textureID[0]);

      // parameters that define how to warp the texture
      gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_S, GL2.GL_REPEAT);
      gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_T, GL2.GL_REPEAT);
      float[] borderColor = {1.0f, 1.0f, 0.0f, 1.0f};
      gl.glTexParameterfv(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_BORDER_COLOR, borderColor, 0);

      // parameters that define how to filter the texture
      gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_LINEAR);
      gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_LINEAR_MIPMAP_LINEAR);
      gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_GENERATE_MIPMAP, GL2.GL_TRUE);


      // texture colors should replace the original color values
      gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_REPLACE); //GL_MODULATE

      // specify the 2D texture map
      gl.glTexImage2D(GL2.GL_TEXTURE_2D, level, GL2.GL_RGB, width, height, border, GL2.GL_RGBA, GL2.GL_UNSIGNED_BYTE, buffer);

      return textureID[0];
    } catch( FileNotFoundException e) {
      System.out.println("Can not find texture data file " + filename);
    } catch(IOException e) {
      e.printStackTrace( );
    }
    return 0;
  }

  private void setTextureParameters(GLAutoDrawable d) { 
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context
    gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_S, wrapS);
    gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_T, wrapT);
    gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, magFilter);
    gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, minFilter);
  }
  
  
  private void drawTexturedPlane(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context
    gl.glEnable(GL2.GL_TEXTURE_2D);

    if(selectedTexID == 0) {
      gl.glBindTexture(GL2.GL_TEXTURE_2D, texID0);
    }else{
      gl.glBindTexture(GL2.GL_TEXTURE_2D, texID1);
    }

    setTextureParameters(d);

    gl.glColor3f(1.0f,0.0f,0.0f);
    gl.glBegin(GL2.GL_POLYGON);
    gl.glTexCoord2f( 5.00f,  5.00f); gl.glVertex3f(-10.0f, 10.0f, 0.0f);
    gl.glTexCoord2f( 5.00f, -4.00f); gl.glVertex3f(-10.0f,-10.0f, 0.0f);
    gl.glTexCoord2f(-4.00f, -4.00f); gl.glVertex3f( 10.0f,-10.0f, 0.0f);
    gl.glTexCoord2f(-4.00f,  5.00f); gl.glVertex3f( 10.0f, 10.0f, 0.0f);
    gl.glEnd();
    gl.glDisable(GL2.GL_TEXTURE_2D);
  }


}

class MyGui extends JFrame implements GLEventListener {

  private Renderer renderer;

  public void createGUI() {
    setTitle("Use the 1 to 5 keys to change the texture parameters");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    GLProfile glp = GLProfile.getDefault();
    GLCapabilities caps = new GLCapabilities(glp);
    GLCanvas canvas = new GLCanvas(caps);
    setSize(320, 320);

    getContentPane().add(canvas);
    final FPSAnimator ani = new FPSAnimator(canvas, 60, true);
    canvas.addGLEventListener(this);
    setVisible(true);
    renderer = new Renderer();

    canvas.addKeyListener(new KeyAdapter() {
      public void keyPressed(KeyEvent event) {
        boolean redraw = false;
        String mode = "";
        switch(event.getKeyCode()) {
        case '1':
          if(renderer.wrapS == GL2.GL_REPEAT) {
            renderer.wrapS = GL2.GL_CLAMP;
            mode = "wrapS = GL_CLAMP";
          }
          else if(renderer.wrapS == GL2.GL_CLAMP) {
            renderer.wrapS = GL2.GL_MIRRORED_REPEAT;
            mode = "wrapS = GL_MIRRORED_REPEAT";
          }
          else if(renderer.wrapS == GL2.GL_MIRRORED_REPEAT) {
            renderer.wrapS = GL2.GL_CLAMP_TO_BORDER;
            mode = "wrapS = GL_CLAMP_TO_BORDER";
          }
          else if(renderer.wrapS == GL2.GL_CLAMP_TO_BORDER) {
            renderer.wrapS = GL2.GL_REPEAT;
            mode = "wrapS = GL_REPEAT";
          }

          redraw = true;
          break;
        case '2':
          if(renderer.wrapT == GL2.GL_REPEAT) {
            renderer.wrapT = GL2.GL_CLAMP;
            mode = "wrapT = GL_CLAMP";
          }
          else if(renderer.wrapT == GL2.GL_CLAMP) {
            renderer.wrapT =  GL2.GL_MIRRORED_REPEAT;
            mode = "wrapT =  GL_MIRRORED_REPEAT";
          }
          else if(renderer.wrapT == GL2.GL_MIRRORED_REPEAT) {
            renderer.wrapT = GL2.GL_CLAMP_TO_BORDER;
            mode = "wrapT = GL_CLAMP_TO_BORDER";
          }
          else if(renderer.wrapT == GL2.GL_CLAMP_TO_BORDER) {
            renderer.wrapT = GL2.GL_REPEAT;
            mode = "wrapT = GL_REPEAT";
          }
          redraw = true;
          break;
        case '3':
          if(renderer.minFilter == GL2.GL_NEAREST) {
            renderer.minFilter = GL2.GL_LINEAR;
            mode = "minFilter = GL_LINEAR";
          }
          else if(renderer.minFilter == GL2.GL_LINEAR) {
            renderer.minFilter = GL2.GL_NEAREST_MIPMAP_NEAREST;
            mode = "minFilter = GL_NEAREST_MIPMAP_NEAREST";
          }
          else if(renderer.minFilter == GL2.GL_NEAREST_MIPMAP_NEAREST) {
            renderer.minFilter = GL2.GL_LINEAR_MIPMAP_NEAREST;
            mode = "minFilter = GL_LINEAR_MIPMAP_NEAREST";
          }
          else if(renderer.minFilter == GL2.GL_LINEAR_MIPMAP_NEAREST) {
            renderer.minFilter = GL2.GL_NEAREST_MIPMAP_LINEAR;
            mode = "minFilter = GL_NEAREST_MIPMAP_LINEAR";
          }
          else if(renderer.minFilter == GL2.GL_NEAREST_MIPMAP_LINEAR) {
            renderer.minFilter = GL2.GL_LINEAR_MIPMAP_LINEAR;
            mode = "minFilter = GL_LINEAR_MIPMAP_LINEAR";
          }
          else if(renderer.minFilter == GL2.GL_LINEAR_MIPMAP_LINEAR) {
            renderer.minFilter = GL2.GL_NEAREST;
            mode = "minFilter = GL_NEAREST";
          }
          redraw = true;
          break;
        case '4':
          if(renderer.magFilter == GL2.GL_NEAREST) {
            renderer.magFilter = GL2.GL_LINEAR;
            mode = "magFilter = GL_LINEAR";
          }
          else if(renderer.magFilter == GL2.GL_LINEAR) {
            renderer.magFilter = GL2.GL_NEAREST;
            mode = "magFilter = GL_NEAREST";
          }
          redraw = true;
          break;
        case '5':
          if(renderer.selectedTexID == 0) {
            renderer.selectedTexID = 1;
            mode = "texture resolution 32x32 pixels";
          } else {
            renderer.selectedTexID = 0;
            mode = "texture resolution 512x512 pixels";
          }
          redraw = true;
          break;
        }
        if(redraw) { // done automatically
          setTitle(mode);
        }
      }
    });
    
    ani.start();
  }

  @Override
  public void init(GLAutoDrawable d) {
    renderer.init(d);
  }

  @Override
  public void reshape(GLAutoDrawable d, int x, int y, int width, int height) {
    renderer.resize(d, width, height);
  }

  @Override
  public void display(GLAutoDrawable d) {
    float offset = 0.25f;
    renderer.t += offset;
    renderer.display(d);
  }

  @Override
  public void dispose(GLAutoDrawable d) {
  }

}

public class TextureParameter {
  public static void main(String[] args) {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        MyGui myGUI = new MyGui();
        myGUI.createGUI();
      }
    });
  }
}
