/* 
 * Gray-Scott
 *
 * A solver of the Gray-Scott model of reaction diffusion.
 *
 * ©2012 pmneila.
 * p.mneila at upm.es
 */

(function(){

// Canvas.
var canvas;
var canvasQ;
var canvasWidth;
var canvasHeight;

var mMouseX, mMouseY;
var mMouseDown = false;

var mRenderer;
var mScene;
var mCamera;
var mUniforms;
var mColors;
var mColorsNeedUpdate = true;
var mLastTime = 0;

var mTexture1, mTexture2;
var mGSMaterial, mScreenMaterial;
var mScreenQuad;

var mToggled = false;

var mMinusOnes = new THREE.Vector2(-1, -1);

// Some presets.
var presets = [
    {
        feed: 0.031,
        kill: 0.057
    },
    {
        feed: 0.031,
        kill: 0.058
    },
    {
        feed: 0.031,
        kill: 0.060
    },
    { 
        feed: 0.033,
        kill: 0.058
    },
    { 
        feed: 0.033,
        kill: 0.059
    },
    { 
        feed: 0.033,
        kill: 0.061
    },
    {
        feed: 0.035,
        kill: 0.059
    },
    {
        feed: 0.035,
        kill: 0.060
    },
    {
        feed: 0.035,
        kill: 0.061
    }
];

// Configuration.
var feed = presets[0].feed;
var kill = presets[0].kill;

var noAnimation = false;

skipWebGL = function(){

    var mobile = /Android|Mobile|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
    var noWebgl = false;
    var canvas;
    var ctx;

    try {
      canvas = document.createElement('canvas');
      ctx = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
    }
    catch (e) {
      console && "log" in console && console.log("No webgl");
    }

    if (ctx == undefined) {
      noWebgl = true;
    }
  
    if (noWebgl || mobile) {
      return true;
    }

    return false
}
init = function()
{
    
	  if( skipWebGL() ){
      	$("canvas").hide();
      	$("#replacementImage").show()
      	noAnimation = true
      	return
    }

    canvasQ = $('#myCanvas');
    canvas = canvasQ.get(0);

    init_controls();
    canvas.onmousedown = onMouseDown;
    canvas.onmouseup = onMouseUp;
    canvas.onmousemove = onMouseMove;
    
    mRenderer = new THREE.WebGLRenderer({canvas: canvas, preserveDrawingBuffer: true});

    mScene = new THREE.Scene();
    mCamera = new THREE.OrthographicCamera(-0.5, 0.5, 0.5, -0.5, 0, 100);
    mCamera.position.z = 100;
    mCamera.scale.x = mCamera.scale.y = 0.99985 ;
    mScene.add(mCamera);

    // For debugging purpose
    window.mCamera = mCamera;
    
    mUniforms = {
        screenWidth: {type: "f", value: undefined},
        screenHeight: {type: "f", value: undefined},
        tSource: {type: "t", value: undefined},
        delta: {type: "f", value: 0.0},
        feed: {type: "f", value: feed},
        kill: {type: "f", value: kill},
        brush: {type: "v2", value: new THREE.Vector2(-10, -10)},
        color1: {type: "v4", value: new THREE.Vector4(0, 0, 0.0, 0)},
        color2: {type: "v4", value: new THREE.Vector4(0, 1, 0, 0.2)},
        color3: {type: "v4", value: new THREE.Vector4(1, 1, 0, 0.21)},
        color4: {type: "v4", value: new THREE.Vector4(1, 0, 0, 0.4)},
        color5: {type: "v4", value: new THREE.Vector4(1, 1, 1, 0.6)}
    };
    mColors = [mUniforms.color1, mUniforms.color2, mUniforms.color3, mUniforms.color4, mUniforms.color5];
    $("#gradient").gradient("setUpdateCallback", onUpdatedColor);
    
    mGSMaterial = new THREE.ShaderMaterial({
            uniforms: mUniforms,
            vertexShader: document.getElementById('standardVertexShader').textContent,
            fragmentShader: document.getElementById('gsFragmentShader').textContent,
        });
    mScreenMaterial = new THREE.ShaderMaterial({
                uniforms: mUniforms,
                vertexShader: document.getElementById('standardVertexShader').textContent,
                fragmentShader: document.getElementById('screenFragmentShader').textContent,
            });
    
    var plane = new THREE.PlaneGeometry(1.0, 1.0);
    mScreenQuad = new THREE.Mesh(plane, mScreenMaterial);
    mScene.add(mScreenQuad);
    
    mColorsNeedUpdate = true;
    
    resize(canvas.clientWidth, canvas.clientHeight);
    
    render(0);
    mUniforms.brush.value = new THREE.Vector2(0.5, 0.5);
    mLastTime = new Date().getTime();
    requestAnimationFrame(render);
}

var resize = function(width, height)
{
    // Set the new shape of canvas.
    canvasQ.width(width);
    canvasQ.height(height);
    
    // Get the real size of canvas.
    canvasWidth = canvasQ.width();
    canvasHeight = canvasQ.height();
    
    mRenderer.setSize(canvasWidth, canvasHeight);
    
    // TODO: Possible memory leak?
    mTexture1 = new THREE.WebGLRenderTarget(canvasWidth/2, canvasHeight/2,
                        {minFilter: THREE.LinearFilter,
                         magFilter: THREE.LinearFilter,
                         format: THREE.RGBAFormat,
                         type: THREE.FloatType});
    mTexture2 = new THREE.WebGLRenderTarget(canvasWidth/2, canvasHeight/2,
                        {minFilter: THREE.LinearFilter,
                         magFilter: THREE.LinearFilter,
                         format: THREE.RGBAFormat,
                         type: THREE.FloatType});
    mTexture1.wrapS = THREE.RepeatWrapping;
    mTexture1.wrapT = THREE.RepeatWrapping;
    mTexture2.wrapS = THREE.RepeatWrapping;
    mTexture2.wrapT = THREE.RepeatWrapping;
    
    mUniforms.screenWidth.value = canvasWidth/2;
    mUniforms.screenHeight.value = canvasHeight/2;
}

var render = function(time)
{
    var dt = (time - mLastTime)/20.0;
    if(dt > 0.8 || dt<=0)
        dt = 0.8;
    mLastTime = time;
    
    mScreenQuad.material = mGSMaterial;
    mUniforms.delta.value = dt;
    mUniforms.feed.value = feed;
    mUniforms.kill.value = kill;
    
    for(var i=0; i<8; ++i)
    {
        if(!mToggled)
        {
            mUniforms.tSource.value = mTexture1;
            mRenderer.render(mScene, mCamera, mTexture2, true);
            mUniforms.tSource.value = mTexture2;
        }
        else
        {
            mUniforms.tSource.value = mTexture2;
            mRenderer.render(mScene, mCamera, mTexture1, true);
            mUniforms.tSource.value = mTexture1;
        }
        
        mToggled = !mToggled;
        mUniforms.brush.value = mMinusOnes;
    }
    
    if(mColorsNeedUpdate)
        updateUniformsColors();
    
    mScreenQuad.material = mScreenMaterial;
    mRenderer.render(mScene, mCamera);
    
    requestAnimationFrame(render);
}

loadPreset = function(idx)
{
    feed = presets[idx].feed;
    kill = presets[idx].kill;
    worldToForm();
}

var updateUniformsColors = function()
{
    var values = $("#gradient").gradient("getValuesRGBS");
    for(var i=0; i<values.length; i++)
    {
        var v = values[i];
        mColors[i].value = new THREE.Vector4(v[0], v[1], v[2], v[3]);
    }
    
    mColorsNeedUpdate = false;
}

var onUpdatedColor = function()
{
    mColorsNeedUpdate = true;
    updateShareString();
}

var onMouseMove = function(e)
{
    var ev = e ? e : window.event;

    mMouseX = ev.pageX - canvasQ.offset().left; // these offsets work with
    mMouseY = ev.pageY - canvasQ.offset().top; //  scrolled documents too
    
    if(mMouseDown)
        mUniforms.brush.value = new THREE.Vector2(mMouseX/canvasWidth, 1-mMouseY/canvasHeight);
}

var onMouseDown = function(e)
{
    var ev = e ? e : window.event;
    mMouseDown = true;
    
    mUniforms.brush.value = new THREE.Vector2(mMouseX/canvasWidth, 1-mMouseY/canvasHeight);
}

var onMouseUp = function(e)
{
    mMouseDown = false;
}

clean = function()
{
    mUniforms.brush.value = new THREE.Vector2(-10, -10);
}

snapshot = function()
{
    var dataURL = canvas.toDataURL("image/png");
    window.open(dataURL, "name-"+Math.random());
}

// resize canvas to fullscreen, scroll to upper left 
// corner and try to enable fullscreen mode and vice-versa
fullscreen = function() {

    var canv = $('#myCanvas');
    var elem = canv.get(0);
    
    if(isFullscreen())
    {
        // end fullscreen
        if (elem.cancelFullscreen) {
            elem.cancelFullscreen();
        } else if (document.mozCancelFullScreen) {
            document.mozCancelFullScreen();
        } else if (document.webkitCancelFullScreen) {
            document.webkitCancelFullScreen();
        }
    }
    
    if(!isFullscreen())
    {
        // save current dimensions as old
        window.oldCanvSize = {
            width : canv.width(), 
            height: canv.height()
        };
        
        // adjust canvas to screen size
        resize(screen.width, screen.height);
        
        // scroll to upper left corner
        $('html, body').scrollTop(canv.offset().top);
        $('html, body').scrollLeft(canv.offset().left);
        
        // request fullscreen in different flavours
        if (elem.requestFullscreen) {
            elem.requestFullscreen();
        } else if (elem.mozRequestFullScreen) {
            elem.mozRequestFullScreen();
        } else if (elem.webkitRequestFullscreen) {
            elem.webkitRequestFullscreen();
        }
    }
}

var isFullscreen = function()
{
    return document.mozFullScreenElement ||
        document.webkitCurrentFullScreenElement ||
        document.fullscreenElement;
}

$(document).bind('webkitfullscreenchange mozfullscreenchange fullscreenchange', function(ev) {
    // restore old canvas size
    if(!isFullscreen())
        resize(window.oldCanvSize.width, window.oldCanvSize.height);
});

var worldToForm = function()
{
    //document.ex.sldReplenishment.value = feed * 1000;
    $("#sld_replenishment").slider("value", feed);
    $("#sld_diminishment").slider("value", kill);
}

var init_controls = function()
{
    $("#sld_replenishment").slider({
        value: feed, min: 0, max:0.1, step:0.001,
        change: function(event, ui) {$("#replenishment").html(ui.value); feed = ui.value; updateShareString();},
        slide: function(event, ui) {$("#replenishment").html(ui.value); feed = ui.value; updateShareString();}
    });
    $("#sld_replenishment").slider("value", feed);
    $("#sld_diminishment").slider({
        value: kill, min: 0, max:0.073, step:0.001,
        change: function(event, ui) {$("#diminishment").html(ui.value); kill = ui.value; updateShareString();},
        slide: function(event, ui) {$("#diminishment").html(ui.value); kill = ui.value; updateShareString();}
    });
    $("#sld_diminishment").slider("value", kill);
    
    $('#share').keypress(function (e) {
        if (e.which == 13) {
            parseShareString();
            return false;
        }
    });
    
    $("#btn_clear").button({
        icons : {primary : "ui-icon-document"},
        text : false
    });
    $("#btn_snapshot").button({
        icons : {primary : "ui-icon-image"},
        text : false
    });
    $("#btn_fullscreen").button({
        icons : {primary : "ui-icon-arrow-4-diag"},
        text : false
    });
    
}

alertInvalidShareString = function()
{
    $("#share").val("Invalid string!");
    setTimeout(updateShareString, 1000);
}

parseShareString = function()
{
    var str = $("#share").val();
    var fields = str.split(",");
    
    if(fields.length != 12)
    {
        alertInvalidShareString();
        return;
    }
    
    var newFeed = parseFloat(fields[0]);
    var newKill = parseFloat(fields[1]);
    
    if(isNaN(newFeed) || isNaN(newKill))
    {
        alertInvalidShareString();
        return;
    }
    
    var newValues = [];
    for(var i=0; i<5; i++)
    {
        var v = [parseFloat(fields[2+2*i]), fields[2+2*i+1]];
        
        if(isNaN(v[0]))
        {
            alertInvalidShareString();
            return;
        }
        
        // Check if the string is a valid color.
        if(! /^#[0-9A-F]{6}$/i.test(v[1]))
        {
            alertInvalidShareString();
            return;
        }
        
        newValues.push(v);
    }
    
    $("#gradient").gradient("setValues", newValues);
    feed = newFeed;
    kill = newKill;
    worldToForm();
}

updateShareString = function()
{
    var str = "".concat(feed, ",", kill);
    
    var values = $("#gradient").gradient("getValues");
    for(var i=0; i<values.length; i++)
    {
        var v = values[i];
        str += "".concat(",", v[0], ",", v[1]);
    }
    $("#share").val(str);
}

var mWord = [[[682,100],[682,667],[569,667],[569,441],[454,441],[454,667],[342,667],[342,100],[454,100],[454,100],[454,326],[569,326],[569,100]], 
//  
[[682,100],[682,213],[457,213],[457,328],[682,328],[682,667],[342,667],[342,553],[570,553],[570,440],[342,440],[342,100],[682,100]], 
//  
[[683,100],[683,215],[453,215],[453,328],[683,328],[683,441],[453,441],[453,668],[342,668],[342,100],[683,100]]]
var currentLetterIndex = 0
var currentPositionIndex = 0
var simSpeed = 0.1
var curPos = []
var nexPos = []
var vectX = 0
var vectY = 0
var angle = 0
var moveX = 0
var moveY = 0
var curX = mWord[0][0][0]
var curY = mWord[0][0][1]
simulateClick = function(){

    if( noAnimation ) {
			return;
		}
    if( mMouseDown || noAnimation == true ){ 
        return 
    }
    // Let's get the current X,Y
    curPos = mWord[currentLetterIndex][currentPositionIndex]

    // Let's get the next position
    // If last letter, loop with first
    if ( currentPositionIndex >= mWord[currentLetterIndex].length - 1 ) {
        nexPos = mWord[currentLetterIndex][0]
    }else{
        nexPos = mWord[currentLetterIndex][currentPositionIndex +1]  
    } 

    // Let's get the line to draw X and Y vector components
    vectX = nexPos[0] - curPos[0]
    vectY = nexPos[1] - curPos[1]


    // Let's move along the vector on the line
    angle = Math.atan2( vectY, vectX )
    moveX = Math.cos(angle) * simSpeed
    moveY = Math.sin(angle) * simSpeed
    curX += moveX
    curY += moveY

    // Draw the point
    mUniforms.brush.value = new THREE.Vector2(curX/canvasWidth, 1-curY/canvasHeight);

    // Is line fully drawn?
    var finishedPos = false
    if (( moveX > 0 && curX > nexPos[0]) 
    || ( moveX < 0 && curX < nexPos[0]) 
    || ( moveY > 0 && curY > nexPos[1])  
    || ( moveY < 0 && curY < nexPos[1]) ){
        finishedPos = true
    }

    // Change position and letter if necessary
    if( finishedPos ){

        // Get new letter if it was the last line
        if( currentPositionIndex >= mWord[currentLetterIndex].length - 1  ){ 
            currentLetterIndex = currentLetterIndex == (mWord.length - 1) ? 0 : currentLetterIndex + 1
            currentPositionIndex = 0

            // Randomize preset
            key = parseInt( Math.random() * presets.length) 
            newPreset = presets[key]
            feed= newPreset.feed
            kill = newPreset.kill
//            mCamera.left = - 0.501
//            mCamera.right = 0.501

        // Just a new line of the letter
        }else{ 
            currentPositionIndex++
        }

        // Force new position point
        curX = mWord[currentLetterIndex][currentPositionIndex][0]
        curY = mWord[currentLetterIndex][currentPositionIndex][1]

    }
            var factor = 0.000002
            var positionBoundary = 0.00005
            randX = (Math.random() - 0.5 ) * factor;
            randY = (Math.random() - 0.5 ) * factor;
            randS = (Math.random() - 0.5 ) * factor * 5 ;
            if( mCamera.position.x < -positionBoundary ){
                mCamera.position.x = -positionBoundary; 
            }else if( mCamera.position.x > positionBoundary ){
                mCamera.position.x = positionBoundary; 
            }else if(mCamera.position.y < -positionBoundary ){
                mCamera.position.y = -positionBoundary; 
            } else if(mCamera.position.y > positionBoundary ){
                mCamera.position.y = positionBoundary; 
            }
            mCamera.position.x += randX; 
            mCamera.position.y += randY; 
            if( mCamera.scale.x < 0.9997 ){
                mCamera.scale.x = 0.9997
            }else if(mCamera.scale.x >= 1.0003  ){
                mCamera.scale.x = 1.0003;
            }
            mCamera.scale.x += randS; 
            mCamera.scale.y += randS ;

            mCamera.updateProjectionMatrix();
    


    }

    setInterval( simulateClick, 1 )


})();
$(function(){

    $(".close").bind("click",function(){ 
        $(this).parent().toggle(); 
    })
    $("#format_show").bind("click",function(){ 
        $("#format").toggle(); 
    })



})
