/**
 * This package provides classes to facilitate the handling of opengl textures, glsl shaders and 
 * off-screen rendering in Processing.
 * @author Andres Colubri
 * @version 0.8.9.4
 *
 * Copyright (c) 2008 Andres Colubri
 *
 * This source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This code 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
 * General Public License for more details.
 * 
 * A copy of the GNU General Public License is available on the World
 * Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also
 * obtain it by writing to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package codeanticode.glgraphics; 
 
import processing.core.*;
import javax.media.opengl.*;
import javax.media.opengl.glu.*;
import java.util.*;

/**
 * @invisible
 * This class that offers some utility methods to set and restore opengl state.
 */
public class GLState
{
    public GLState(GL gl)
    {
        this.gl = gl;

        if (INSTANCES_COUNT == 0) 
        {
            String glVersion = gl.glGetString(GL.GL_VERSION);
            int glMajor = Character.getNumericValue(glVersion.charAt(0));
            int glMinor = Character.getNumericValue(glVersion.charAt(2));
            String glslVersion = gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION_ARB);
            
            PApplet.println("OpenGL version: " + glMajor + "." + glMinor + " (" + glVersion + ")");
            if (glslVersion != null) PApplet.println("GLSL version: " + glslVersion);
            else PApplet.println("Unknown GLSL version.");
        	
            glu = new GLU();
            gl.glGenFramebuffersEXT(1, FBO, 0);

            fboStack = new Stack<Integer>();
            currentFBO = 0;
        }
        INSTANCES_COUNT++;
    }
  
    public void setOrthographicView(int w, int h)
    {
        gl.glViewport(0, 0, w, h);

        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();

        //gl.glOrtho(0.0, w, 0.0, h, -100.0, +100.0);
	glu.gluOrtho2D(0.0, w, 0.0, h);

        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();
    }
    
    public void saveView()
    {
        gl.glPushAttrib(GL.GL_VIEWPORT_BIT);
        saveGLMatrices();
    }

    public void restoreView()
    {
        restoreGLMatrices();
        gl.glPopAttrib();
    }
    
    public void saveGLState()
    {
        gl.glPushAttrib(GL.GL_ALL_ATTRIB_BITS);
        saveGLMatrices();
    }      
   
    public void restoreGLState()
    {
        restoreGLMatrices();
        gl.glPopAttrib();
    }
    
    public void saveGLMatrices()
    {
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glPushMatrix();
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glPushMatrix();
        //gl.glMatrixMode(GL.GL_TEXTURE);
        //gl.glPushMatrix();
    }

    public void restoreGLMatrices()
    {
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glPopMatrix();
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glPopMatrix();
        //gl.glMatrixMode(GL.GL_TEXTURE);
        //gl.glPopMatrix();
    }

    public void clearTex(int glid, int target, int color)
    {
        float r, g, b, a;
        int ir, ig, ib, ia;

        ia = (color >> 24) & 0xff;
        ir = (color >> 16) & 0xff;
        ig = (color >> 8) & 0xff;
        ib = color & 0xff; 

        a = ia / 255.0f;
        r = ir / 255.0f;
        g = ig / 255.0f;
        b = ib / 255.0f;

        pushFramebuffer();
        setFramebuffer(FBO[0]);
        gl.glFramebufferTexture2DEXT(GL.GL_FRAMEBUFFER_EXT, GL.GL_COLOR_ATTACHMENT0_EXT, target, glid, 0);
        gl.glDrawBuffer(GL.GL_COLOR_ATTACHMENT0_EXT);

        gl.glClearColor(r, g, b, a);
        gl.glClear(GL.GL_COLOR_BUFFER_BIT);

        popFramebuffer();
    }

	public void paintTex(int glid, int target, int w, int h, int color)
    {
        float r, g, b, a;
        int ir, ig, ib, ia;

        ia = (color >> 24) & 0xff;
        ir = (color >> 16) & 0xff;
        ig = (color >> 8) & 0xff;
        ib = color & 0xff;

        a = ia / 255.0f;
        r = ir / 255.0f;
        g = ig / 255.0f;
        b = ib / 255.0f;

        pushFramebuffer();
        setFramebuffer(FBO[0]);
        gl.glFramebufferTexture2DEXT(GL.GL_FRAMEBUFFER_EXT, GL.GL_COLOR_ATTACHMENT0_EXT, target, glid, 0);
        gl.glDrawBuffer(GL.GL_COLOR_ATTACHMENT0_EXT);

        saveView();
        setOrthographicView(w, h);
        gl.glColor4f(r, g, b, a);
        gl.glBegin(GL.GL_QUADS);
            gl.glVertex2f(0.0f, 0.0f);
            gl.glVertex2f(w, 0.0f);
            gl.glVertex2f(w, h);
            gl.glVertex2f(0.0f, h);
        gl.glEnd();
        restoreView();

        popFramebuffer();
    }

    protected void pushFramebuffer()
    {
        fboStack.push(new Integer(currentFBO));
    }

    protected void setFramebuffer(int fbo)
    {
        currentFBO = fbo;
        gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, currentFBO);
    }

    protected void popFramebuffer()
    {
        try {
            Integer n = fboStack.pop();
            currentFBO = n.intValue();
            gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, currentFBO);
        } catch (EmptyStackException e) {
            System.out.println("Empty framebuffer stack");
        }
    }

    public static GLU glu;
    public GL gl;
    protected static long INSTANCES_COUNT = 0;
    protected static int[] FBO = { 0 };

    protected static int currentFBO;
    protected static Stack<Integer> fboStack;
}
