var settings = {
    release: true,
};


var Sync = function() {
    this.BPM = 90,
    this.ROWS_PER_BEAT = 8,
    this.ROW_RATE = this.BPM / 60 * this.ROWS_PER_BEAT,
    this.musicReady = false,
    this.music = new Audio(),
    this.device = new JSRocket.SyncDevice(),
    this.row = 0,
    this.previousRow = 0,
    this.tracks = {
        _tracks: {},
        init: function(device) {
            this._tracks.flash = {
                type: "color",
                r: device.getTrack("flash.r"),
                g: device.getTrack("flash.g"),
                b: device.getTrack("flash.b"),
                a: device.getTrack("flash.a")
            };
            this._tracks.blob = {
                type: "blob",
                fov: device.getTrack("blob.fov"),
                x: device.getTrack("blob.x"),
                y: device.getTrack("blob.y"),
                z: device.getTrack("blob.z"),
                sx: device.getTrack("blob.sx"),
                sy: device.getTrack("blob.sy"),
                sz: device.getTrack("blob.sz"),
                t: device.getTrack("blob.type"),
                tw: device.getTrack("blob.twist"),
                bend: device.getTrack("blob.bend"),
                m: device.getTrack("blob.multiply"),
                rx: device.getTrack("blob.rx"),
                ry: device.getTrack("blob.ry"),
                rz: device.getTrack("blob.rz"),
                v: device.getTrack("blob.visible"),
                r: device.getTrack("blob.r"),
                g: device.getTrack("blob.g"),
                b: device.getTrack("blob.b"),
                dx: device.getTrack("blob.dx"),
                dy: device.getTrack("blob.dy"),
                dz: device.getTrack("blob.dz"),
                oblivion: device.getTrack("blob.oblivion"),
            };
            this._tracks.logo = {
                type: "logo",
                x: device.getTrack("logo.x"),
                y: device.getTrack("logo.y"),
                z: device.getTrack("logo.z"),
                w: device.getTrack("logo.w"),
                h: device.getTrack("logo.h"),
                r: device.getTrack("logo.r"),
                g: device.getTrack("logo.g"),
                b: device.getTrack("logo.b"),
                a: device.getTrack("logo.a"),
                e: device.getTrack("logo.e"),
                hl: device.getTrack("logo.hl"),
            };
            this._tracks.starfield = {
                type: "starfield",
                a: device.getTrack("starfield.alpha"),
                rotX: device.getTrack("starfield.x-rotation"),
                rotY: device.getTrack("starfield.y-rotation"),
                rotZ: device.getTrack("starfield.z-rotation"),
                posZ: device.getTrack("starfield.z-position"),
            };
            this._tracks.circles = {
                type: "circles",
                a: device.getTrack("circles.a"),
                x1: device.getTrack("circles.x1"),
                y1: device.getTrack("circles.y1"),
                r1: device.getTrack("circles.r1"),
                g1: device.getTrack("circles.g1"),
                b1: device.getTrack("circles.b1"),
                s1: device.getTrack("circles.s1"),
                x2: device.getTrack("circles.x2"),
                y2: device.getTrack("circles.y2"),
                r2: device.getTrack("circles.r2"),
                g2: device.getTrack("circles.g2"),
                b2: device.getTrack("circles.b2"),
                s2: device.getTrack("circles.s2"),
                x3: device.getTrack("circles.x3"),
                y3: device.getTrack("circles.y3"),
                r3: device.getTrack("circles.r3"),
                g3: device.getTrack("circles.g3"),
                b3: device.getTrack("circles.b3"),
                s3: device.getTrack("circles.s3"),
                v: device.getTrack("circles.v"),
            };         
        },
        get: function(name, row) {
            var track = this._tracks[name];
            if(track.type == "color") {
                return {
                    r: track.r.getValue(row) || 0,
                    g: track.g.getValue(row) || 0,
                    b: track.b.getValue(row) || 0,
                    a: track.a.getValue(row) || 0
                }
            }
            else if(track.type == "blob") {
                return {
                    fov: track.fov.getValue(row) || 0,
                    x: track.x.getValue(row) || 0,
                    y: track.y.getValue(row) || 0,
                    z: track.z.getValue(row) || 0,
                    sx: track.sx.getValue(row) || 0,
                    sy: track.sy.getValue(row) || 0,
                    sz: track.sz.getValue(row) || 0,
                    type: track.t.getValue(row) || 0,
                    twist: track.tw.getValue(row) || 0,
                    bend: track.bend.getValue(row) || 0,
                    multiply: track.m.getValue(row) || 0,
                    rx: track.rx.getValue(row) || 0,
                    ry: track.ry.getValue(row) || 0,
                    rz: track.rz.getValue(row) || 0,
                    visible: track.v.getValue(row) || 0,
                    r: track.r.getValue(row) || 0,
                    g: track.g.getValue(row) || 0,
                    b: track.b.getValue(row) || 0,
                    dx: track.dx.getValue(row) || 0,
                    dy: track.dy.getValue(row) || 0,
                    dz: track.dz.getValue(row) || 0,
                    oblivion: track.oblivion.getValue(row) || 0,
                }
            }
            else if(track.type == "logo") {
                return {
                    x: track.x.getValue(row) || 0,
                    y: track.y.getValue(row) || 0,
                    z: track.z.getValue(row) || 0,
                    width: track.w.getValue(row) || 0,
                    height: track.h.getValue(row) || 0,
                    r: track.r.getValue(row) || 0,
                    g: track.g.getValue(row) || 0,
                    b: track.b.getValue(row) || 0,
                    a: track.a.getValue(row) || 0,
                    enabled: track.e.getValue(row) || 0,
                    highlight: track.hl.getValue(row) || 0
                }
            }
            else if(track.type == "starfield") {
                return {
                    alpha: track.a.getValue(row) || 0,
                    rotX: track.rotX.getValue(row) || 0,
                    rotY: track.rotY.getValue(row) || 0,
                    rotZ: track.rotZ.getValue(row) || 0,
                    posZ: track.posZ.getValue(row) || 0
                }
            }
            else if(track.type == "circles") {
                return {
                    alpha: track.a.getValue(row) || 0,
                    x1: track.x1.getValue(row) || 0,
                    y1: track.y1.getValue(row) || 0,
                    r1: track.r1.getValue(row) || 0,
                    g1: track.g1.getValue(row) || 0,
                    b1: track.b1.getValue(row) || 0,
                    s1: track.s1.getValue(row) || 0,
                    x2: track.x2.getValue(row) || 0,
                    y2: track.y2.getValue(row) || 0,
                    r2: track.r2.getValue(row) || 0,
                    g2: track.g2.getValue(row) || 0,
                    b2: track.b2.getValue(row) || 0,
                    s2: track.s2.getValue(row) || 0,
                    x3: track.x3.getValue(row) || 0,
                    y3: track.y3.getValue(row) || 0,
                    r3: track.r3.getValue(row) || 0,
                    g3: track.g3.getValue(row) || 0,
                    b3: track.b3.getValue(row) || 0,
                    s3: track.s3.getValue(row) || 0,
                    visible: track.v.getValue(row) || 0
                }
            }
            else if(track.type == "distorition") {
                return {
                    distortion: track.d.getValue(row) ||0
                }
            }
        }
    },

    this.prepare = function() {
        if(settings.release) {
            this.device.setConfig({
                rocketXML: "assets/music/vortex2017.rocket"
            });
            this.device.init("demo");
        }
        else {
            this.device.setConfig({
                socketURL:'ws://localhost:1339'
            });
            this.device.init();
        }
   
        this.device.on("ready", this.ready.bind(this));
        this.device.on("update", this.update.bind(this));
        this.device.on("play", this.play.bind(this));
        this.device.on("pause", this.pause.bind(this));
    },

    this.loadMusic = function() {
        this.music.src = "assets/music/ninjapinja.mp3",
        this.music.load();
        this.music.preload = true;
        this.music.addEventListener("canplay", this.canPlay.bind(this));
    },

    this.canPlay = function() {
        if(this.musicReady) {
            return;
        }  
        if(settings.release) { 
            render();
            this.music.play();
        } else {
            this.music.pause();
            this.music.currentTime = sync.row / sync.ROW_RATE;
        }
        this.musicReady = true;
       
    },
    this.ready = function() {
        this.tracks.init(this.device);
        this.loadMusic();
    },
    this.update = function(row) {
        if (!isNaN(row)) {
            this.row = row;
            this.music.currentTime = this.row / this.ROW_RATE;
        }
        render();
    },
    this.play = function() {
        this.music.currentTime = this.row / this.ROW_RATE;
        this.music.play();
        render();
    },
    this.pause = function() {
        this.row = this.music.currentTime * this.ROW_RATE;
        window.cancelAnimationFrame(render, document);
        this.music.pause();
    }
};

var Universe = function() {
    this.screen = {
        width: window.innerWidth,
        height: window.innerHeight,
        renderer: null,
    },
    this.observer = {
        camera: null,
        controls: null,
        hud: null,
        target: null,
        speed: 0.01,
        target: null,
        targetDirection: null,
        translationTween: null,
        tweenPosition: null,
        translationTweenUpdate: function(v) {
            this.camera.position.x = v.x;
            this.camera.position.y = v.y;
            this.camera.position.z = v.z;

        },
        translationTweenComplete: function() {
            this.translationTween = null;
        },
        setTarget: function(t) {
            this.target = t;
            this.targetDirection = this.camera.position.clone().add(t).normalize();
        }
    },
    this.time = {
        delta: 0,
        time: 0,
        frameStart: 0,
        update: function() {
            var c = new Date().getTime();
            if(this.frameStart > 0)
                this.delta = c - this.frameStart;
            this.time += this.delta;

            this.frameStart = c;
        }
    },
    this.world = {
        scene: null
    },
    this.hud = {
        scene: null,
        width: window.innerWidth,
        height: window.innerHeight,
    }
    this.render = function() {
        this.screen.renderer.render(this.world.scene, this.observer.camera);
        //this.screen.renderer.render(this.hud.scene, this.observer.hud);
    },
    this.update = function() {
        return;
        this.observer.controls.update();
        if(this.observer.translationTween) {

            this.observer.translationTween.update(this.time.time);
        }
    }
};

var sync = new Sync();
var universe = new Universe();
var blobUniforms = null;
var logoUniforms = null;
var blob = null;
var pointLight = null;
var flash = null;
var logo = null;
var starfield = null;
var circles = null;
var circlesUniforms = null;

function initialize() {
    universe.screen.renderer = new THREE.WebGLRenderer();
    universe.screen.renderer.autoClear = false;
    universe.screen.width = window.innerWidth;
    universe.screen.height = window.innerHeight;
    universe.screen.renderer.setSize(universe.screen.width, universe.screen.height);
    document.body.appendChild(universe.screen.renderer.domElement);
    
    universe.world.scene = new THREE.Scene();
    universe.hud.scene = new THREE.Scene();

    universe.observer.camera = new THREE.PerspectiveCamera(75, universe.screen.width / universe.screen.height, 0.1, 1000 );
    universe.observer.hud = new THREE.OrthographicCamera(-universe.hud.width, universe.hud.width, universe.hud.height, -universe.hud.height, 0, 30 );

    universe.observer.controls = new THREE.OrbitControls(universe.observer.camera);

    universe.observer.camera.position.z = 5;
    universe.observer.camera.position.y = 0;

    universe.world.scene.add(universe.observer.camera);


    pointLight = new THREE.PointLight(0xff0000, 1, 100);
    pointLight.position.set(0.0, 5.0, 0.0);
    universe.world.scene.add(pointLight);

    var blobGeometry = new THREE.PlaneBufferGeometry(500, 500, 250, 250);

    blobUniforms = THREE.UniformsUtils.merge([
        THREE.UniformsLib["lights"]
    ]);
    blobUniforms["oblivion"] = {
        value: 0.0
    };
    blobUniforms["blobColor"] = {
            value: new THREE.Color(0x4286f4)
    };
    blobUniforms["resolution"] = {
        value: new THREE.Vector2(universe.screen.width, universe.screen.height)
    };
    blobUniforms["fov"] = {
        value: 1.0
    };
    blobUniforms["blobPosition"] = {
        value: new THREE.Vector3(0.0, 0.0, 0.0)
    };
    blobUniforms["blobType"] = {
        value: 1
    };
    blobUniforms["blobSize"] = {
        value: new THREE.Vector3(0.0, 0.0, 0.0)
    };
    blobUniforms["blobTwist"] = {
        value: 1.0
    };
    blobUniforms["blobBend"] = {
        value: 1.0
    };
    blobUniforms["blobMultiply"] = {
        value: 1.0
    };
    blobUniforms["blobRepeat"] = {
        value: new THREE.Vector3(0.0, 0.0, 0.0)
    };
    blobUniforms["blobVisible"] = {
        value: 1.0
    };
    blobUniforms["blobDistortion"] = {
        value: new THREE.Vector3(0.0, 0.0, 0.0)
    };
    var blobMaterial = new THREE.ShaderMaterial( {
		uniforms:       blobUniforms,
		vertexShader:   document.getElementById("simple-vertex-shader").textContent,
		fragmentShader: document.getElementById("raymarching-fragment-shader").textContent,

		blending:       THREE.AdditiveBlending,
		depthTest:      true,
		transparent:    false,
        wireframe:      false,
        lights:         true,

    });
    
    blob = new THREE.Mesh(blobGeometry, blobMaterial);
 
    universe.world.scene.add(blob);
    blob.position.z = -5;

    var flashGeometry = new THREE.PlaneGeometry(20, 20, 32);
    var flashMaterial = new THREE.MeshBasicMaterial({
        color: 0xffff00, 
        side: THREE.DoubleSide,
        transparent: true,
        opacity: 1
    });

    flash = new THREE.Mesh(flashGeometry, flashMaterial);
    flash.position.z = -2;
    universe.observer.camera.add(flash);

    logoUniforms = THREE.UniformsUtils.merge([
        THREE.UniformsLib["common"],
        THREE.UniformsLib["lights"]
    ]);

    var logoTexture = new THREE.TextureLoader().load("assets/graphics/tahtituho.png");
    logoTexture.wrapS = logoTexture.wrapT = THREE.RepeatWrapping;
    logoUniforms["resolution"] = {
        value: new THREE.Vector2(universe.screen.width, universe.screen.height)
    };
    logoUniforms["hPos"] = {
        value: 0.0
    };
    logoUniforms["width"] = {
        value: 0.0
    };
    logoUniforms["color"] = {
        value: new THREE.Vector3(0.0, 0.0, 0.0)
    };
    logoUniforms["alpha"] = {
        value: 0.0
    };
    logoUniforms["enabled"] = {
        value: 0.0
    };
    logoUniforms["texture"] = {
        value: logoTexture
    };
    
    var logoShaderMaterial = new THREE.ShaderMaterial({
        uniforms:       logoUniforms,
        vertexShader:   document.getElementById("simple-vertex-shader").textContent,
        fragmentShader: document.getElementById("logo-fragment-shader").textContent,
       
        depthTest:      false,
        transparent:    true,
        wireframe:      false,
        lights:         false,
      
   

    });
    var logoGeometry = new THREE.PlaneGeometry(25.0, 25.0 / 3.72, 320, 320);
    logo = new THREE.Mesh(logoGeometry, logoShaderMaterial);
    logo.position.z = -2;

    universe.observer.camera.add(logo);

    var starfieldGeometry = new THREE.Geometry();
    
    for ( var i = 0; i < 10000; i ++ ) {
    
        var star = new THREE.Vector3();
        star.x = THREE.Math.randFloatSpread(2000);
        star.y = THREE.Math.randFloatSpread(2000);
        star.z = THREE.Math.randFloatSpread(2000);
    
        starfieldGeometry.vertices.push(star);
    
    }
    
    var starfieldMaterial = new THREE.PointsMaterial({ 
        color: 0xffffff,
        transparent: true,
        map: new THREE.TextureLoader().load("assets/graphics/particle.png"),
        size: 2.0
    });
    
    starfield = new THREE.Points(starfieldGeometry, starfieldMaterial);
    
    universe.world.scene.add(starfield);
  
    var circlesGeometry = new THREE.PlaneBufferGeometry(500, 500, 250, 250);
    
    circlesUniforms = THREE.UniformsUtils.merge([
        THREE.UniformsLib["lights"]
    ]);
    circlesUniforms["resolution"] = {
        value: new THREE.Vector2(universe.screen.width, universe.screen.height)
    };
    circlesUniforms["alpha"] = {
        value: 1.0
    };
    circlesUniforms["pos1"] = {
        value: new THREE.Vector2(0.0, 0.0)
    };
    circlesUniforms["size1"] = {
        value: 1.0
    };
    circlesUniforms["color1"] = {
        value: new THREE.Vector3(0.0, 0.0, 0.0)
    };
    circlesUniforms["pos2"] = {
        value: new THREE.Vector2(0.0, 0.0)
    };
    circlesUniforms["size2"] = {
        value: 1.0
    };
    circlesUniforms["color2"] = {
        value: new THREE.Vector3(0.0, 0.0, 0.0)
    };
    circlesUniforms["pos3"] = {
        value: new THREE.Vector2(0.0, 0.0)
    };
    circlesUniforms["size3"] = {
        value: 1.0
    };
    circlesUniforms["color3"] = {
        value: new THREE.Vector3(0.0, 0.0, 0.0)
    };

    var circlesMaterial = new THREE.ShaderMaterial( {
        uniforms:       circlesUniforms,
        vertexShader:   document.getElementById("simple-vertex-shader").textContent,
        fragmentShader: document.getElementById("circles-fragment-shader").textContent,

        blending:       THREE.AdditiveBlending,
        depthTest:      true,
        transparent:    false,
        wireframe:      false,
        lights:         false,

    });
    
    circles = new THREE.Mesh(circlesGeometry, circlesMaterial);
    circles.position.z = -5;
    universe.world.scene.add(circles);
    sync.prepare();
    sync.loadMusic();
    
    
}

function render() {
    universe.time.update();

    if(sync.music.paused === false) {
        //only update the row in Rocket when the demo is playing
        sync.row = sync.music.currentTime * sync.ROW_RATE;
        sync.device.update(sync.row);
    }
    
   
    var flashSync = sync.tracks.get("flash", sync.row);
    flash.material.color.r = flashSync.r;
    flash.material.color.g = flashSync.g;
    flash.material.color.b = flashSync.b;
    flash.material.opacity = flashSync.a;

    var blobSync = sync.tracks.get("blob", sync.row);
    blobUniforms.blobColor.value = new THREE.Vector3(blobSync.r, blobSync.g, blobSync.b);
    blobUniforms.fov.value = blobSync.fov;

    blobUniforms.oblivion.value = blobSync.oblivion;
    blobUniforms.blobPosition.value = new THREE.Vector3(blobSync.x, blobSync.y, blobSync.z);
    blobUniforms.blobType.value = blobSync.type;
    blobUniforms.blobSize.value = new THREE.Vector3(blobSync.sx, blobSync.sy, blobSync.sz);
    blobUniforms.blobTwist.value = blobSync.twist;
    blobUniforms.blobBend.value = blobSync.bend;
    blobUniforms.blobMultiply.value = blobSync.multiply;
    blobUniforms.blobRepeat.value = new THREE.Vector3(blobSync.rx, blobSync.ry, blobSync.rz);
    blobUniforms.blobDistortion.value = new THREE.Vector3(blobSync.dx, blobSync.dy, blobSync.dz);
    blob.material.visible = (blobSync.visible == 1) ? true : false;

    var logoSync = sync.tracks.get("logo", sync.row);
    logoUniforms.hPos.value = logoSync.highlight; 
    logoUniforms.width.value = logoSync.width; 
    logoUniforms.color.value = new THREE.Vector3(logoSync.r, logoSync.g, logoSync.b);
    logoUniforms.alpha.value = logoSync.a;
    logoUniforms.enabled.value = logoSync.enabled;
    logo.position.x = logoSync.x;
    logo.position.y = logoSync.y;
    logo.position.z = logoSync.z;

    var starfieldSync = sync.tracks.get("starfield", sync.row);
    starfield.material.opacity = starfieldSync.alpha;
    starfield.rotation.x = starfieldSync.rotX;
    starfield.rotation.y = starfieldSync.rotY;
    starfield.rotation.z = starfieldSync.rotZ;
    starfield.position.z = starfieldSync.posZ;

    var circlesSync = sync.tracks.get("circles", sync.row);
    circlesUniforms.alpha.value = circlesSync.alpha;
    circlesUniforms.pos1.value = new THREE.Vector2(circlesSync.x1, circlesSync.y1);
    circlesUniforms.color1.value = new THREE.Vector3(circlesSync.r1, circlesSync.g1, circlesSync.b1);
    circlesUniforms.size1.value = circlesSync.s1;
    circlesUniforms.pos2.value = new THREE.Vector2(circlesSync.x2, circlesSync.y2);
    circlesUniforms.color2.value = new THREE.Vector3(circlesSync.r2, circlesSync.g2, circlesSync.b2);
    circlesUniforms.size2.value = circlesSync.s2;
    circlesUniforms.pos3.value = new THREE.Vector2(circlesSync.x3, circlesSync.y3);
    circlesUniforms.color3.value = new THREE.Vector3(circlesSync.r3, circlesSync.g3, circlesSync.b3);
    circlesUniforms.size3.value = circlesSync.s3;
    circles.material.visible = (circlesSync.visible == 1) ? true : false;

    universe.update();
    universe.render();

    
    if((settings.release === true)  || (sync.music.paused === false)) {
        window.requestAnimationFrame(render);
    }
    else {
        window.cancelAnimationFrame(render);
    }
}





