'use strict';
var stgMouse = {
    dotcolor: 0x9b59b6,
    clickcolor: 0x7477A0,
    goodcolor: 0x00FF00,
    errorcolor: 0xFF0000,
    positiveObjectColor: 0x27ae60,
    negativeObjectColor: 0xc0392b,
    neutralObjectColor: 0xf1c40f,

    feedbackDuration: 1,
    scale: 100
};

var camera = {};
var scene = {};
var start = {};
var f = {};
var renderer = {};
var pointsGeometry = {};
var clickPointsGeometry = {};
var goodPointsGeometry = {};
var errorPointsGeometry = {};
var points = {};
var body = {};
var elementContainer = {};
var clickPoints = {};
var goodPoints = {};
var errorPoints = {};
var animate = {};
var datas = {};
var currIndex = {};
var line = {};
var goodPointsVector = {};
var errorPointsVector = {};
var scoreTime = {};
var posTime = {};
var negTime = {};

var slider = {};
var playbutton = {};
var timeline = {};
var score = {};
var pos = {};
var neg = {};
var total_samples_mouse = {};
var pause = {};
var clickedSlider = {};
var movedSlider = {};
var stageContainer = {};

var pointMaterialMouse = new THREE.PointsMaterial({ color: stgMouse.dotcolor, size: 10 });
var clickpointMaterialMouse = new THREE.PointsMaterial({ color: stgMouse.clickcolor, size: 15 });
var goodpointMaterialMouse = new THREE.PointsMaterial({ color: stgMouse.goodcolor, size: 20 });
var errorpointMaterialMouse = new THREE.PointsMaterial({ color: stgMouse.errorcolor, size: 20 });

// tracked objects
var trackedObjectsPositiveMaterialMouse = new THREE.PointsMaterial({ color: stgMouse.positiveObjectColor, size: 8 });
var trackedObjectsPositiveGeometryMouse = {};
var trackedObjectsPositivePointsMouse   = {};

var trackedObjectsNegativeMaterialMouse = new THREE.PointsMaterial({ color: stgMouse.negativeObjectColor, size: 8 });
var trackedObjectsNegativeGeometryMouse = {};
var trackedObjectsNegativePointsMouse   = {};

var trackedObjectsNeutralMaterialMouse = new THREE.PointsMaterial({ color: stgMouse.neutralObjectColor, size: 14 });
var trackedObjectsNeutralGeometryMouse = {};
var trackedObjectsNeutralPointsMouse = {};


var lastPositionMouse = {};
var onMouseMoveCallbacks = {};


if (typeof window.clamp01==='undefined') {
    window.clamp01 = function(num) {
        return Math.min(Math.max(num, 0.0), 1.0);
    }
}


function animateMouse(elem, data) {
    var canvasNS = "http://www.w3.org/1999/xhtml";
    var container = document.getElementById(elem);
    while (container.firstChild) {
        container.removeChild(container.firstChild);
    }
    container.setAttribute("width", "100%");
    container.setAttribute("height", "100%");
    // here are defined standard settings for the chart (or the parameter in input is filled with missing elements) 

    total_samples_mouse[elem] = 0;
    if (typeof data["cursor"] !== 'undefined' && data["cursor"].length > 0) {
        total_samples_mouse[elem] = data["cursor"].length;
    } else {
        total_samples_mouse[elem] = data["tracked-objects"].length;
    }

    datas[elem] = data;

    f[elem] = datas[elem].f;
    slider[elem] = document.createElement("input");
    slider[elem].setAttribute("type", "range");
    slider[elem].setAttribute("min", "0");
    slider[elem].setAttribute("step", "0.01");
    slider[elem].setAttribute("max", total_samples_mouse[elem] / f[elem]);


    timeline[elem] = document.createElement("span");
    timeline[elem].classList.add("timelabel");
    score[elem] = document.createElement("span");
    score[elem].classList.add("scorelabel");
    var pt = document.createElement("b");
    pt.textContent = "Pt ";
    score[elem].appendChild(pt);
    score[elem].appendChild(document.createElement("span"));

    camera[elem] = new THREE.OrthographicCamera(0, 1, 1, 0, 0.01, 1000);
    camera[elem].position.z = 1;
    scene[elem] = new THREE.Scene();
    scene[elem].background = new THREE.Color(0xF5F5F5);
    currIndex[elem] = -1;
    pointsGeometry[elem] = new THREE.BufferGeometry();
    clickPointsGeometry[elem] = new THREE.BufferGeometry();

    goodPointsGeometry[elem] = new THREE.BufferGeometry();
    errorPointsGeometry[elem] = new THREE.BufferGeometry();

    // tracked objects
    trackedObjectsPositiveGeometryMouse[elem] = new THREE.BufferGeometry();
    trackedObjectsNegativeGeometryMouse[elem] = new THREE.BufferGeometry();
    trackedObjectsNeutralGeometryMouse[elem] = new THREE.BufferGeometry();
    
    goodPointsVector[elem] = [];
    errorPointsVector[elem] = [];
    scoreTime[elem] = [];
    posTime[elem] = [];
    negTime[elem] = [];
    for (var tindex in datas[elem]["tracked-objects"]) {
        scoreTime[elem][tindex] = tindex > 0 ? scoreTime[elem][tindex - 1] : 0;
        posTime[elem][tindex] = tindex > 0 ? posTime[elem][tindex - 1] : 0;
        negTime[elem][tindex] = tindex > 0 ? negTime[elem][tindex - 1] : 0;

        for (var key in datas[elem]) {
            if (Array.isArray(datas[elem][key]) && typeof datas[elem][key][tindex] == 'object' && datas[elem][key][tindex].s) {
                scoreTime[elem][tindex] += datas[elem][key][tindex].s;
                if (datas[elem][key][tindex].s > 0) {
                    posTime[elem][tindex]++;
                } else {
                    negTime[elem][tindex]++;
                }
            }
        }
    }

    pos[elem] = document.createElement("span");
    pos[elem].classList.add("poslabel");
    var check = document.createElement("span");
    check.classList.add("glyphicon");
    check.classList.add("glyphicon-ok");

    pos[elem].appendChild(document.createElement("span"));
    var totalPos = document.createElement("span");
    totalPos.textContent = "/" + posTime[elem][total_samples_mouse[elem] - 1];
    pos[elem].appendChild(totalPos);
    pos[elem].appendChild(check);
    if (negTime[elem][total_samples_mouse[elem] - 1] && negTime[elem][total_samples_mouse[elem] - 1] > 0) {
        neg[elem] = document.createElement("span");
        neg[elem].classList.add("neglabel");
        var cross = document.createElement("span");
        cross.classList.add("glyphicon");
        cross.classList.add("glyphicon-remove");

        neg[elem].appendChild(document.createElement("span"));
        var totalNeg = document.createElement("span");
        totalNeg.textContent = "/" + negTime[elem][total_samples_mouse[elem] - 1];
        neg[elem].appendChild(totalNeg);
        neg[elem].appendChild(cross);
    }
    calculateMouse(elem, 0);
    points[elem] = new THREE.Points(pointsGeometry[elem], pointMaterialMouse);
    clickPoints[elem] = new THREE.Points(clickPointsGeometry[elem], clickpointMaterialMouse);
    goodPoints[elem] = new THREE.Points(goodPointsGeometry[elem], goodpointMaterialMouse);
    errorPoints[elem] = new THREE.Points(errorPointsGeometry[elem], errorpointMaterialMouse);

    // tracked objects
    trackedObjectsPositivePointsMouse[elem]= new THREE.Points(trackedObjectsPositiveGeometryMouse[elem], trackedObjectsPositiveMaterialMouse);
    trackedObjectsNegativePointsMouse[elem]= new THREE.Points(trackedObjectsNegativeGeometryMouse[elem], trackedObjectsNegativeMaterialMouse);
    trackedObjectsNeutralPointsMouse[elem]= new THREE.Points(trackedObjectsNeutralGeometryMouse[elem], trackedObjectsNeutralMaterialMouse);




    body[elem] = new THREE.Group();
    elementContainer[elem] = new THREE.Group();

    elementContainer[elem].add(clickPoints[elem]);
    elementContainer[elem].add(points[elem]);
    elementContainer[elem].add(goodPoints[elem]);
    elementContainer[elem].add(errorPoints[elem]);

    // tracked objects
    elementContainer[elem].add(trackedObjectsPositivePointsMouse[elem]);
    elementContainer[elem].add(trackedObjectsNegativePointsMouse[elem]);
    elementContainer[elem].add(trackedObjectsNeutralPointsMouse[elem]);
    


    body[elem].add(elementContainer[elem]);


    scene[elem].add(body[elem]);
    renderer[elem] = new THREE.WebGLRenderer({ antialias: true });
    renderer[elem].setSize(container.clientWidth, container.clientHeight - 25);
    container.appendChild(renderer[elem].domElement);
    animate[elem] = animateFactoryMouse(elem);
    animate[elem](window.performance.now());


    container.appendChild(slider[elem]);
    slider[elem].addEventListener("touchstart", function (event) {
        event.stopPropagation();
        clickedSlider[elem] = true;
    }, false);
    slider[elem].addEventListener("mousedown", function (event) {
        clickedSlider[elem] = true;
    }, false);
    slider[elem].addEventListener("touchend", function (event) {
        event.stopPropagation();
        if (clickedSlider[elem]) {
            goodPointsVector[elem] = [];
            errorPointsVector[elem] = [];
            start[elem] = window.performance.now() - slider[elem].value * 1000;
        }
        clickedSlider[elem] = false;
    }, false);
    slider[elem].addEventListener("mouseup", function (event) {
        if (clickedSlider[elem]) {
            goodPointsVector[elem] = [];
            errorPointsVector[elem] = [];
            start[elem] = window.performance.now() - slider[elem].value * 1000;
        }
        clickedSlider[elem] = false;
    }, false);
    slider[elem].addEventListener("mouseout", function (event) {
        clickedSlider[elem] = false;
    }, false);
    slider[elem].addEventListener("mousemove", function (event) {
        if (clickedSlider[elem]) {
            goodPointsVector[elem] = [];
            errorPointsVector[elem] = [];
            start[elem] = window.performance.now() - slider[elem].value * 1000;
            movedSlider[elem] = true;
        }
    }, false);
    slider[elem].addEventListener("touchmove", function (event) {
        if (clickedSlider[elem]) {
            goodPointsVector[elem] = [];
            errorPointsVector[elem] = [];
            start[elem] = window.performance.now() - slider[elem].value * 1000;
            movedSlider[elem] = true;
        }
    }, false);
    playbutton[elem] = document.createElement("button");
    playbutton[elem].classList.add("onlyicon");
    playbutton[elem].classList.add("play");
    var pauseButton = document.createElement("span");
    pauseButton.classList.add("glyphicon");
    pauseButton.classList.add("glyphicon-pause");
    pauseButton.setAttribute("aria-hidden", true);
    playbutton[elem].appendChild(pauseButton);
    playbutton[elem].addEventListener("mouseup", function (event) {
        console.log("Pause clicked: " + elem)
        pause[elem] = !pause[elem];
        if (pause[elem]) {
            pauseButton.classList.remove("glyphicon-pause");
            pauseButton.classList.add("glyphicon-play");
        } else {
            pauseButton.classList.add("glyphicon-pause");
            pauseButton.classList.remove("glyphicon-play");
            start[elem] = window.performance.now() - slider[elem].value * 1000;
        }
    });
    container.appendChild(playbutton[elem]);
    container.appendChild(timeline[elem]);

    if (data.events) {
        for (var iev in data.events) {
            if (data.events[iev].index == 0) {
                switch (data.events[iev].type) {
                    case "stage":
                        stageContainer[elem] = document.createElement("div");
                        stageContainer[elem].classList.add("stage-label");
                        stageContainer[elem].appendChild(document.createTextNode("STAGE " + data.events[iev].value));
                        container.appendChild(stageContainer[elem]);
                        break;
                }
            }
        }
    }
    container.appendChild(score[elem]);
    container.appendChild(pos[elem]);
    if (neg[elem])
        container.appendChild(neg[elem]);

    var size = 2;
    var divisions = 10;
    var gridHelper = new THREE.GridHelper(size, divisions, 0xAAAAAA, 0xFFFFFF);
    gridHelper.rotation.x = -Math.PI / 2;
    gridHelper.position.x = 0.5;
    gridHelper.position.y = 0.5;
    gridHelper.position.z = -0.5;

    elementContainer[elem].add(gridHelper);

}


function animateFactoryMouse(elem) {
    return function (timestamp) {

        if (!start[elem]) {
            start[elem] = timestamp;
        }
        requestAnimationFrame(animate[elem]);
        if (!clickedSlider[elem] && !pause[elem]) {
            calculateMouse(elem, (timestamp - start[elem]) / 1000);
        } else if (movedSlider[elem]) {
            calculateMouse(elem, (timestamp - start[elem]) / 1000);
            movedSlider[elem] = false;
        }

        renderer[elem].render(scene[elem], camera[elem]);
    }
}

function calculateMouse(elem, time) {
    currIndex[elem] = Math.floor(time * f[elem]);
    if (!clickedSlider[elem]) {
        slider[elem].value = time;
    }
    var seconds = Math.floor(time % 60);

    timeline[elem].textContent = Math.floor(time / 60) + ":" + (seconds < 10 ? "0" + seconds : seconds);
    var vertices = [];
    var clickVertices = [];

    var trackedObjectsPositiveVertices = [];
    var trackedObjectsNegativeVertices = [];
    var trackedObjectsNeutralVertices = [];

    if (currIndex[elem] >= total_samples_mouse[elem]) {
        currIndex[elem] = 0;
        start[elem] = 0;
        goodPointsVector[elem] = [];
        errorPointsVector[elem] = [];
    }

    var idx = currIndex[elem];

    for (var key in datas[elem]) {
        if (key == 'events') {
            var maxStage = null;
            for (var iev in datas[elem][key]) {
                if (datas[elem][key][iev].index <= idx) {
                    switch (datas[elem][key][iev].type) {
                        case "stage":
                            if (maxStage == null || maxStage.index < datas[elem][key][iev].index) {
                                maxStage = datas[elem][key][iev];
                            }
                            break;
                    }
                }
            }
            if (maxStage && stageContainer[elem] && stageContainer[elem].innerHTML != "STAGE " + maxStage.value) {
                stageContainer[elem].removeChild(stageContainer[elem].firstChild);
                stageContainer[elem].appendChild(document.createTextNode("STAGE " + maxStage.value));
            }
        } else if (key == 'cursor') {
            if (Array.isArray(datas[elem][key]) && Array.isArray(datas[elem][key][idx])) {
                var mousePos = datas[elem][key][idx];
                vertices.push(clamp01(mousePos[0] / stgMouse.scale), clamp01(1 - mousePos[1] / stgMouse.scale), clamp01(mousePos[2] / stgMouse.scale));
            } else if (Array.isArray(datas[elem][key]) && typeof datas[elem][key][idx] == 'object' && datas[elem][key][idx].p) {
                if (datas[elem][key][idx].c) {
                    clickVertices.push(datas[elem][key][idx].p[0] / stgMouse.scale, 1 - datas[elem][key][idx].p[1] / stgMouse.scale, datas[elem][key][idx].p[2] / stgMouse.scale);
                }
                if (datas[elem][key][idx].s != undefined) {
                    if (datas[elem][key][idx].s > 0) {
                        goodPointsVector[elem].push({ t: time, p: datas[elem][key][idx].p });
                    } else {
                        errorPointsVector[elem].push({ t: time, p: datas[elem][key][idx].p });
                    }
                }
                var objPos = datas[elem][key][idx].p;
                vertices.push(clamp01(objPos[0] / stgMouse.scale), clamp01(1 - objPos[1] / stgMouse.scale), clamp01(objPos[2] / stgMouse.scale));
            }
        } else if (key == 'tracked-objects') {
            if (idx>=0) {
                var tkObjects = datas[elem][key][idx];
                for(var cnn=0;cnn<tkObjects.length;cnn++) {
                    var objPos = tkObjects[cnn]['screen-position'];
                    if (typeof tkObjects[cnn].points !== 'undefined' ) {
                        if (tkObjects[cnn].points==0) {
                            trackedObjectsNeutralVertices.push(clamp01(objPos.x / stgMouse.scale), clamp01(1 - objPos.y / stgMouse.scale), 0.03);
                        } else if (tkObjects[cnn].points<0) {
                            trackedObjectsNegativeVertices.push(clamp01(objPos.x / stgMouse.scale), clamp01(1 - objPos.y / stgMouse.scale), 0.03);
                        } else {
                            trackedObjectsPositiveVertices.push(clamp01(objPos.x / stgMouse.scale), clamp01(1 - objPos.y / stgMouse.scale), 0.03);
                        }
                        
                        if (tkObjects[cnn]["cursor"]>0 && tkObjects[cnn]["points"]!=0) {
                            if (tkObjects[cnn]["points"]>0) {
                                goodPointsVector[elem].push({ t: time, p: [objPos.x, objPos.y, 0.03] });
                            } else {
                                errorPointsVector[elem].push({ t: time, p:  [objPos.x, objPos.y, 0.03] });
                            }
                        }
                        
                    }
                }
            }
        }
    }
    score[elem].lastChild.textContent = "" + scoreTime[elem][idx];
    pos[elem].firstChild.textContent = posTime[elem][idx];
    if (neg[elem])
        neg[elem].firstChild.textContent = negTime[elem][idx];

    var goodVertices = [];
    for (var index in goodPointsVector[elem]) {
        if (time - goodPointsVector[elem][index].t < stgMouse.feedbackDuration) {
            goodVertices.push(clamp01(goodPointsVector[elem][index].p[0] / stgMouse.scale), clamp01(1 - goodPointsVector[elem][index].p[1] / stgMouse.scale), 0.02);
        }
    }
    var errorVertices = [];
    for (var index in errorPointsVector[elem]) {
        if (time - errorPointsVector[elem][index].t < stgMouse.feedbackDuration) {
            errorVertices.push(clamp01(errorPointsVector[elem][index].p[0] / stgMouse.scale), clamp01(1 - errorPointsVector[elem][index].p[1] / stgMouse.scale), 0.02);
        }
    }
    if (!(vertices.length > 0 && vertices[0] == 0 && vertices[1] == 1)) {
        pointsGeometry[elem].addAttribute('position', new THREE.BufferAttribute(new Float32Array(vertices), 3));
    } else {
        pointsGeometry[elem].removeAttribute('position');
    }
    //addAttribute
    clickPointsGeometry[elem].addAttribute('position', new THREE.BufferAttribute(new Float32Array(clickVertices), 3));
    goodPointsGeometry[elem].addAttribute('position', new THREE.BufferAttribute(new Float32Array(goodVertices), 3));
    errorPointsGeometry[elem].addAttribute('position', new THREE.BufferAttribute(new Float32Array(errorVertices), 3));

    trackedObjectsPositiveGeometryMouse[elem].addAttribute('position',  new THREE.BufferAttribute(new Float32Array(trackedObjectsPositiveVertices), 3))
    trackedObjectsNegativeGeometryMouse[elem].addAttribute('position',  new THREE.BufferAttribute(new Float32Array(trackedObjectsNegativeVertices), 3))
    trackedObjectsNeutralGeometryMouse[elem].addAttribute('position',  new THREE.BufferAttribute(new Float32Array(trackedObjectsNeutralVertices), 3))
    
}
