/* <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> */

/**
@module js/document/documentManager
@requires montage/core/core
@requires montage/ui/component
*/
var Montage = require("montage/core/core").Montage,
    Component = require("montage/ui/component").Component,
    viewUtils = require("js/helper-classes/3D/view-utils").ViewUtils,
    drawUtils = require("js/helper-classes/3D/draw-utils").DrawUtils,
    vecUtils = require("js/helper-classes/3D/vec-utils").VecUtils;

exports.Layout = Montage.create(Component, {

    canvas: {
        value: null,
        serializable: true
    },

    stage: {
        value: null,
        serializable: true
    },

    ctx: { value: null },

    drawFillColor: { value: 'rgba(255,255,255,1)' },
    ctxLineWidth: { value: 0.2 },

    _currentDocument: {
        value : null,
        enumerable : false
    },

    currentDocument : {
        get : function() {
            return this._currentDocument;
        },
        set : function(value) {
            if (value === this._currentDocument) {// || value.getProperty("currentView") !== "design") {
                return;
            }

            this._currentDocument = value;

            if(!value) {

            } else if(this._currentDocument.currentView === "design") {
                this.elementsToDraw = this._currentDocument.model.documentRoot.childNodes;
            }
        }
    },

    _layoutView: {
        value: "layoutAll"
    },

    layoutView: {
        get: function() {
            return this._layoutView;
        },
        set: function(value) {
            if(this._layoutView !== value) {
                this._layoutView = value;
                this.draw();
            }
        }
    },

    domTree: {
        value: []
    },

    elementsToDraw: {
        value: []
    },

    deserializedFromTemplate: {
        value: function() {
            this.ctx = this.canvas.getContext("2d");
            this.ctx.lineWidth = this.ctxLineWidth;
            this.ctx.fillStyle = this.drawFillColor;

            this.eventManager.addEventListener("selectionChange", this, false);
            this.eventManager.addEventListener("elementsRemoved", this, false);
        }
    },

    // Redraw stage only once after all deletion is completed
    handleElementsRemoved: {
        value: function(event) {
            this.draw();
            this.draw3DInfo(false);
        }
    },

    handleSelectionChange: {
        value: function(event) {
            var containerIndex;

            if(this.currentDocument === null){
                return;
            }

            if(this.currentDocument.currentView === "design"){
                // Make an array copy of the line node list which is not an array like object
                this.domTree = this.application.ninja.currentDocument.model.views.design.getLiveNodeList(true);
                // Index of the current container
                containerIndex = this.domTree.indexOf(this.currentDocument.model.domContainer);

                if(containerIndex < 0) {
                    // Stage is the container.
                    this.domTree = Array.prototype.slice.call(this.currentDocument.model.domContainer.childNodes, 0);
                } else {
                    // Child nodes of the container
                    this.domTree = Array.prototype.slice.call(this.domTree[containerIndex].childNodes, 0);
                }
            }
            // Clear the elements to draw
            this.elementsToDraw.length = 0;

            // Draw the non selected elements
            if(!event.detail.isDocument) {
                this.elementsToDraw = this.domTree.filter(function(value) {
                    return (event.detail.elements.indexOf(value) === -1);
                });
            } else {
                this.elementsToDraw = Array.prototype.slice.call(this.domTree, 0);
            }

            this.draw(); // Not a reel yet
            this.draw3DInfo(false);

            // Clear the domTree copy
            this.domTree.length = 0;
        }
    },

    draw: {
        value: function() {
            this.clearCanvas();

            // TODO Bind the layoutview mode to the current document
            // var mode  = this.application.ninja.currentDocument.layoutMode;
            if(this.layoutView === "layoutOff") return;

            var els = this.elementsToDraw.length;
            for(var i = 0, el; i < els; i++){
                this.drawTagOutline(this.elementsToDraw[i]);
            }
        }
    },

    draw3DInfo: {
        value: function(updatePlanes) {
            if(updatePlanes) {
                drawUtils.updatePlanes();
                this.application.ninja.stage.stageDeps.snapManager._isCacheInvalid = true;
            }

            if(this.stage.appModel.show3dGrid) {
                this.application.ninja.stage.stageDeps.snapManager.updateWorkingPlaneFromView();
            }
            drawUtils.drawWorkingPlane();
            drawUtils.draw3DCompass();
        }
    },

    clearCanvas: {
        value: function() {
            this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        }
    },

    drawTagOutline: {
        value: function (item) {

            if(!item || !this.application.ninja.selectionController.isNodeTraversable(item)) return;

            // Don't draw outlines for shapes.
            // TODO Use the element mediator/controller/model to see if its a shape
            // if (utilsModule.utils.isElementAShape(item)) return;


            // draw the layout
            viewUtils.setViewportObj( item );
            var bounds3D = viewUtils.getElementViewBounds3D( item );
            var tmpMat = viewUtils.getLocalToGlobalMatrix( item );

            var zoomFactor = 1;
            if (this.stage._viewport && this.stage._viewport.style && this.stage._viewport.style.zoom) {
				zoomFactor = Number(this.stage._viewport.style.zoom);
            }

            var sSL = this.stage._scrollLeft;
            var sST = this.stage._scrollTop;

            for (var j=0;  j<4;  j++) {
                var localPt = bounds3D[j];
                var tmpPt = viewUtils.localToGlobal2(localPt, tmpMat);

                if(zoomFactor !== 1) {
                    tmpPt = vecUtils.vecScale(3, tmpPt, zoomFactor);

                    tmpPt[0] += sSL*(zoomFactor - 1);
                    tmpPt[1] += sST*(zoomFactor - 1);
                }
                bounds3D[j] = tmpPt;
            }

            if(item.uuid === this.currentDocument.model.domContainer.uuid) {
                this.ctx.save();
                this.ctx.strokeStyle = "#C61F00";

                this.ctx.beginPath();

                this.ctx.moveTo( bounds3D[3][0] + 0.5 ,  bounds3D[3][1] - 0.5 );

                this.ctx.lineTo( bounds3D[0][0] - 0.5 ,  bounds3D[0][1] - 0.5 );
                this.ctx.lineTo( bounds3D[1][0] - 0.5 ,  bounds3D[1][1] + 0.5 );
                this.ctx.lineTo( bounds3D[2][0] + 0.5  ,  bounds3D[2][1] + 0.5 );
                this.ctx.lineTo( bounds3D[3][0] + 0.5  ,  bounds3D[3][1] + 0.5 );

                this.ctx.closePath();
                this.ctx.stroke();

                this.ctx.restore();
            } else {
                // Draw the Item ouline
                this._dashedLine(bounds3D[3][0] - 0.5,bounds3D[3][1]- 0.5,bounds3D[0][0] + 2.5, bounds3D[0][1] - 0.5,[5,5]);
                this._dashedLine(bounds3D[0][0] - 0.5, bounds3D[0][1] - 0.5, bounds3D[1][0]- 0.5, bounds3D[1][1] + 0.5, [5,5] );
                this._dashedLine(bounds3D[1][0] - 0.5 , bounds3D[1][1] + 0.5, bounds3D[2][0]+ 0.5, bounds3D[2][1] + 0.5, [5,5] );
                this._dashedLine(bounds3D[2][0] + 0.5, bounds3D[2][1] + 0.5, bounds3D[3][0] + 0.5, bounds3D[3][1] - 0.5, [5,5] );
            }

            // Draw the Label is all mode
            if(this.layoutView === "layoutAll") {
                this.ctx.strokeStyle = 'rgba(0,0,0,1)'; // Black Stroke
                this.ctx.strokeRect(bounds3D[0][0]+5.5, bounds3D[0][1]-15.5, 70, 11);
                this.ctx.fillStyle = 'rgba(255,255,255,1)'; // White Fill
                this.ctx.fillRect(bounds3D[0][0]+6, bounds3D[0][1]-15, 69, 10);

                this.ctx.fillStyle = 'rgba(0,0,0,1)';
                this.ctx.font = "9px Droid Sans";

                this.ctx.fillText(this._elementName(item), bounds3D[0][0] + 8, bounds3D[0][1] - 7);
            }
        }
    },

    /**
     * redrawDocument: Redraws the outline for the entire document
     */
    redrawDocument: {
        value: function() {
            if(this.application.ninja.currentDocument) {
                this.clearCanvas();
                this.WalkDOM(this.application.ninja.currentDocument.model.documentRoot);

                //drawUtils.updatePlanes();
                //if(this.application.ninja.currentDocument.draw3DGrid) drawUtils.drawWorkingPlane();
                //drawUtils.draw3DCompass();
            }
        }
    },

    drawElementsOutline: {
        value: function(elements) {
            this.clearCanvas();
            this.WalkDOM(this.application.ninja.currentDocument.model.documentRoot, elements);
        }
    },

    WalkDOM: {
        value: function(element, excludeArray) {
            if(!element)
            {
                return;
            }
            
            try {
                if(element.nodeType == 1 && this.application.ninja.currentDocument.inExclusion(element) === -1 ) {

                    if(excludeArray) {
                        var found = false;
                        for(var j=0, elt; elt = excludeArray[j]; j++) {

                            if(elt.uuid === element.uuid) {
                                found = true;
                            }
                        }

                        if(!found) {
                            this.drawTagOutline(element);
                        }
                    } else {
                        this.drawTagOutline(element);
                    }

                }

                if(element.elementModel && element.elementModel.isComponent) {
                    this.WalkDOM(element.nextSibling, excludeArray);
                } else {
                    this.WalkDOM(element.firstChild, excludeArray);
                    this.WalkDOM(element.nextSibling, excludeArray);
                }
            } catch (err) {
                console.log(err);
            }
        }
    },

    // Dashed line function found at http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas/
    // Portions used with permission of Gavin Kistner (phrogz)
    _dashedLine: {
        value: function(x, y, x2, y2, dashArray) {
            this.ctx.lineCap = "square";
            this.ctx.beginPath();

            if(! dashArray) dashArray=[10,5];
            var dashCount = dashArray.length;
            var dx = (x2 - x);
            var dy = (y2 - y);
            var xSlope = (Math.abs(dx) > Math.abs(dy));
            var slope = (xSlope) ? dy / dx : dx / dy;

            this.ctx.moveTo(x, y);
            var distRemaining = Math.sqrt(dx * dx + dy * dy);
            var dashIndex = 0;
            while(distRemaining >= 0.1){
                var dashLength = Math.min(distRemaining, dashArray[dashIndex % dashCount]);
                var step = Math.sqrt(dashLength * dashLength / (1 + slope * slope));
                if(xSlope){
                    if(dx < 0) step = -step;
                    x += step;
                    y += slope * step;
                }else{
                    if(dy < 0) step = -step;
                    x += slope * step;
                    y += step;
                }

                this.ctx[(dashIndex % 2 == 0) ? 'lineTo' : 'moveTo'](x, y);
                distRemaining -= dashLength;
                dashIndex++;
            }

            this.ctx.closePath();
            this.ctx.stroke();
        }
    },

    _elementName: {
        value: function(item) {
            if(item.elementModel && item.elementModel.hasOwnProperty("selection")) {
                return item.elementModel['selection'];
            } else {
                return "";
            }
        }
    }

    
});