package cz.fidentis.analyst.scene;

import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2;
import java.awt.Color;
import javax.vecmath.Vector3d;

/**
 * A drawable object, i.e., an object with drawing state and capable to 
 * render itself into an OpenGL context.
 * 
 * @author Radek Oslejsek
 * @author Richard Pajersky
 */
public abstract class Drawable {
    
    private boolean display = true;
    
    /* material info */
    private Color color = Color.LIGHT_GRAY;
    private float transparency = 1; // 0 = off, 1 = max
    private Color highlights = new Color(0, 0, 0, 1);
    
    /* transformation */
    private Vector3d translation = new Vector3d(0, 0, 0);
    private Vector3d rotation = new Vector3d(0, 0, 0);
    private Vector3d scale = new Vector3d(0, 0, 0);
    
    /* rendering mode */
    /**
     * Render mode to use, one from {@code GL_FILL}, {@code GL_LINE}, {@code GL_POINT}
     */
    private int renderMode = GL2.GL_FILL; // GL_FILL, GL_LINE, or GL_POINT
    
    /**
     * Renders the scene.
     * @param gl OpenGL context
     */
    public void render(GL2 gl) {
        initRendering(gl);
        
        gl.glPushMatrix(); 
        
        // rotate
        gl.glRotated(getRotation().x, 1, 0, 0);
        gl.glRotated(getRotation().y, 0, 1, 0);
        gl.glRotated(getRotation().z, 0, 0, 1);
        // move
        gl.glTranslated(getTranslation().x, getTranslation().y, getTranslation().z);
        // scale
        gl.glScaled(1 + getScale().x, 1 + getScale().y, 1 + getScale().z);
        
        renderObject(gl);

        gl.glPopMatrix();
        
        finishRendering(gl);
    }
    
    protected void initRendering(GL2 gl) {
        gl.glShadeModel(GL2.GL_SMOOTH);
        gl.glPolygonMode(GL.GL_FRONT_AND_BACK, getRenderMode());
        
        // set color
        float[] rgba = {color.getRed() / 255f, color.getGreen() / 255f, 
            color.getBlue() / 255f , getTransparency()};
        gl.glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_DIFFUSE, rgba, 0);

    }
    
    protected abstract void renderObject(GL2 gl);

    protected void finishRendering(GL2 gl) {    
    }
        
    /**
     * This drawable mesh is included in the rendered scene.
     */
    public void show() {
        display = true;
    }
    
    /**
     * This drawable mesh is excluded from the rendered scene (skipped).
     */
    public void hide() {
        display = false;
    }
    
    /**
     * 
     * @return {@code true} if the object is included (rendered) in the scene.
     */
    public boolean isShown() {
        return display;
    }
    
    /**
     * Sets color
     * @param color Color
     */
    public void setColor(Color color) {
        this.color = color;
    }

    /**
     * @return {@link Color}
     */
    public Color getColor() {
        return color;
    }
    
    /**
     * @return Current value of transparency
     */
    public float getTransparency() {
        return transparency;
    }

    /**
     * Sets transparency
     * @param transparency Transparency
     */
    public void setTransparency(float transparency) {
        this.transparency = transparency;
    }
    
    /**
     * @return {@link Color} of highlights
     */
    public Color getHighlights() {
        return highlights;
    }

    /**
     * Sets {@link Color} of highlights
     * @param highlights 
     */
    public void setHighlights(Color highlights) {
        this.highlights = highlights;
    }
    
    /**
     * @return Current translation
     */
    public Vector3d getTranslation() {
        return translation;
    }

    /**
     * Sets tranlation
     * @param translation Translation
     */
    public void setTranslation(Vector3d translation) {
        this.translation = translation;
    }

    /**
     * @return Current rotation
     */
    public Vector3d getRotation() {
        return rotation;
    }

    /**
     * Sets rotation
     * @param rotation 
     */
    public void setRotation(Vector3d rotation) {
        this.rotation = rotation;
    }

    /**
     * @return Current scale
     */
    public Vector3d getScale() {
        return scale;
    }

    /**
     * Sets scale
     * @param scale Scale
     */
    public void setScale(Vector3d scale) {
        this.scale = scale;
    }
    
    /**
     * @return Value of {@link #renderMode}
     */
    public int getRenderMode() {
        return renderMode;
    }

    /**
     * Sets render mode
     * @param renderMode Render mode
     * @throws IllegalArgumentException if renderMode isn't {@code GL_FILL}, {@code GL_LINE} or {@code GL_POINT}
     */
    public void setRenderMode(int renderMode) {
        if (renderMode != GL2.GL_FILL && 
                renderMode != GL2.GL_LINE &&
                renderMode != GL2.GL_POINT) {
            throw new IllegalArgumentException("invalid mode");
        }
        this.renderMode = renderMode;
    }

}
