/*---

    iGeo - http://igeo.jp

    Copyright (c) 2002-2013 Satoru Sugihara

    This file is part of iGeo.

    iGeo is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation, version 3.

    iGeo is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with iGeo.  If not, see <http://www.gnu.org/licenses/>.

---*/

package igeo.p;

import processing.core.*;
import processing.opengl.*;

import javax.media.opengl.*;
import javax.media.opengl.awt.*;
import javax.media.opengl.glu.GLU;

import java.awt.event.*;
import java.awt.geom.*;
import java.awt.*;
import java.util.Enumeration;

//import com.sun.opengl.util.j2d.Overlay;
import com.jogamp.opengl.util.awt.Overlay; // processing 2.0

import igeo.*;
import igeo.gui.*;

/**
   A child class of Processing's PGraphic to draw on Processing using OpenGL.
   This class also manages the IServer to manage all the objects in iGeo.
   
   @author Satoru Sugihara
*/
public class PIGraphicsGL2 extends PGraphics implements GLEventListener{
    
    public IPanel panel;
    public IGraphicsGL igg;
    
    /** To draw Java2D graphic over OpenGL graphic. */
    public Overlay overlay;
    
    /** Background color of overlay should be transparent. */
    static public Color overlayBG = new Color(0,0,0,0);
    
    /** To show iGeo correctly in Processing's basic mode, this needs to be true. */
    public boolean overwritePAppletFinish=true;
    public boolean finished=false;
    
    /** To show iGeo correctly in Processing's basic mode, this needs to be true. */
    public boolean overwritePAppletLoop=true;
    public boolean looping=true;
    
    
    public GLCanvas glCanvas;
    public boolean initialized;


    public GLU glu; // test
    
    public PIGraphicsGL2(){
	super();
	initialized=false;
    }
    
    public void initGraphics(){
	
	glCanvas = new GLCanvas();
	
	glCanvas.setBounds(0,0,this.width,this.height);
	glCanvas.setBackground(new Color(this.backgroundColor, true));
	glCanvas.setFocusable(true);
	this.parent.setLayout(new BorderLayout());
	this.parent.add(glCanvas, BorderLayout.CENTER);
	glCanvas.requestFocusInWindow();
	
	this.parent.removeListeners(this.parent);
	this.parent.addListeners(glCanvas);
	
	glCanvas.addGLEventListener(this);
	
	
	// ig stuff
	
	// initialize root GUI
	panel = new IGridPanel(0,0,parent.getWidth(),parent.getHeight(),2,2);
	panel.setVisible(true); 
	
	panel.setParent(this.parent);
	//panel.setAdapter(this);
	
	// initialize iGeo 
	IG ig = IG.init(panel);
	ig.server().graphicServer().enableGL(); //
	//ig.setBasePath(parent.sketchPath("")); // not sketchPath
	
	ig.setOnline(this.parent.online);
	
	if(!parent.online){ // only when running local
	    ig.setBasePath(parent.dataPath("")); // for default path to read/write files
	}
	
	ig.setInputWrapper(new PIInput(parent));
	
	
	parent.addMouseListener(panel);
	parent.addMouseMotionListener(panel);
	parent.addMouseWheelListener(panel);
	parent.addKeyListener(panel);
	parent.addFocusListener(panel);
	parent.addComponentListener(panel);

	if(parent.frame!=null){
	    parent.frame.addWindowListener(panel);
	}
	
	
	//igg = new IGraphics();
	igg = new IGraphicsGL();
	
	//noSmooth();
	
	
	//if(PIConfig.drawBeforeProcessing){ parent.registerPre(this); }
	//else{ parent.registerDraw(this); }
	//parent.registerPost(this);
	
	
	if(PIConfig.resizable && parent.frame!=null){ // frame seems to be null in exported applet
	    parent.frame.setResizable(true);
	}
	
	
	
	
	initialized=true;
    }

    @Override
    public void init(GLAutoDrawable drawable){
      GL2 gl = drawable.getGL().getGL2();      // get the OpenGL graphics context
      glu = new GLU();                         // get GL Utilities
      gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // set background (clear) color
      gl.glClearDepth(1.0f);      // set clear depth value to farthest
      gl.glEnable(GL2.GL_DEPTH_TEST); // enables depth testing
      gl.glDepthFunc(GL2.GL_LEQUAL);  // the type of depth test to do
      gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL2.GL_NICEST); // best perspective correction
      gl.glShadeModel(GL2.GL_SMOOTH); // blends colors nicely, and smoothes out lighting
    }

    @Override
    public void display(GLAutoDrawable drawable){
	GL2 gl = drawable.getGL().getGL2();  // get the OpenGL 2 graphics context
	gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT); // clear color and depth buffers
	gl.glLoadIdentity();  // reset the model-view matrix
	
	// ----- Your OpenGL rendering code here (Render a white triangle for testing) -----
	gl.glTranslatef(0.0f, 0.0f, -6.0f); // translate into the screen
	gl.glBegin(GL2.GL_TRIANGLES); // draw using triangles
	gl.glVertex3f(0.0f, 1.0f, 0.0f);
	gl.glVertex3f(-1.0f, -1.0f, 0.0f);
	gl.glVertex3f(1.0f, -1.0f, 0.0f);
	gl.glEnd();
    }
    
    @Override
    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height){
	GL2 gl = drawable.getGL().getGL2();  // get the OpenGL 2 graphics context
	
	if (height == 0) height = 1;   // prevent divide by zero
	float aspect = (float)width / height;
	
	// Set the view port (display area) to cover the entire window
	gl.glViewport(0, 0, width, height);
	
	// Setup perspective projection, with aspect ratio matches viewport
	gl.glMatrixMode(GL2.GL_PROJECTION);  // choose projection matrix
	gl.glLoadIdentity();             // reset projection matrix
	glu.gluPerspective(45.0, aspect, 0.1, 100.0); // fovy, aspect, zNear, zFar
	
	// Enable the model-view transform
	gl.glMatrixMode(GL2.GL_MODELVIEW);
	gl.glLoadIdentity(); // reset
    }
    
    @Override
    public void dispose(GLAutoDrawable drawable){
    }
    
    
    
    @Override
    public void requestDraw(){
	if(primarySurface){
	    if(initialized){
		glCanvas.display(); // ok?
	    }
	    else{
		initGraphics();
	    }
	}
    }
    
    
    /**
       setParent is called by Processing in the initialization process of Processing.
       Here the initialization proces of iGeo is also done.
       @param parent parent PApplet of Processing.
    */
    /*
    public void setParent(PApplet parent){
	
	super.setParent(parent);
	
	// initialize root GUI
	panel = new IGridPanel(0,0,parent.getWidth(),parent.getHeight(),2,2);
	panel.setVisible(true); 
	
	panel.setParent(parent);
	//panel.setAdapter(this);
	
	// initialize iGeo 
	IG ig = IG.init(panel);
	ig.server().graphicServer().enableGL(); //
	//ig.setBasePath(parent.sketchPath("")); // not sketchPath
	
	ig.setOnline(parent.online);
	
	if(!parent.online){ // only when running local
	    ig.setBasePath(parent.dataPath("")); // for default path to read/write files
	}
	
	ig.setInputWrapper(new PIInput(parent));
	
	
	parent.addMouseListener(panel);
	parent.addMouseMotionListener(panel);
	parent.addMouseWheelListener(panel);
	parent.addKeyListener(panel);
	parent.addFocusListener(panel);
	parent.addComponentListener(panel);

	if(parent.frame!=null){
	    parent.frame.addWindowListener(panel);
	}
	
	
	//igg = new IGraphics();
	igg = new IGraphicsGL();
	
	//noSmooth();
	
	
	if(PIConfig.drawBeforeProcessing){ parent.registerPre(this); }
	else{ parent.registerDraw(this); }
	parent.registerPost(this);
	
	
	if(PIConfig.resizable && parent.frame!=null){ // frame seems to be null in exported applet
	    parent.frame.setResizable(true);
	}
	
	//super.hints[DISABLE_OPENGL_2X_SMOOTH]=true;  //
	//super.hints[ENABLE_OPENGL_4X_SMOOTH]=true;  //
    }
    
    public void setGLProperties(){
	
	GL2 gl = pgl.gl.getGL2(); // processing 2.0
	
	gl.glEnable(GL2.GL_MULTISAMPLE); //
	gl.glEnable(GL2.GL_POINT_SMOOTH); //
	gl.glEnable(GL2.GL_LINE_SMOOTH); //
	gl.glEnable(GL2.GL_POLYGON_SMOOTH); //
	
	gl.glEnable(GL2.GL_ALPHA_TEST); //
	//gl.glEnable(GL2.GL_BLEND); //
	//gl.glDisable(GL2.GL_BLEND); //
	//gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA); //
	//gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE); //
	
	gl.glHint(GL2.GL_LINE_SMOOTH_HINT, GL2.GL_NICEST); //
	gl.glHint(GL2.GL_POINT_SMOOTH_HINT, GL2.GL_NICEST); //
	gl.glHint(GL2.GL_POLYGON_SMOOTH_HINT, GL2.GL_NICEST); //
	
	//gl.glEnable(GL2.GL_NORMALIZE); //
	//gl.glEnable(GL2.GL_AUTO_NORMAL); //
	//gl.glShadeModel(GL2.GL_SMOOTH); //
	
	//gl.glLightModeli(GL2.GL_LIGHT_MODEL_TWO_SIDE, 1); //
	
	//gl.glEnable(GL2.GL_DEPTH_TEST); // already enabled in super
	//gl.glDisable(GL2.GL_DEPTH_TEST); // ? for transparency
	
	//gl.glEnable(GL2.GL_LIGHTING); // test!
	//gl.glEnable(GL2.GL_LIGHT1); // test!
	
    }
        
    public void pre(){ drawIG(); }
    public void draw(){ drawIG(); }
    */
    
    
    /**
       Drawing all the iGeo objects through IPanel.
       Overlay is also used to draw 2D graphics on top of OpenGL 3D graphics.
    */

    /*
    public synchronized void drawIG(){
	
	GL2 gl = pgl.gl.getGL2(); // processing 2.0
	
	int[] viewport=null;
	if(PIConfig.restoreGLViewport){
	    viewport = new int[4];
	    gl.glGetIntegerv(GL2.GL_VIEWPORT, viewport, 0);
	}
	
	gl.glMatrixMode(GL2.GL_MODELVIEW);
        gl.glPushMatrix();
	
        gl.glMatrixMode(GL2.GL_PROJECTION);
        gl.glPushMatrix();
	
	if(PIConfig.resetGLDepthBefore) gl.glClear(GL2.GL_DEPTH_BUFFER_BIT);
	
	//gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
	
	//gl.glClear(GL2.GL_COLOR_BUFFER_BIT);
	
	setGLProperties();
	
	if(igg.getGraphics()==null){
	    setOverlay();
	}
	
	//igg.setGraphics(overlay.createGraphics());
	if(igg.getGraphics()!=null){ // temporarily out for processing 2.0
	    igg.getGraphics().clearRect(0,0,parent.getWidth(),parent.getHeight()); //
	}
	
	//overlay = new Overlay(drawable); //
	//Graphics2D g = overlay.createGraphics();
	//igg.setGraphics(g);
	
	igg.setGL(gl);
		
	panel.draw(igg);
	
	if(PIConfig.resetGLDepthAfter) gl.glClear(GL2.GL_DEPTH_BUFFER_BIT);
	
	gl.glMatrixMode(GL2.GL_PROJECTION);
        gl.glPopMatrix();
	
	gl.glMatrixMode(GL2.GL_MODELVIEW);
        gl.glPopMatrix();
	
	
	// bring the original viewport back
	if(PIConfig.restoreGLViewport && viewport!=null){
	    gl.glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
	}
	
	if(overlay!=null){
	    overlay.markDirty(0,0,parent.getWidth(),parent.getHeight());
	    overlay.drawAll();
	}
	
	//g.dispose();
	//igg.getGraphics().dispose();
    }
    */
    
    public void setOverlay(){
	/*
	if(pgl.canvas instanceof GLDrawable){
	    //overlay = new Overlay(drawable); // processing 1.5
	    overlay = new Overlay((GLDrawable)pgl.canvas); // processing 2.0
	    igg.setGraphics(overlay.createGraphics());
	    igg.getGraphics().setBackground(overlayBG);
	}
	*/
    }
    
    public void setSize(int w, int h){
	
	IG.err("setSize : "+w+"x"+h); //
	
	super.setSize(w,h);
	setOverlay(); // update overlay

	//panel.setSize(w,h); //
    }
    
    /*
    public void post(){
	if(overwritePAppletFinish) parent.finished=finished;
	if(overwritePAppletLoop) if(looping) parent.loop(); else parent.noLoop();
    }
    
    public void loop(){ if(!looping) looping=true; }
    public void noLoop(){ if(looping) looping=false; }
    
    public void start(){ if(finished) finished=false; }
    public void stop(){ if(!finished) finished=true; }
    */
    
    /*
    public void mousePressed(MouseEvent e){
    }
    public void mouseReleased(MouseEvent e){
    }
    public void mouseClicked(MouseEvent e){
    }
    public void mouseEntered(MouseEvent e){
    }
    public void mouseExited(MouseEvent e){
    }
    public void mouseMoved(MouseEvent e){
    }
    public void mouseDragged(MouseEvent e){
    }
    public void mouseWheelMoved(MouseWheelEvent e){
    }
    public void keyPressed(KeyEvent e){
    }
    public void keyReleased(KeyEvent e){
    }
    public void keyTyped(KeyEvent e){
    }
    public void focusLost(FocusEvent e){
    }
    public void focusGained(FocusEvent e){
    }
    */
    /*
    public void componentHidden(ComponentEvent e){
    }
    public void componentMoved(ComponentEvent e){
    }
    public synchronized void componentResized(ComponentEvent e){
	int w = e.getComponent().getBounds().width;
	int h = e.getComponent().getBounds().height;
	setSize(w,h);
    }
    public void componentShown(ComponentEvent e){
    }
    */
    
    /*
    public void close(){
	Enumeration applets = parent.getAppletContext().getApplets();
	while(applets.hasMoreElements() && !anyother){
	    if(applets.nextElement() != parent){
	    }
	}
    }
    */
}

