/* <copyright>
This file contains proprietary software owned by Motorola Mobility, Inc.<br/>
No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/>
(c) Copyright 2011 Motorola Mobility, Inc.  All Rights Reserved.
</copyright> */

/* Base class for the 3D rotation tools
Subclass RotateObject3DTool will rotate the object that was clicked.
Subclass RotateStage3DTool will rotate the stage.
 */
var Montage = require("montage/core/core").Montage,
    ModifierToolBase = require("js/tools/modifier-tool-base").ModifierToolBase,
    snapManager = require("js/helper-classes/3D/snap-manager").SnapManager,
    viewUtils = require("js/helper-classes/3D/view-utils").ViewUtils,
    vecUtils = require("js/helper-classes/3D/vec-utils").VecUtils,
    drawUtils = require("js/helper-classes/3D/draw-utils").DrawUtils,
    ElementsMediator = require("js/mediators/element-mediator").ElementMediator;

exports.Rotate3DToolBase = Montage.create(ModifierToolBase, {
	_canSnap: { value: false },

    _inLocalMode: { value: true, enumerable: true },

    drawWithoutSnapping:
    {
        value: function(event)
        {
            if(this._handleMode !== null)
            {
                this._matL = this._handles[this._handleMode]._matL.slice(0);
                this._planeEq = this._handles[this._handleMode]._planeEq.slice(0);
                this._dirVecL = this._handles[this._handleMode]._dirVecL.slice(0);
                this._startPoint = MathUtils.getLocalPoint(this.downPoint.x,
                                                            this.downPoint.y,
                                                            this._planeEq,
                                                            this._matL);
                if(!this._startPoint)
                {
                    this._startPoint = [this.downPoint.x, this.downPoint.y];
                }
            }
        }
    },

    modifyElements: {
        value: function(data, event) {
            var mat,
                angle,
                pt0 = data.pt0,
                pt1 = data.pt1;

            if(this._handleMode !== null)
            {
                if(this._activateOriginHandle)
                {
                    // move the transform origin handle
                    var dx = pt1.x - pt0.x;
                    var dy = pt1.y - pt0.y;
                    this._origin[0] += dx;
                    this._origin[1] += dy;

                    var len = this._targets.length;
                    if(len === 1)
                    {
                        this._startOriginArray[0][0] += dx;
                        this._startOriginArray[0][1] += dy;
                    }
                    this.downPoint.x = pt1.x;
                    this.downPoint.y = pt1.y;
                    this.DrawHandles();
                    return;
                }

                angle = this._getAngleToRotate(pt1.x, pt1.y);
                if(event.shiftKey)
                {
                    var f = Math.floor(angle/(Math.PI/4));
                    angle = f*Math.PI/4;
                }
                this._delta = angle;
                switch(this._handleMode)
                {
                    case 0:
                        // Rotate X;
                        mat = Matrix.RotationX(angle);
                        break;
                    case 1:
                        // Rotate Y
                        mat = Matrix.RotationY(angle);
                        break;
                    case 2:
                        // Rotate Z
                        mat = Matrix.RotationZ(angle);
                        break;
                    default:
                        break;
                }
            }
            else
            {
                if (event.ctrlKey || event.metaKey)
                {
                    var zAngle = this._mouseSpeed * (pt1.y - this.downPoint.y) * Math.PI / 180.0;
                    if (zAngle === 0)
                    {
                        zAngle = 0.01 * Math.PI / 180.0;
                    }
                    if(event.shiftKey)
                    {
                        var f = Math.floor(zAngle/(Math.PI/4));
                        zAngle = f*Math.PI/4;
                    }
                    mat = Matrix.RotationZ(zAngle);
                }
                else
                {
                    var yAngle = this._mouseSpeed * (pt1.x - this.downPoint.x) * Math.PI / 180.0;
                    var xAngle = -this._mouseSpeed * (pt1.y - this.downPoint.y) * Math.PI / 180.0;
                    if(event.shiftKey)
                    {
                        var f = Math.floor(yAngle/(Math.PI/4));
                        yAngle = f*Math.PI/4;
                        f = Math.floor(xAngle/(Math.PI/4));
                        xAngle = f*Math.PI/4;
                    }

                    // check the orientation of the X axis
                    //if (drawUtils.drawYZ)  xAngle = -xAngle;
                    var yMat  = Matrix.RotationY( yAngle ),
                        xMat  = Matrix.RotationX( xAngle );

                    mat = glmat4.multiply(yMat, xMat, []);
                }
            }

            if(this._inLocalMode && (this._targets.length === 1) )
            {
                this._rotateLocally(mat);
            }
            else
            {
                this._rotateGlobally(mat);
            }

//            this.UpdateSelection();
            NJevent("elementChanging", {type : "Changing", redraw: false});
        }
    },

    _rotateLocally: {
        value: function (rotMat) {
            var len = this._targets.length;
            for(var i = 0; i < len; i++)
            {
                var item = this._targets[i];
                var elt = item.elt;
                var curMat = item.mat;

                // pre-translate by the transformation center
                var tMat = Matrix.I(4);

                var transformCtr = this._startOriginArray[i];

                tMat[12] = transformCtr[0];
                tMat[13] = transformCtr[1];
                tMat[14] = transformCtr[2];

                var mat = glmat4.multiply(curMat, tMat, []);

                // translate back
                tMat[12] = -transformCtr[0];
                tMat[13] = -transformCtr[1];
                tMat[14] = -transformCtr[2];

                glmat4.multiply(mat, rotMat, mat);


                glmat4.multiply(mat, tMat, mat);

                // while moving, set inline style to improve performance
                viewUtils.setMatrixForElement( elt, mat, true );
            }
        }
    },

    _rotateGlobally: {
        value: function (rotMat) {
            var len = this._targets.length;
            for(var i = 0; i < len; i++)
            {
                var item = this._targets[i];
                var elt = item.elt;
                var curMat = item.mat;

                // pre-translate by the transformation center
                var tMat = Matrix.I(4);

                var transformCtr = this._startOriginArray[i].slice(0);
                transformCtr = MathUtils.transformPoint(transformCtr, curMat);

                tMat[12] = transformCtr[0];
                tMat[13] = transformCtr[1];
                tMat[14] = transformCtr[2];

                var mat = glmat4.multiply(tMat, rotMat, []);

                // translate back
                tMat[12] = -transformCtr[0];
                tMat[13] = -transformCtr[1];
                tMat[14] = -transformCtr[2];

                glmat4.multiply(mat, tMat, mat);

                glmat4.multiply(mat, curMat, mat);

                viewUtils.setMatrixForElement( elt, mat, true );
            }
        }
    },

    _getAngleToRotate: {
        value: function (x, y) {
            var angle;
            var pt = MathUtils.getLocalPoint(x, y, this._planeEq, this._matL);
            if(!pt)
            {
                //TODO - should this be _startPoint.x/y instead of downPoint.x/y?
                var st = [this.downPoint.x, this.downPoint.y];
                pt = [x, y];
                var sub = vecUtils.vecSubtract(2, pt, st);
                var dot = vecUtils.vecDot(2, sub, this._dirVecL);

                angle = vecUtils.vecDist(2, pt, st) * 0.1;

                if (dot < 0)
                {
                    angle = -angle;
                }
            }
            else
            {
                angle = MathUtils.getAngleBetweenPoints(this._startPoint, pt);
            }
            return angle;
        }
    },

    captureSelectionChange: {
        value: function(event){
            this.eventManager.addEventListener("selectionDrawn", this, true);
        }
    },

    captureSelectionDrawn: {
        value: function(event){
            this._origin = null;
            this._targets = [];
            this._startOriginArray = null;

            var len = this.application.ninja.selectedElements.length;
            if(len)
            {
                if(len === 1)
                {
                    this.target = this.application.ninja.selectedElements[0]._element;
                    drawUtils.addElement(this.target);

                    viewUtils.pushViewportObj( this.target );
                    var eltCtr = viewUtils.getCenterOfProjection();
                    viewUtils.popViewportObj();

                    var ctrOffset = this.target.elementModel.props3D.m_transformCtr;
                    if(ctrOffset)
                    {
                        eltCtr[2] = 0;
                        eltCtr = vecUtils.vecAdd(3, eltCtr, ctrOffset);
                    }
                    
                    this._origin = viewUtils.localToGlobal(eltCtr, this.target);
                    this._updateTargets();
                    this._setTransformOrigin(false);
                }
                else
                {
                    this.target = this.application.ninja.currentDocument.documentRoot;
                    this._origin = drawUtils._selectionCtr.slice(0);
                    this._origin[0] += this.application.ninja.stage.userContentLeft;
                    this._origin[1] += this.application.ninja.stage.userContentTop;
                    this._updateTargets();
                    this._setTransformOrigin(true);
                }
            }
            else
            {
                this.target = null;
            }
            this.DrawHandles();

            if(event)
            {
                this.eventManager.removeEventListener("selectionDrawn", this, true);
            }
        }
    },

    _updateTargets: {
        value: function(addToUndoStack) {
            var newStyles = [],
                previousStyles = [],
                len = this.application.ninja.selectedElements.length;
            this._targets = [];
            for(var i = 0; i < len; i++)
            {
                var elt = this.application.ninja.selectedElements[i]._element;
//                this._initProps3D(elt);


                var curMat = viewUtils.getMatrixFromElement(elt);
                var curMatInv = glmat4.inverse(curMat, []);

                viewUtils.pushViewportObj( elt );
                var eltCtr = viewUtils.getCenterOfProjection();
                viewUtils.popViewportObj();

                eltCtr = viewUtils.localToGlobal(eltCtr, elt);

                this._targets.push({elt:elt, mat:curMat, matInv:curMatInv, ctr:eltCtr});
                if(addToUndoStack)
                {
                    var previousStyleStr = {dist:this._undoArray[i].dist, mat:MathUtils.scientificToDecimal(this._undoArray[i].mat.slice(0), 5)};

                    var newStyleStr = {dist:viewUtils.getPerspectiveDistFromElement(elt), mat:MathUtils.scientificToDecimal(curMat, 5)};

                    previousStyles.push(previousStyleStr);
                    newStyles.push(newStyleStr);
                }
            }
            if(addToUndoStack)
            {
                ElementsMediator.set3DProperties(this.application.ninja.selectedElements,
                                                newStyles,
                                                "Change",
                                                "rotateTool",
                                                previousStyles
                                              );
            }
            // Save previous value for undo/redo
            this._undoArray = [];
            for(i = 0, len = this._targets.length; i < len; i++)
            {
                var elt = this._targets[i].elt;
                var _mat = viewUtils.getMatrixFromElement(elt);
                var _dist = viewUtils.getPerspectiveDistFromElement(elt);
                this._undoArray.push({mat:_mat, dist:_dist});
            }
        }
    },

    _setTransformOrigin: {
        value: function(shouldUpdateCenter) {
            if(!this._origin)
            {
                return;
            }
            var len = this._targets.length;
            var elt,
                eltCtr,
                ctrOffset,
                matInv;
            if( len === 1)
            {
                elt = this._target;

                if(shouldUpdateCenter)
                {
                    eltCtr = this._targets[0].ctr;
                    ctrOffset = vecUtils.vecSubtract(3, this._origin, eltCtr);

                    matInv = this._targets[0].matInv;
                    ctrOffset = MathUtils.transformVector(ctrOffset, matInv);

                    elt.elementModel.props3D.m_transformCtr = ctrOffset;
                }
                else
                {
                    this._startOriginArray = [];
                    var ctrOffset = this._target.elementModel.props3D.m_transformCtr;
                    if(!ctrOffset)
                    {
                        ctrOffset = [0,0,0];
                    }
                }
                this._startOriginArray[0] = ctrOffset;
            }
            else
            {
                // Update transform ctr for all elements if transform origin was modified
                this._startOriginArray = [];
                for (var i = 0; i < len; i++) {
                    elt = this._targets[i].elt;
                    eltCtr = this._targets[i].ctr;
                    ctrOffset = vecUtils.vecSubtract(3, this._origin, eltCtr);
                    matInv = this._targets[i].matInv;
                    ctrOffset = MathUtils.transformVector(ctrOffset, matInv);

                    this._startOriginArray[i] = ctrOffset;
                }
            }
        }
    },

	HandleDoubleClick: {
		value: function () {

            if(!this._target)
            {
                return;
            }

            if(this._activateOriginHandle)
            {
                var len = this.application.ninja.selectedElements.length;
                if( (len === 1) || (this._toolID === "rotateStage3DTool") )
                {
                    this._target.elementModel.props3D.m_transformCtr = null;
                }

                this._handleMode = null;
                this._activateOriginHandle = false;
                this.application.ninja.stage.drawingCanvas.style.cursor = this._cursor;

                this.captureSelectionDrawn(null);
            }
		}
	},

    Reset : {
        value : function()
        {
            var item,
                elt,
                mat,
                dist,
                newStyles = [],
                previousStyles = [],
                len = this._targets.length,
                iMat;
            for(var i = 0; i < len; i++)
            {
                // Reset to the identity matrix
                item = this._targets[i];
                iMat = Matrix.I(4);
                mat = item.mat;
//                iMat[12] = mat[12];
//                iMat[13] = mat[13];
//                iMat[14] = mat[14];

                dist = this._undoArray[i].dist;

                var previousStyleStr = {dist:dist, mat:mat};

                var newStyleStr = {dist:dist, mat:iMat};

                previousStyles.push(previousStyleStr);
                newStyles.push(newStyleStr);

            }

            ElementsMediator.set3DProperties(this.application.ninja.selectedElements,
                                            newStyles,
                                            "Change",
                                            "rotateTool",
                                            previousStyles
                                          );

            this.isDrawing = false;
            this.endDraw(event);

//			this.UpdateSelection(true);
            this.Configure(true);
        }
    },

        /**
    * SHIFT/ALT/SPACE Key Handlers
    */
    HandleShiftKeyDown: {
        value: function (event) {
        }
    },

    HandleShiftKeyUp: {
        value: function () {
        }
    },

    HandleSpaceKeyDown: {
        value: function () {
        }
    },

    HandleSpaceUp: {
        value: function () {
        }
    },

    HandleAltKeyDown: {
		value: function(event) {
            this._inLocalMode = !this._inLocalMode;
            this.DrawHandles();
		}
	},

	HandleAltKeyUp: {
		value: function(event) {
			this._inLocalMode = !this._inLocalMode;
            this.DrawHandles();
		}
	},

    handleScroll: {
        value: function(event) {
            this.captureSelectionDrawn(null);
        }
    },

    /**
     * This function is for specifying custom feedback routine
     * upon mouse over.
     * For example, the drawing tools will add a glow when mousing
     * over existing canvas elements to signal to the user that
     * the drawing operation will act on the targeted canvas.
     */
    _showFeedbackOnMouseMove : {
        value: function (event) {
            if(this._target && this._handles)
            {
                var len = this._handles.length;
                var i = 0,
                    toolHandle,
                    c,
                    point = webkitConvertPointFromPageToNode(this.application.ninja.stage.canvas,
                                                            new WebKitPoint(event.pageX, event.pageY));
                for (i=0; i<len; i++)
                {
                    toolHandle = this._handles[i];
                    c = toolHandle.collidesWithPoint(point.x, point.y);
                    if(c === 1)
                    {
                        this.application.ninja.stage.drawingCanvas.style.cursor = "move";
                        this._handleMode = i;
                        this._activateOriginHandle = true;
                        return;
                    }
                    else if(c === 2)
                    {
                        this.application.ninja.stage.drawingCanvas.style.cursor = toolHandle._cursor;
                        this._handleMode = i;
                        this._activateOriginHandle = false;
                        return;
                    }
                }
            }

            this._handleMode = null;
            this._activateOriginHandle = false;
   //            this.application.ninja.stage.drawingCanvas.style.cursor = this._cursor;
            this.application.ninja.stage.drawingCanvas.style.cursor = "auto";
        }
    },

    DrawHandles: {
        value: function (angle) {
            this.application.ninja.stage.clearDrawingCanvas();

            var item = this._target;
            if(!item)
            {
                return;
            }

            // Draw tool handles

            // set the element to be the viewport object - temporarily
            var lMode = this._inLocalMode;
            if( (this._toolID !== "rotateStage3DTool") &&
                    (this.application.ninja.selectedElements.length === 1) )
            {
                viewUtils.pushViewportObj( item );
            }
            else
            {
                lMode = false;
                viewUtils.pushViewportObj( this.application.ninja.currentDocument.documentRoot );
            }
            var base = this._origin;

            if( (this._handleMode !== null) && !this._activateOriginHandle )
            {
                switch(this._handleMode)
                {
                    case 0:
                        this._handles[1]._strokeStyle = 'rgba(0, 255, 0, 0.2)';
                        this._handles[2]._strokeStyle = 'rgba(0, 0, 255, 0.2)';
                        break;
                    case 1:
                        this._handles[0]._strokeStyle = 'rgba(255, 0, 0, 0.2)';
                        this._handles[2]._strokeStyle = 'rgba(0, 0, 255, 0.2)';
                        break;
                    case 2:
                        this._handles[0]._strokeStyle = 'rgba(255, 0, 0, 0.2)';
                        this._handles[1]._strokeStyle = 'rgba(0, 255, 0, 0.2)';
                        break;
                }
            }
            this._handles[0].draw(base, item, lMode);
            this._handles[1].draw(base, item, lMode);
            this._handles[2].draw(base, item, lMode);

            if(angle && (this._handleMode !== null))
            {
                this._handles[this._handleMode].drawShadedAngle(angle, this._startPoint);
            }


            this._handles[0]._strokeStyle = 'rgba(255, 0, 0, 1)';
            this._handles[1]._strokeStyle = 'rgba(0, 255, 0, 1)';
            this._handles[2]._strokeStyle = 'rgba(0, 0, 255, 1)';

            viewUtils.popViewportObj();
        }
    }

});