'use strict';

var VJS = VJS || {};
VJS.widgets = VJS.widgets || {};

/**
 *
 * It is typically used to get information about an image from the mouse cursor.
 *
 * Demo: {@link https://fnndsc.github.io/vjs#widget_pixelProbe}
 *
 * @constructor
 * @class
 * @memberOf VJS.widgets
 * @public
 *
 */
VJS.widgets.pixelProbe = function(image, imageMeshes) {
    // it is an object 3D that we can add to the scene :)
    THREE.Object3D.call(this);

    this.domElement = null;
    this.rasContainer = null;
    this.ijkContainer = null;
    this.valueContainer = null;

    this.imageMeshes = imageMeshes;
    this.image = image;

    this.volumeCore = null;

    this.marks = [];

    this.createDomElement();

    this._worldCoordinate = null; //LPS
    this._dataCoordinate = null; //IJK
    this._dataValue = null; //
    this._labelValue = null; //
};

VJS.widgets.pixelProbe.prototype = Object.create(THREE.Object3D.prototype);
VJS.widgets.pixelProbe.prototype.constructor = VJS.widgets.pixelProbe;

VJS.widgets.pixelProbe.prototype.createDomElement = function() {

    // RAS
    this.rasContainer = document.createElement('div');
    this.rasContainer.setAttribute('id', 'VJSProbeRAS');

    // IJK
    this.ijkContainer = document.createElement('div');
    this.ijkContainer.setAttribute('id', 'VJSProbeIJK');

    // Value
    this.valueContainer = document.createElement('div');
    this.valueContainer.setAttribute('id', 'VJSProbeValue');

    this.domElement = document.createElement('div');
    this.domElement.setAttribute('id', 'VJSProbe');
    this.domElement.appendChild(this.rasContainer);
    this.domElement.appendChild(this.ijkContainer);
    this.domElement.appendChild(this.valueContainer);
};

VJS.widgets.pixelProbe.prototype.computeValues = function() {
    // convert point to IJK
    if (this.image) {
        var worldToData = this.image._stack[0]._lps2IJK;

        var dataCoordinate = new THREE.Vector3().copy(this._worldCoordinate).applyMatrix4(worldToData);
        var temp = dataCoordinate.clone();

        // same rounding in the shaders
        dataCoordinate.x = Math.floor(dataCoordinate.x + 0.5);
        dataCoordinate.y = Math.floor(dataCoordinate.y + 0.5);
        dataCoordinate.z = Math.floor(dataCoordinate.z + 0.5);
        this._dataCoordinate = dataCoordinate;

        if (dataCoordinate.x >= 0 &&
            dataCoordinate.y >= 0 &&
            dataCoordinate.z >= 0) {
            // var textureSize = this.image._stack[0]._textureSize;
            var rows = this.image._stack[0]._rows;
            var columns = this.image._stack[0]._columns;

            // var index = this._dataCoordinate.x + columns * this._dataCoordinate.y + rows * columns * this._dataCoordinate.z;

            // var textureIndex = Math.floor(index / (textureSize * textureSize));
            // var inTextureIndex = index % (textureSize * textureSize);

            this._dataValue = this.image._stack[0]._frame[this._dataCoordinate.z]._pixelData[this._dataCoordinate.x + columns * this._dataCoordinate.y];
        } else {
            window.console.log('something funny happening in compute value');
            window.console.log(dataCoordinate);
            window.console.log(temp);
        }
    }
};

VJS.widgets.pixelProbe.prototype.updateUI = function(mouse) {
    var rasContent = this._worldCoordinate.x.toFixed(2) + ' : ' + this._worldCoordinate.y.toFixed(2) + ' : ' + this._worldCoordinate.z.toFixed(2);
    this.rasContainer.innerHTML = 'LPS: ' + rasContent;

    var ijkContent = this._dataCoordinate.x + ' : ' + this._dataCoordinate.y + ' : ' + this._dataCoordinate.z;
    this.ijkContainer.innerHTML = 'IJK: ' + ijkContent;

    var valueContent = this._dataValue;
    this.valueContainer.innerHTML = 'Value: ' + valueContent;

    // position of the div...
    // need a mode to track the mouse
    document.getElementById('VJSProbe').style.display = 'block';
    document.getElementById('VJSProbe').style.top = mouse.clientY + 10;
    document.getElementById('VJSProbe').style.left = mouse.clientX + 10;

};

VJS.widgets.pixelProbe.prototype.update = function(raycaster, mouse, camera, canvas) {

    if (!this.imageMeshes) {
        return;
    }

    this.updateMarkDom(raycaster, mouse, camera, canvas);

    // calculate image intersecting the picking ray
    var intersects = raycaster.intersectObjects(this.imageMeshes);

    for (var intersect in intersects) {
        var worldCoordinates = new THREE.Vector3().copy(intersects[intersect].point);

        // if we intersect an image with a ShaderMaterial
        // TODO: review that
        if (intersects[intersect].object.material.type === 'ShaderMaterial') {
            this._worldCoordinate = worldCoordinates;
            // window.console.log(this._worldCoordinate);
            this.computeValues();
            this.updateUI(mouse);
            return;
        }
    }

    // hide UI if not intersecting the planne
    this.hideUI();
};

VJS.widgets.pixelProbe.prototype.hideUI = function() {
    document.getElementById('VJSProbe').style.display = 'none';
};

VJS.widgets.pixelProbe.prototype.mark = function(raycaster, mouse) {
    // calculate image intersecting against itself (ideally N spheres)
    // no all good yet, because we can click on Shader Materail and still
    // intersect another voxel if looking at plane from the side
    // do we intersect a cube of the probe (in front of the plane not detected yet...)
    var intersects = raycaster.intersectObjects(this.children);
    var worldCoordinates = null;
    // Look for a pixelProbeMark
    // for (var intersect in intersects) {
    //   worldCoordinates = new THREE.Vector3().copy(intersects[intersect].point);

    //   // if on a mark, do not do anything
    //   if (intersects[intersect].object.name === 'pixelProbeMark') {
    //     window.console.log('intersect pixelProbeMark!');

    //     return null;
    //   }
    // }

    // Look for intersection against image
    window.console.log(this);
    intersects = raycaster.intersectObjects(this.imageMeshes);
    for (var intersect2 in intersects) {
        worldCoordinates = new THREE.Vector3().copy(intersects[intersect2].point);

        // might be better to re-loop
        // if we intersect an image with a ShaderMaterial
        // TODO: review that
        if (intersects[intersect2].object.material.type === 'ShaderMaterial') {
            window.console.log('intersect shader material!');
            this._worldCoordinate = worldCoordinates;
            this.computeValues();

            // make sure this IJK mark is not already shown...
            for (var i = 0; i < this.marks.length; i++) {
                if (this.marks[i].ijk.x === this._dataCoordinate.x &&
                    this.marks[i].ijk.y === this._dataCoordinate.y &&
                    this.marks[i].ijk.z === this._dataCoordinate.z) {
                    return;
                }
            }

            // create the geometry for it!
            // var sphereGeometry = new THREE.SphereGeometry(1);
            // var material = new THREE.MeshBasicMaterial({
            //     // not selected: amber? #FFC107
            //     // orange? #FF9800
            //     // selected: deep orange? #FF5722
            //     color: 0xFF5722
            //   });
            // var sphere = new THREE.Mesh(sphereGeometry, material);
            // sphere.applyMatrix(new THREE.Matrix4().makeTranslation(
            //   worldCoordinates.x, worldCoordinates.y, worldCoordinates.z));

            // position against World Voxel Center! Not against the mouse!!
            var dataToWorld = this.image._stack[0]._ijk2LPS;
            var worldCenterCoordinate = new THREE.Vector3()
                .copy(this._dataCoordinate)
                .applyMatrix4(dataToWorld);

            var voxDataCoord = this._dataCoordinate.clone();

            var voxelGeometry = new THREE.BoxGeometry(1, 1, 1);
            voxelGeometry.applyMatrix(new THREE.Matrix4().makeTranslation(
                this._dataCoordinate.x,
                this._dataCoordinate.y,
                this._dataCoordinate.z));
            voxelGeometry.applyMatrix(this.image._stack[0]._ijk2LPS);
            var voxelMaterial = new THREE.MeshBasicMaterial({
                wireframe: true,
                color: 0xFFC107
            });
            var voxel = new THREE.Mesh(voxelGeometry, voxelMaterial);
            // move to world space!
            // voxel.applyMatrix(new THREE.Matrix4().makeTranslation(
            //   worldCoordinates.x, worldCoordinates.y, worldCoordinates.z));
            voxel.name = 'pixelProbeMark';
            this.add(voxel);

            // store mark
            var mark = {
                id: voxel.id,
                position: worldCenterCoordinate,
                ijk: voxDataCoord
            };
            this.marks.push(mark);
            window.console.log(this.marks);

            var domElement = this.markDom(mark, mouse);

            return domElement;
        }
    }
};

// do not need mouse in theory...
VJS.widgets.pixelProbe.prototype.markDom = function(mark, mouse) {

    // that could be a web-component!
    // RAS
    var rasContainer = document.createElement('div');
    rasContainer.setAttribute('class', 'VJSProbeRAS');

    var rasContent = this._worldCoordinate.x.toFixed(2) + ' : ' + this._worldCoordinate.y.toFixed(2) + ' : ' + this._worldCoordinate.z.toFixed(2);
    rasContainer.innerHTML = 'LPS: ' + rasContent;

    // IJK
    var ijkContainer = document.createElement('div');
    ijkContainer.setAttribute('class', 'VJSProbeIJK');

    var ijkContent = this._dataCoordinate.x + ' : ' + this._dataCoordinate.y + ' : ' + this._dataCoordinate.z;
    ijkContainer.innerHTML = 'IJK: ' + ijkContent;

    // Value
    var valueContainer = document.createElement('div');
    valueContainer.setAttribute('class', 'VJSProbeValue');

    var valueContent = this._dataValue;
    valueContainer.innerHTML = 'Value: ' + valueContent;

    // Package everything
    var domElement = document.createElement('div');
    domElement.setAttribute('id', 'mark' + mark.id);
    domElement.setAttribute('class', 'mark');
    domElement.appendChild(rasContainer);
    domElement.appendChild(ijkContainer);
    domElement.appendChild(valueContainer);

    domElement.style.display = 'block';
    domElement.style.top = mouse.clientY + 10;
    domElement.style.left = mouse.clientX + 10;

    return domElement;
};

// do not need mouse in theory...
VJS.widgets.pixelProbe.prototype.updateMarkDom = function(raycaster, mouse, camera, canvas) {

    for (var i = 0; i < this.marks.length; i++) {
        // find element in DOM!
        // world coordinates to screen
        var screenCoordinates = this.marks[i].position.clone();
        screenCoordinates.project(camera);

        screenCoordinates.x = Math.round((screenCoordinates.x + 1) * canvas.offsetWidth / 2);
        screenCoordinates.y = Math.round((-screenCoordinates.y + 1) * canvas.offsetHeight / 2);
        screenCoordinates.z = 0;

        // update div position
        // window.console.log(document.getElementById('mark' + this.marks[i].id));
        document.getElementById('mark' + this.marks[i].id).style.top = screenCoordinates.y + 10;
        document.getElementById('mark' + this.marks[i].id).style.left = screenCoordinates.x + 10;

    }

};

/*** Exports ***/

var moduleType = typeof module;
if ((moduleType !== 'undefined') && module.exports) {
    module.exports = VJS.widgets.pixelProbe;
}