//getUserMedia support check
// first deal with browser prefixes
// var getUserMedia = navigator.getUserMedia ||
//     navigator.mozGetUserMedia ||
//     navigator.webkitGetUserMedia;

var trackWidth = 320;
var trackHeight = 240;

//emscripten code
var Module = {
    preRun: [],
    postRun: [],
    print: (function() {
        return function(text) {
            if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
            console.log(text);
        };
    })(),
    printErr: function(text) {
        if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
        if (0) { // XXX disabled for safety typeof dump == 'function') {
            dump(text + '\n'); // fast, straight to the real console
        } else {
            console.error(text);
        }
    },
    canvas: (function() {
        // var canvas = document.getElementById('canvas');

        // As a default initial behavior, pop up an alert when webgl context is lost. To make your
        // application robust, you may want to override this behavior before shipping!
        // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
        pixiCvs.addEventListener("webglcontextlost", function(e) {
            alert('WebGL context lost. You will need to reload the page.');
            e.preventDefault();
        }, false);

        return pixiCvs;
    })(),
    setStatus: function(text) {
        if (!Module.setStatus.last) Module.setStatus.last = {
            time: Date.now(),
            text: ''
        };
        if (text === Module.setStatus.text) return;
        var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
        var now = Date.now();
        if (m && now - Date.now() < 30) return; // if this is a progress update, skip it if too soon
        if (m) {
            text = m[1];

        } else {

        }
    },
    totalDependencies: 0,
    monitorRunDependencies: function(left) {
        this.totalDependencies = Math.max(this.totalDependencies, left);
        Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies - left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
    },
    onRuntimeInitialized: function() {
        // context = pixiCvs.getContext("2d");

        //this is what we draw the squares in, i think
        track_canvas = document.createElement('canvas');
        track_canvas.width = trackWidth;
        track_canvas.height = trackHeight;
        track_context = track_canvas.getContext('2d');

        c_canvas = document.createElement('canvas');
        c_canvas.width = video.width;
        c_canvas.height = video.height;
        c_context = c_canvas.getContext('2d');

        img_u8 = new jsfeat.matrix_t(trackWidth, trackHeight, jsfeat.U8_t | jsfeat.C1_t);
        ii_sum = new Int32Array((trackWidth + 1) * (trackHeight + 1));
        ii_sqsum = new Int32Array((trackWidth + 1) * (trackHeight + 1));
        ii_tilted = new Int32Array((trackWidth + 1) * (trackHeight + 1));

        nDataBytes = c_canvas.width * c_canvas.height * 4;
        dataPtr = Module._malloc(nDataBytes);
        dataHeap = new Uint8Array(Module.HEAPU8.buffer, dataPtr, nDataBytes);

        LoadModel = Module.cwrap('LoadModel', 'number', ['number', 'number']);
        LoadModel(c_canvas.width, c_canvas.height);

        Track = Module.cwrap('Track', 'array', ['number', 'number', 'number', 'number', 'number']);

        compatibility.requestAnimationFrame(tick);
    }
};
Module.setStatus('Downloading...');
window.onerror = function(event) {
    // TODO: do not warn on ok events like simulating an infinite loop or exitStatus
    Module.setStatus('Exception thrown, see JavaScript console');

    Module.setStatus = function(text) {
        if (text) Module.printErr('[post-exception status] ' + text);
    };
};


var debug = false;

var stat = new profiler();
stat.add("haar detector");

var classifier = jsfeat.haar.frontalface;
var img_u8;
var edg;
var work_canvas;
var work_context;
var ii_sum;
var ii_sqsum;
var ii_tilted;
var ii_canny;
var code = jsfeat.COLOR_RGBA2GRAY;
var w = 0;
var h = 0;
var imageData;
var max_work_size = 160;
var pointScale = video.width / trackWidth;
var nDataBytes;
var dataPtr;
var dataHeap;
var assetType = 103;
var dataAttr = "";

var rects;
var points = [];
points[0] = 0;
points[1] = 0;
var arrayAvg37 = [];
var arrayAvg41 = [];
var arrayAvg40 = [];
var arrayAvg38 = [];
var arrayAvg43 = [];
var arrayAvg47 = [];
var arrayAvg44 = [];
var arrayAvg46 = [];

//face coordinates
var distBetweenEyes = -1;
var holderLeftX = -1;
var holderLeftY = -1;
var holderRightX = -1;
var holderRightY = -1;
var holderScaleX = -1;
var holderScaleY = -1;
var scY = -1;
var r = -1;
var color = "#000000";
var amount = 0.3;
var faceScale = -1;
var maxLength = 3;
var num;

var debugCvs = document.getElementById("debug");
var debugCtx = debugCvs.getContext("2d");

function smoothPts(point, arr, id) {
    if (point > 0) {

        arr.push(point);
        if (arr.length == maxLength) {
            arr.shift();
        };

        num = 0;
        for (var i = 0; i < arr.length; i++) {
            num = num + arr[i];
        };

        num /= arr.length;

        switch (id) {
            case 37:
                points[37 * 2 + 1] = num;
                break;

            case 41:
                points[41 * 2 + 1] = num;
                break;

            case 40:
                points[40 * 2 + 1] = num;
                break;

            case 38:
                points[38 * 2 + 1] = num;
                break;

            case 43:
                points[43 * 2 + 1] = num;
                break;

            case 47:
                points[47 * 2 + 1] = num;
                break;

            case 46:
                points[46 * 2 + 1] = num;
                break;

            case 44:
                points[44 * 2 + 1] = num;
                break;
        }

    };
}

//facetracker loop
function tick() {
    faceAnim = compatibility.requestAnimationFrame(tick);

    stat.new_frame();

    if (video.readyState === video.HAVE_ENOUGH_DATA) {

        debugCtx.clearRect(0, 0, debugCvs.width, debugCvs.height);


        stat.start("haar detector");

        if (points[0] === 0 && points[1] === 0) {
            track_context.drawImage(video, 0, 0, trackWidth, trackHeight);
            imageData = track_context.getImageData(0, 0, trackWidth, trackHeight);

            jsfeat.imgproc.grayscale(imageData.data, track_canvas.width, track_canvas.height, img_u8);
            jsfeat.imgproc.compute_integral_image(img_u8, ii_sum, ii_sqsum, classifier.tilted ? ii_tilted : null);
            //jsfeat.imgproc.equalize_histogram(img_u8, img_u8);

            rects = jsfeat.haar.detect_multi_scale(ii_sum, ii_sqsum, ii_tilted, null, img_u8.cols, img_u8.rows, classifier, 1.2, 3);
            rects = jsfeat.haar.group_rectangles(rects, 1);
        } else {
            rects[0].x = 0;
            rects[0].y = 0;
            rects[0].width = 100;
            rects[0].height = 100;
        }

        if (rects.length > 0) {
            c_context.drawImage(pixiCvs, 0, 0, c_canvas.width, c_canvas.height);
            imageData = c_context.getImageData(0, 0, c_canvas.width, c_canvas.height);

            dataHeap.set(imageData.data);
            var returnArrayPtr = Track(dataPtr, rects[0].x * pointScale, rects[0].y * pointScale, rects[0].width * pointScale, rects[0].height * pointScale);

            points = readArray(returnArrayPtr, 136);

            distBetweenEyes = Math.sqrt(Math.pow(points[39 * 2] - points[42 * 2], 2) + Math.pow(points[39 * 2 + 1] - points[42 * 2 + 1], 2));
            faceScale = distBetweenEyes / 140;


            var leftSpacing = Math.sqrt(Math.pow(points[37 * 2] - points[41 * 2], 2) + Math.pow(points[37 * 2 + 1] - points[41 * 2 + 1], 2));
            var rightSpacing = Math.sqrt(Math.pow(points[44 * 2] - points[46 * 2], 2) + Math.pow(points[44 * 2 + 1] - points[46 * 2 + 1], 2));

            var neutralDistance = 25 * faceScale;
            var leftDelta = (neutralDistance - leftSpacing) * 1.25;
            var rightDelta = (neutralDistance - rightSpacing) * 1.25;

            points[37 * 2 + 1] += leftDelta;
            points[41 * 2 + 1] -= leftDelta;
            points[38 * 2 + 1] += leftDelta;
            points[40 * 2 + 1] -= leftDelta;

            points[43 * 2 + 1] += rightDelta;
            points[47 * 2 + 1] -= rightDelta;
            points[44 * 2 + 1] += rightDelta;
            points[46 * 2 + 1] -= rightDelta;

            //points[45 * 2 + 1] += 5;
            smoothPts(points[37 * 2 + 1], arrayAvg37, 37);
            smoothPts(points[41 * 2 + 1], arrayAvg41, 41);
            smoothPts(points[38 * 2 + 1], arrayAvg38, 38);
            smoothPts(points[40 * 2 + 1], arrayAvg40, 40);
            smoothPts(points[43 * 2 + 1], arrayAvg43, 43);
            smoothPts(points[47 * 2 + 1], arrayAvg47, 47);
            smoothPts(points[44 * 2 + 1], arrayAvg44, 44);
            smoothPts(points[46 * 2 + 1], arrayAvg46, 46);


            holderLeftX = (points[38 * 2] + points[41 * 2]) / 2;
            holderLeftY = (points[38 * 2 + 1] + points[41 * 2 + 1]) / 2;

            holderRightX = (points[43 * 2] + points[46 * 2]) / 2;
            holderRightY = (points[43 * 2 + 1] + points[46 * 2 + 1]) / 2;

            scY = holderScaleY;
            if (scY < 0.05)
                scY = 0.05;

            r = Math.atan2(points[39 * 2 + 1] - points[36 * 2 + 1], points[39 * 2] - points[36 * 2]) * 180 / Math.PI;

            if (debug) {
                draw_faces();
            }

        }

        stat.stop("haar detector");

        if (debug) {
            $('#log').html(stat.log());
        } else {
            $('#log').html();
        }

    }
}

//draws points for debugging
function draw_faces() {
    if (rects[0].x > 0) {

        debugCtx.strokeStyle = "#00FF00";
        debugCtx.strokeRect((rects[0].x * pointScale) | 0, (rects[0].y * pointScale) | 0, (rects[0].width * pointScale) | 0, (rects[0].height * pointScale) | 0);
    }
    for (var i = 0; i < points.length; ++i) {
        if (i === 60 || i === 59) {
            debugCtx.fillStyle = "#ff9900";
        } else {
            debugCtx.fillStyle = "#00FF00";
        }

        debugCtx.beginPath();
        debugCtx.arc(points[i * 2], points[i * 2 + 1], 2, 0, 2 * Math.PI, false);
        debugCtx.closePath();
        debugCtx.fill();
    }

}

function readArray(ptr, length) {
    var array = [];

    for (var i = 0; i < length; i++) {
        var value = getValue(ptr + (i * 4), 'i32');
        array.push(value);
    }
    return array;

}

//debug
// $(document).on("keypress", function(e) {
//     if (e.which === 100) {
//         if (debug) {
//             debug = false;
//         } else {
//             debug = true;
//         }

//         if ($('#log').is(":visible")) {
//             $('#log').hide();
//         } else {
//             $('#log').show();
//         }
//     }
// });
