/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package walltest;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.light.PointLight;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.BloomFilter;
import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Sphere;

/**
 *
 * @author rolf
 */
public class TestLight extends SimpleApplication {

    Material mat, mat1;
    Node lightCubeNode;
    Sphere s = new Sphere(8, 8, 0.1f);
    LightBulb[][][] theCube;
    float time;
    int state;
    private BulletAppState bulletAppState;

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

    @Override
    public void simpleInitApp() {
        //
        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);

        lightCubeNode = new Node();
        rootNode.attachChild(lightCubeNode);
        initMaterial();
        initLights(8);

        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
        BloomFilter bloom = new BloomFilter(BloomFilter.GlowMode.Objects);
        bloom.setBlurScale(4.0f);
        bloom.setBloomIntensity(4.0f);
        fpp.addFilter(bloom);
        viewPort.addProcessor(fpp);

    }

    private void initMaterial() {
        mat = new Material(assetManager, // Create new material and...
                "Common/MatDefs/Light/Lighting.j3md"); // ... specify .j3md file to use (illuminated).
        mat.setBoolean("UseMaterialColors", true);  // Set some parameters, e.g. blue.
        mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
        mat.setColor("Ambient", new ColorRGBA(1.0f, 1.0f, 1.0f, 0.1f));
        mat.setColor("Diffuse", new ColorRGBA(1.0f, 1.0f, 1.0f, 0.8f));
        mat.setColor("GlowColor", new ColorRGBA(0.7f, 0.4f, 0.2f, 1.0f));

        mat1 = new Material(assetManager, // Create new material and...
                "Common/MatDefs/Light/Lighting.j3md"); // ... specify .j3md file to use (illuminated).
        mat1.setBoolean("UseMaterialColors", true);  // Set some parameters, e.g. blue.
        mat1.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
        mat1.setColor("Ambient", new ColorRGBA(1.0f, 1.0f, 1.0f, 0.1f));
        mat1.setColor("Diffuse", new ColorRGBA(1.0f, 1.0f, 1.0f, 0.8f));
        mat1.setColor("GlowColor", new ColorRGBA(0.9f, 0.7f, 0.5f, 1.0f));



    }

    private void dropSphere() {
        Sphere s1 = new Sphere(8, 8, 0.255f);
        Geometry ball = new Geometry("ball", s1);
        ball.setMaterial(mat1);
        rootNode.attachChild(ball);
        float mass = 1.0f;
        RigidBodyControl ball_phy = new RigidBodyControl(mass);
        ball.addControl(ball_phy);
        ball_phy.setFriction(0.2f);
        ball_phy.setPhysicsLocation(new Vector3f(0f, 5f, 0f));
        bulletAppState.getPhysicsSpace().add(ball_phy);
    }

    private void initLights(int size) {
        theCube = new LightBulb[size][size][size];
        for (int x = 0; x < size; x++) {
            for (int y = 0; y < size; y++) {
                for (int z = 0; z < size; z++) {
                    Vector3f pos = new Vector3f(x / 2.0f, y / 2.0f, z / 2.0f);
                    theCube[x][y][z] = new LightBulb(lightCubeNode, pos);
                }
            }
        }
        lightCubeNode.setLocalTranslation(-size / 4.0f, -size / 4.0f, -size / 4.0f);
    }

    @Override
    public void simpleUpdate(float tpf) {
        time += tpf;

        if (time > 4.0) {
            time = 0;
            state = (state + 1) % 4;
            dropSphere();
        }

        switch (state) {
            case 0:
                allLights(time % 0.7f, time % 0.6f, time % 1f);
                break;
            case 1:
                randomN(theCube.length * theCube.length);
                break;
            case 2:
                shiftX();
                break;
            case 3:
                sineX(time);
                break;

        }
        rootNode.rotate(tpf / 10.0f, tpf / 5f, 0);
    }

    // -------------------------------------------------------------------------
    private void allLights(float r, float g, float b) {
        Vector3f col = new Vector3f(r, g, b);
        int size = theCube.length;
        for (int x = 0; x < size; x++) {
            for (int y = 0; y < size; y++) {
                for (int z = 0; z < size; z++) {
                    theCube[x][y][z].setColor(col);
                }
            }
        }
    }

    // -------------------------------------------------------------------------
    private void shiftX() {
        int size = theCube.length;
        for (int x = size - 1; x >= 0; x--) {
            for (int y = 0; y < size; y++) {
                for (int z = 0; z < size; z++) {
                    Vector3f col = theCube[x][y][z].color;
                    theCube[(x + 1) % size][y][z].setColor(col);
                }
            }
        }
    }

    // -------------------------------------------------------------------------
    private void randomN(int n) {
        allLights(0.1f, 0.1f, 0.1f);
        int size = theCube.length;
        for (int i = 0; i < n; i++) {
            int x = (int) (Math.random() * size);
            int y = (int) (Math.random() * size);
            int z = (int) (Math.random() * size);
            float r = (float) Math.random();
            float g = (float) Math.random();
            float b = (float) Math.random();
            theCube[x][y][z].setColor(new Vector3f(r, g, b));
        }
    }

    // -------------------------------------------------------------------------
    private void sineX(float t) {
        int size = theCube.length;
        // shift
        for (int x = size - 2; x >= 0; x--) {
            for (int y = 0; y < size; y++) {
                for (int z = 0; z < size; z++) {
                    Vector3f col = theCube[x][y][z].color;
                    theCube[(x + 1)][y][z].setColor(col);
                }
            }
        }
        // wave
        float r = (float) Math.random();
        float g = (float) Math.random();
        float b = (float) Math.random();
        Vector3f c = new Vector3f(r, g, b);
        int row = (int) (Math.round(Math.sin(t / 0.1) * (size - 1) / 2.0 + (size - 1) / 2.0));
        for (int y = 0; y < size; y++) {
            for (int z = 0; z < size; z++) {
                if (y == row) {
                    theCube[0][y][z].setColor(c);
                } else {
                    theCube[0][y][z].setColor(new Vector3f(0.1f, 0.1f, 0.1f));
                }
            }
        }
    }

    // -------------------------------------------------------------------------
    class LightBulb {

        AmbientLight l;
        DirectionalLight d;
        Vector3f color;

        public LightBulb(Node parent, Vector3f pos) {
            Node n = new Node();

            Geometry g = new Geometry("lightbulb", s);
            g.setMaterial(mat);
            g.setQueueBucket(Bucket.Transparent);
            g.setLocalTranslation(pos);
            l = new AmbientLight();
            d = new DirectionalLight();
            d.setDirection(new Vector3f(0, -1f, 0));
            n.addLight(d);
            n.addLight(l);
            n.attachChild(g);
            parent.attachChild(n);
            setColor(new Vector3f(1, 1, 1));

            /* Make the lightbulb physical with mass 0.0f! */
            RigidBodyControl phy = new RigidBodyControl(0.0f);
            g.addControl(phy);
            phy.setKinematic(true);
            bulletAppState.getPhysicsSpace().add(phy);
        }

        void setColor(Vector3f col) {
            color = col;
            d.setColor(new ColorRGBA(col.x, col.y, col.z, 1.0f));
            l.setColor(new ColorRGBA(col.x / 2, col.y / 2, col.z / 2, 1.0f));
        }
    }
}
