/*
 * Copyright (c) 2009-2012 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * 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.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT OWNER 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.
 */

package jme3test.post;

import com.jme3.app.SimpleApplication;
import com.jme3.light.PointLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Matrix4f;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.post.SceneProcessor;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Node;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture2D;
import com.jme3.ui.Picture;

public class TestMultiRenderTarget extends SimpleApplication implements SceneProcessor {

    private FrameBuffer fb;
    private Texture2D diffuseData, normalData, specularData, depthData;
    private Picture display1, display2, display3, display4;
    
    private Picture display;
    private Material mat;

    private String techOrig;
    private PointLight[] pls;

    public static void main(String[] args){
        TestMultiRenderTarget app = new TestMultiRenderTarget();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        viewPort.addProcessor(this);

//        flyCam.setEnabled(false);
        cam.setLocation(new Vector3f(4.8037705f, 4.851632f, 10.789033f));
        cam.setRotation(new Quaternion(-0.05143692f, 0.9483723f, -0.21131563f, -0.230846f));

        Node tank = (Node) assetManager.loadModel("Models/HoverTank/Tank2.mesh.xml");
        
        //tankMesh.getMaterial().setColor("Specular", ColorRGBA.Black);
        rootNode.attachChild(tank);

        display1 = new Picture("Picture");
        display1.move(0, 0, -1); // make it appear behind stats view
        display2 = (Picture) display1.clone();
        display3 = (Picture) display1.clone();
        display4 = (Picture) display1.clone();
        display  = (Picture) display1.clone();

        ColorRGBA[] colors = new ColorRGBA[]{
            ColorRGBA.White,
            ColorRGBA.Blue,
            ColorRGBA.Cyan,
            ColorRGBA.DarkGray,
            ColorRGBA.Green,
            ColorRGBA.Magenta,
            ColorRGBA.Orange,
            ColorRGBA.Pink,
            ColorRGBA.Red,
            ColorRGBA.Yellow
        };

        pls = new PointLight[3];
        for (int i = 0; i < pls.length; i++){
            PointLight pl = new PointLight();
            pl.setColor(colors[i % colors.length]);
            pl.setRadius(5);
            display.addLight(pl);
            pls[i] = pl;
        }
    }

    @Override
    public void simpleUpdate(float tpf) {
        super.simpleUpdate(tpf);//To change body of generated methods, choose Tools | Templates.
        for (int i = 0; i < 3; i++){
            PointLight pl = pls[i];
            float angle = (float)Math.PI * (i + (timer.getTimeInSeconds() % 6)/3); // 3s for full loop
            pl.setPosition( new Vector3f(FastMath.cos(angle)*3f, 0,
                                         FastMath.sin(angle)*3f));
        }
    }
    public void initialize(RenderManager rm, ViewPort vp) {
        reshape(vp, vp.getCamera().getWidth(), vp.getCamera().getHeight());
        viewPort.setOutputFrameBuffer(fb);
        guiViewPort.setClearFlags(true, true, true);
        guiNode.attachChild(display);
//        guiNode.attachChild(display1);
        guiNode.attachChild(display2);
//        guiNode.attachChild(display3);
//        guiNode.attachChild(display4);
        guiNode.updateGeometricState();
    }

    public void reshape(ViewPort vp, int w, int h) {
        diffuseData  = new Texture2D(w, h, Format.RGBA8);
        normalData   = new Texture2D(w, h, Format.RGBA8);
        specularData = new Texture2D(w, h, Format.RGBA8);
        depthData    = new Texture2D(w, h, Format.Depth);

        mat = new Material(assetManager, "Common/MatDefs/Light/Deferred.j3md");
        mat.setTexture("DiffuseData",  diffuseData);
        mat.setTexture("SpecularData", specularData);
        mat.setTexture("NormalData",   normalData);
        mat.setTexture("DepthData",    depthData);

        display.setMaterial(mat);
        display.setPosition(0, 0);
        display.setWidth(w);
        display.setHeight(h);

        display1.setTexture(assetManager, diffuseData, false);
        display2.setTexture(assetManager, normalData, false);
        display3.setTexture(assetManager, specularData, false);
        display4.setTexture(assetManager, depthData, false);

        display1.setPosition(0, 0);
        display2.setPosition(w/2, 0);
        display3.setPosition(0, h/2);
        display4.setPosition(w/2, h/2);

        display1.setWidth(w/2);
        display1.setHeight(h/2);

        display2.setWidth(w/2);
        display2.setHeight(h/2);

        display3.setWidth(w/2);
        display3.setHeight(h/2);

        display4.setWidth(w/2);
        display4.setHeight(h/2);

        guiNode.updateGeometricState();
        
        fb = new FrameBuffer(w, h, 1);
        fb.setDepthTexture(depthData);
        fb.addColorTexture(diffuseData);
        fb.addColorTexture(normalData);
        fb.addColorTexture(specularData);
        fb.setMultiTarget(true);

        /*
         * Marks pixels in front of the far light boundary
            Render back-faces of light volume
            Depth test GREATER-EQUAL
            Write to stencil on depth pass
            Skipped for very small distant lights
         */
        
        /*
         * Find amount of lit pixels inside the volume
             Start pixel query
             Render front faces of light volume
             Depth test LESS-EQUAL
             Don’t write anything – only EQUAL stencil test
         */

        /*
         * Enable conditional rendering
            Based on query results from previous stage
            GPU skips rendering for invisible lights
         */

        /*
         * Render front-faces of light volume
            Depth test - LESS-EQUAL
            Stencil test - EQUAL
            Runs only on marked pixels inside light
         */
    }

    public boolean isInitialized() {
        return diffuseData != null;
    }

    public void preFrame(float tpf) {
        Matrix4f inverseViewProj = cam.getViewProjectionMatrix().invert();
        mat.setMatrix4("ViewProjectionMatrixInverse", inverseViewProj);
        techOrig = renderManager.getForcedTechnique();
        renderManager.setForcedTechnique("GBuf");
    }

    public void postQueue(RenderQueue rq) {
    }

    public void postFrame(FrameBuffer out) {
        renderManager.setForcedTechnique(techOrig);
    }

    public void cleanup() {
    }

}
