var flatSurface = $('#surface-shader');

if (flatSurface.length) {
    var MESH = {
        width: 1.2,
        height: 1.2,
        depth: 0,
        segments: 16,
        slices: 8,
        xRange: 0.8,
        yRange: 0.1,
        zRange: 1.0,
        ambient: '#555555',
        diffuse: '#ffffff',
        speed: 0.002
    };

    var LIGHT = {
        count: 2,
        xyScalar: 1,
        zOffset: 100,
        ambient: flatSurface.data('ambient-color'),
        diffuse: flatSurface.data('diffuse-color'),
        speed: 0.002,
        gravity: 500,
        dampening: 0.95,
        minLimit: 10,
        maxLimit: null,
        minDistance: 20,
        maxDistance: 800,
        autopilot: true,
        draw: false,
        bounds: FSS.Vector3.create(),
        step: FSS.Vector3.create(
            Math.randomInRange(0.2, 1.0),
            Math.randomInRange(0.2, 1.0),
            Math.randomInRange(0.2, 1.0)
        )
    };



    var RENDER = {
        renderer: 'canvas'
    };

    var now, start = Date.now();
    var center = FSS.Vector3.create();
    var attractor = FSS.Vector3.create();
    var containerFSS = flatSurface[0];
    var output = flatSurface[0];
    var rendererFSS, sceneFSS, mesh, geometry, material;
    var canvasRenderer;

}


function initialise() {
    createRenderer();
    createScene();
    createMesh();
    createLights();
    addEventListeners();
    resize(containerFSS.offsetWidth, containerFSS.offsetHeight);
    animateFSS();
}

function createRenderer() {
    canvasRenderer = new FSS.CanvasRenderer();
    setRenderer(RENDER.renderer);
}

function setRenderer() {
    if (rendererFSS) {
        output.removeChild(rendererFSS.element);
    }

    rendererFSS = canvasRenderer;

    rendererFSS.setSize(containerFSS.offsetWidth, containerFSS.offsetHeight);
    output.appendChild(rendererFSS.element);
}

function createScene() {
    sceneFSS = new FSS.Scene();
}

function createMesh() {
    sceneFSS.remove(mesh);
    rendererFSS.clear();
    geometry = new FSS.Plane(MESH.width * rendererFSS.width, MESH.height * rendererFSS.height, MESH.segments, MESH.slices);
    material = new FSS.Material(MESH.ambient, MESH.diffuse);
    mesh = new FSS.Mesh(geometry, material);
    sceneFSS.add(mesh);

    // Augment vertices for animation
    var v, vertex;
    for (v = geometry.vertices.length - 1; v >= 0; v--) {
        vertex = geometry.vertices[v];
        vertex.anchor = FSS.Vector3.clone(vertex.position);
        vertex.step = FSS.Vector3.create(
            Math.randomInRange(0.2, 1.0),
            Math.randomInRange(0.2, 1.0),
            Math.randomInRange(0.2, 1.0)
        );
        vertex.time = Math.randomInRange(0, Math.PIM2);
    }
}

function createLights() {
    var l, light;
    for (l = sceneFSS.lights.length - 1; l >= 0; l--) {
        light = sceneFSS.lights[l];
        sceneFSS.remove(light);
    }
    rendererFSS.clear();
    for (l = 0; l < LIGHT.count; l++) {
        light = new FSS.Light(LIGHT.ambient, LIGHT.diffuse);
        light.ambientHex = light.ambient.format();
        light.diffuseHex = light.diffuse.format();
        sceneFSS.add(light);

        // Augment light for animation
        light.mass = Math.randomInRange(0.5, 1);
        light.velocity = FSS.Vector3.create();
        light.acceleration = FSS.Vector3.create();
        light.force = FSS.Vector3.create();

        // Ring SVG Circle
        light.ring = document.createElementNS(FSS.SVGNS, 'circle');
        light.ring.setAttributeNS(null, 'stroke', light.ambientHex);
        light.ring.setAttributeNS(null, 'stroke-width', '0.5');
        light.ring.setAttributeNS(null, 'fill', 'none');
        light.ring.setAttributeNS(null, 'r', '10');

        // Core SVG Circle
        light.core = document.createElementNS(FSS.SVGNS, 'circle');
        light.core.setAttributeNS(null, 'fill', light.diffuseHex);
        light.core.setAttributeNS(null, 'r', '4');
    }
}

function resize(width, height) {
    rendererFSS.setSize(width, height);
    FSS.Vector3.set(center, rendererFSS.halfWidth, rendererFSS.halfHeight);
    createMesh();
}

function animateFSS() {
    now = Date.now() - start;
    update();
    renderFSS();
    requestAnimationFrame(animateFSS);
}

function update() {
    var ox, oy, oz, l, light, v, vertex, offset = MESH.depth / 2;

    // Update Bounds
    FSS.Vector3.copy(LIGHT.bounds, center);
    FSS.Vector3.multiplyScalar(LIGHT.bounds, LIGHT.xyScalar);

    // Update Attractor
    FSS.Vector3.setZ(attractor, LIGHT.zOffset);

    // Overwrite the Attractor position
    if (LIGHT.autopilot) {
        ox = Math.sin(LIGHT.step[0] * now * LIGHT.speed);
        oy = Math.cos(LIGHT.step[1] * now * LIGHT.speed);
        FSS.Vector3.set(attractor,
            LIGHT.bounds[0] * ox,
            LIGHT.bounds[1] * oy,
            LIGHT.zOffset);
    }

    // Animate Lights
    for (l = sceneFSS.lights.length - 1; l >= 0; l--) {
        light = sceneFSS.lights[l];

        // Reset the z position of the light
        FSS.Vector3.setZ(light.position, LIGHT.zOffset);

        // Calculate the force Luke!
        var D = Math.clamp(FSS.Vector3.distanceSquared(light.position, attractor), LIGHT.minDistance, LIGHT.maxDistance);
        var F = LIGHT.gravity * light.mass / D;
        FSS.Vector3.subtractVectors(light.force, attractor, light.position);
        FSS.Vector3.normalise(light.force);
        FSS.Vector3.multiplyScalar(light.force, F);

        // Update the light position
        FSS.Vector3.set(light.acceleration);
        FSS.Vector3.add(light.acceleration, light.force);
        FSS.Vector3.add(light.velocity, light.acceleration);
        FSS.Vector3.multiplyScalar(light.velocity, LIGHT.dampening);
        FSS.Vector3.limit(light.velocity, LIGHT.minLimit, LIGHT.maxLimit);
        FSS.Vector3.add(light.position, light.velocity);
    }

    // Animate Vertices
    for (v = geometry.vertices.length - 1; v >= 0; v--) {
        vertex = geometry.vertices[v];
        ox = Math.sin(vertex.time + vertex.step[0] * now * MESH.speed);
        oy = Math.cos(vertex.time + vertex.step[1] * now * MESH.speed);
        oz = Math.sin(vertex.time + vertex.step[2] * now * MESH.speed);
        FSS.Vector3.set(vertex.position,
            MESH.xRange * geometry.segmentWidth * ox,
            MESH.yRange * geometry.sliceHeight * oy,
            MESH.zRange * offset * oz - offset);
        FSS.Vector3.add(vertex.position, vertex.anchor);
    }

    // Set the Geometry to dirty
    geometry.dirty = true;
}

function renderFSS() {
    rendererFSS.render(sceneFSS);

    // Draw Lights
    if (LIGHT.draw) {
        var l, lx, ly, light;
        for (l = sceneFSS.lights.length - 1; l >= 0; l--) {
            light = sceneFSS.lights[l];
            lx = light.position[0];
            ly = light.position[1];
            rendererFSS.context.lineWidth = 0.5;
            rendererFSS.context.beginPath();
            rendererFSS.context.arc(lx, ly, 10, 0, Math.PIM2);
            rendererFSS.context.strokeStyle = light.ambientHex;
            rendererFSS.context.stroke();
            rendererFSS.context.beginPath();
            rendererFSS.context.arc(lx, ly, 4, 0, Math.PIM2);
            rendererFSS.context.fillStyle = light.diffuseHex;
            rendererFSS.context.fill();

        }
    }
}


function addEventListeners() {
    window.addEventListener('resize', onWindowResizFSS);
}

// Callbacks

function onWindowResizFSS() {
    resize(containerFSS.offsetWidth, containerFSS.offsetHeight);
    renderFSS();
}


// Init
if (flatSurface.length) {
    initialise();
}
