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

var Montage = 	require("montage/core/core").Montage,
    Component = require("montage/ui/component").Component,
    drawUtils = require("js/helper-classes/3D/draw-utils").DrawUtils,
    vecUtils = 	require("js/helper-classes/3D/vec-utils").VecUtils;

exports.Stage = Montage.create(Component, {

    // TODO - Need to figure out how to remove this dependency
    // Needed by some tools that depend on selectionDrawn event to set up some logic
    drawNow: { value : false },

    // TO REVIEW
    zoomFactor: {value : 1 },

    _canvasSelectionPrefs:  { value: { "thickness" : 1.0, "color" : "#46a1ff" } },
    _canvasDrawingPrefs:    { value: { "thickness" : 1.0, "color" : "#000" } },
    drawingContextPreferences: { get: function() { return this._canvasDrawingPrefs; } },

    _iframeContainer: { value: null },

    _scrollFlag: {value: true, writable: true},
    outFlag: { value: false, writable: true },

    _resizeCanvases: { value: true },

	viewUtils: {
		get: function()  {  return this.stageDeps.viewUtils;  }
	},

	snapManager: {
		get: function()  {  return this.stageDeps.snapManager;  }
	},

    drawUtils: {
        get: function()  {  return this.stageDeps.drawUtils;  }
    },

    resizeCanvases: {
        get: function() {
            return this._resizeCanvases;
        },
        set: function(value) {
            this._resizeCanvases = value;
            if(value) {
                this.needsDraw = true;
            }
        }
    },

    _updatedStage: { value: false },

    updatedStage: {
        get: function() {
            return this._updatedStage;
        },
        set: function(value) {
            this._updatedStage = value;
            if(value) {
                this.needsDraw = true;
            }
        }
    },

    _currentDocumentStageView: {
        value: "front"
    },

    currentDocumentStageView: {
        get: function() {
            return this._currentDocumentStageView;
        },
        set: function(value) {
            if(this._currentDocumentStageView !== value) {
                this._currentDocumentStageView = value;
                this.setStageView(this.currentDocumentStageView);
            }
        }
    },

    contextMenu: {
        value: false
    },

    /** MAIN CANVASES **/
    _canvas: { value: null },   // selection bounds, 3d normals and the overall 3d selection box use this canvas
    canvas: { get: function() { return this._canvas; } },

    _context: { value: null },
    context: { get: function() { return this._context; } },

    _layoutCanvas: { value: null },
    layoutCanvas: { get: function() { return this._layoutCanvas; } },

    _gridCanvas: { value: null },
    gridCanvas: { get: function() { return this._gridCanvas; } },

    _gridContext: { value: null },
    gridContext: { get: function() { return this._gridContext; } },

    _drawingCanvas: { value: null },
    drawingCanvas: { get: function() { return this._drawingCanvas; } },

    _drawingContext: { value: null },
    drawingContext: { get: function() { return this._drawingContext; } },

    _clickPoint: { value: { x: { value: null }, y: { value: null } } },

    // We will set this to false while moving objects to improve performance
    showSelectionBounds: { value: true },

    _viewport:              { value: null },
    _documentOffsetLeft:    { value: 0 },
    _documentOffsetTop:     { value: 0 },
    _scrollLeft:            { value: 0 },
    _scrollTop:             { value: 0 },
    _userContentLeft:       { value: 0 },
    _userContentTop:        { value: 0 },
    _userContentBorder:     { value: 0 },

    viewport: {
        get: function () { return this._viewport; },
        set: function(value) { this._viewport = value; }
    },

    documentOffsetLeft: {
        get: function() { return this._documentOffsetLeft; },
        set: function(value) { this._documentOffsetLeft = value; }
    },

    documentOffsetTop: {
        get: function() { return this._documentOffsetTop },
        set: function(value) { this._documentOffsetTop = value; }
    },

    scrollLeft: {
        get: function() { return this._scrollLeft; }
    },

    scrollTop: {
        get: function() { return this._scrollTop; }
    },

    userContentLeft: {
        get: function() { return this._userContentLeft; },
        set: function(value) { this._userContentLeft = value;}
    },

    userContentTop: {
        get: function() { return this._userContentTop; },
        set: function(value) { this._userContentTop = value;}
    },

    userContentBorder: {
        get: function() { return this._userContentBorder; },
        set: function(value) { this._userContentBorder = value; }
    },

    _currentDocument: {
        value : null
    },

    currentDocument : {
        get : function() {
            return this._currentDocument;
        },
        set : function(value) {
            if (value === this._currentDocument) {
                return;
            }

            if(!this._currentDocument && value.currentView === "design") {
                this.showRulers();
                this.hideCanvas(false);
            }

            this._currentDocument = value;

            if(!value) {
                this.hideRulers();
                this.hideCanvas(true);
            } else if(this._currentDocument.currentView === "design") {
                this.clearAllCanvas();
                this.initWithDocument(false);
            }
        }
        },

    _userPaddingLeft: { value: 0 },
    _userPaddingTop: { value: 0 },

    userPaddingLeft: {
        get: function() { return this._userPaddingLeft; },
        set: function(value) {
            this._userPaddingLeft = value;
            this._documentOffsetLeft = -value;
            this.application.ninja.currentDocument.model.documentRoot.ownerDocument.getElementsByTagName("HTML")[0].style["padding-left"] = -value + "px";
            this.userContentLeft = this._documentOffsetLeft - this._scrollLeft;
            this.updatedStage = true;
        }
    },

    userPaddingTop: {
        get: function() { return this._userPaddingTop; },
        set: function(value) {
            this._userPaddingTop = value;
            this._documentOffsetTop = -value;
            this.application.ninja.currentDocument.model.documentRoot.ownerDocument.getElementsByTagName("HTML")[0].style["padding-top"] = -value + "px";
            this.userContentTop = this._documentOffsetTop - this._scrollTop;
            this.updatedStage = true;
        }
    },

    willDraw: {
        value: function() {
            if(this.resizeCanvases) {
                // TODO GET THE SCROLL SIZE FROM THE CSS -- 11 px
                this._canvas.width = this._layoutCanvas.width = this._drawingCanvas.width = this._gridCanvas.width = this.element.offsetWidth - 11 ;
                this._canvas.height = this._layoutCanvas.height = this._drawingCanvas.height = this._gridCanvas.height = this.element.offsetHeight - 11;// - 26 - 26;

                // Hack for now until a full component
                this.layout.draw();
                if(this.application.ninja.currentDocument) {
                    this.layout.draw3DInfo(true);
                }
            } else if(this.updatedStage) {
                this.layout.draw();
                this.layout.draw3DInfo(true);
            }
        }
    },

    didDraw: {
        value: function() {
            this.resizeCanvases = false;
            this.updatedStage = false;
        }
    },

    prepareForDraw: {
        value: function() {

            this._context = this._canvas.getContext("2d");
            this._drawingContext= this._drawingCanvas.getContext("2d");
            this._gridContext= this._gridCanvas.getContext("2d");

            // Setup event listeners
            this._drawingCanvas.addEventListener("mousedown", this, false);
            this._drawingCanvas.addEventListener("mouseup", this, false);
            this._drawingCanvas.addEventListener("dblclick", this, false);
            this._drawingCanvas.addEventListener("mousewheel", this, false);

            // Hide the canvas
            this.hideCanvas(true);

            this.eventManager.addEventListener( "appMouseUp", this, false);


            this.eventManager.addEventListener( "openDocument", this, false);
            this.eventManager.addEventListener( "switchDocument", this, false);
            this.eventManager.addEventListener( "enableStageMove", this, false);
            this.eventManager.addEventListener( "disableStageMove", this, false);

            this.eventManager.addEventListener( "selectionChange", this, false);
            this.eventManager.addEventListener( "elementChanging", this, false);
            this.eventManager.addEventListener( "elementChange", this, false);

        }
    },

    // Event details will contain the active document prior to opening a new one
    handleOpenDocument: {
        value: function(evt) {
            this.initWithDocument();
        }
    },

    handleSwitchDocument: {
        value: function(evt) {
            this.initWithDocument(true);
        }
    },

    initWithDocument: {
        value: function(didSwitch) {
            var designView = this.application.ninja.currentDocument.model.views.design;

            // Recalculate the canvas sizes because of splitter resizing
            this._canvas.width = this._layoutCanvas.width = this._drawingCanvas.width = this._gridCanvas.width = this.element.offsetWidth - 11 ;
            this._canvas.height = this._layoutCanvas.height = this._drawingCanvas.height = this._gridCanvas.height = this.element.offsetHeight - 11;

            designView.iframe.contentWindow.addEventListener("scroll", this, false);

            this.addPropertyChangeListener("appModel.show3dGrid", this, false);

            this._userPaddingLeft = 0;
            this._userPaddingTop = 0;

            this._documentOffsetLeft = 0;
            this._documentOffsetTop  = 0;

            this._userContentLeft = 0;
            this._userContentTop = 0;

            this._scrollLeft = 0;
            this._scrollTop = 0;

            this.initialize3DOnOpenDocument();

            if(designView._template) {
                var initialLeft = parseInt((this.canvas.width - designView._template.size.width)/2);
                var initialTop = parseInt((this.canvas.height - designView._template.size.height)/2);
                if(initialLeft > this.documentOffsetLeft) {
                    this.userPaddingLeft = -initialLeft;
                }
                if(initialTop > this.documentOffsetTop) {
                    this.userPaddingTop = -initialTop;
                }
            }

            if(didSwitch) {
                this.application.ninja.currentDocument.model.views.design.document.body.scrollLeft = this.application.ninja.currentDocument.model.scrollLeft;
                this.application.ninja.currentDocument.model.views.design.document.body.scrollTop = this.application.ninja.currentDocument.model.scrollTop;
                this.handleScroll();
            } else {
                this.centerStage();
            }

            // TODO - We will need to modify this once we support switching between multiple documents
            this.application.ninja.toolsData.selectedToolInstance._configure(true);
        }
    },

    /**
    * Event handler for the change @ 3DGrid
    */
    handleChange: {
        value: function(notification) {
            if("appModel.show3dGrid" === notification.currentPropertyPath) {
                if(this.appModel.show3dGrid) {

                    drawUtils.drawXY = true;
                    this.stageDeps.snapManager.updateWorkingPlaneFromView();
                    this.updatedStage = true;

                } else {

                    drawUtils.drawXY = false;
                    drawUtils.drawYZ = false;
                    drawUtils.drawXZ = false;
                    this.updatedStage = true;
                }
            }
        }
    },

    enableMouseInOut: {
        value: function() {
            this._drawingCanvas.addEventListener("mouseout", this, false);
            this._drawingCanvas.addEventListener("mouseover", this, false);
        }
    },

    disableMouseInOut: {
        value: function() {
            this._drawingCanvas.removeEventListener("mouseout", this, false);
            this._drawingCanvas.removeEventListener("mouseover", this, false);
        }
    },

    handleMouseout: {
        value: function(event) {
            this.outFlag = true;
        }
    },

    handleMouseover : {
        value: function(event) {
            this.outFlag = false;
        }
    },

    handleMousedown: {
        value: function(event) {
            // Call the focus manager to set focus to blur any focus'd elements
            this.focusManager.setFocus();


            var point;
//            event.preventDefault();   // commenting because HTML elements in the IDE are retaining focus
            // If right click set the context menu to true to prevent a mouse up.
            if (event._event.button == 2) {
                this.contextMenu = true;
                return;
            }

            point = webkitConvertPointFromPageToNode(this.canvas, new WebKitPoint(event.pageX, event.pageY));


            this._clickPoint.x = point.x; // event.layerX;
            this._clickPoint.y = point.y; // event.layerY;

            this.enableMouseInOut();
            
            this.application.ninja.toolsData.selectedToolInstance.downPoint.x = point.x;
            this.application.ninja.toolsData.selectedToolInstance.downPoint.y = point.y;
            this.application.ninja.toolsData.selectedToolInstance.HandleLeftButtonDown(event);

        }
    },

    handleMouseup: {
        value: function(event) {
            // If the mouse up comes from dismissing the context menu return
            if(this.contextMenu) {
                this.contextMenu = false;
                return;
            }

            //this.disableMouseInOut();

            this.application.ninja.toolsData.selectedToolInstance.HandleLeftButtonUp(event);

        }
    },

    handleDblclick: {
        value: function(event) {
            event.preventDefault();
            this.application.ninja.toolsData.selectedToolInstance.HandleDoubleClick(event);
        }
    },

    handleMousewheel: {
        value: function(event) {
            if(event._event.wheelDelta > 0) {
                this.application.ninja.currentDocument.model.views.design.document.body.scrollTop -= 20;
            } else {
                this.application.ninja.currentDocument.model.views.design.document.body.scrollTop += 20;
            }
        }
    },

    /**
     * Enables the MouseMove on Canvas
     */
    handleEnableStageMove: {
        value: function() {
            this._drawingCanvas.addEventListener("mousemove", this, false);
        }
    },

    handleDisableStageMove: {
        value: function() {
            this._drawingCanvas.removeEventListener("mousemove", this, false);
        }
    },

    handleMousemove: {
        value: function(event) {
            this.application.ninja.toolsData.selectedToolInstance.HandleMouseMove(event);
        }
    },

    handleAppMouseUp: {
        value: function(event) {
            if(this.outFlag) {
                if(this.application.ninja.toolsData.selectedToolInstance.isDrawing) {
                    this.application.ninja.toolsData.selectedToolInstance.HandleLeftButtonUp(event);
                }
                this.disableMouseInOut();
                this.outFlag = false;
            }
        }
    },

    handleSelectionChange: {
        value: function(event) {
            // TODO - this is a hack for now because some tools depend on selectionDrawn event for some logic
            if(this.drawNow) {
                this.draw();
                this.drawNow = false;
            } else {
                this.needsDraw = true;
            }
        }
    },

    handleElementChanging: {
        value: function(event) {
            this.needsDraw = true;
        }
    },

    handleElementChange: {
        value: function(event) {
            this.needsDraw = true;
        }
    },

    /**
     Handles the Stage Scroll.
     */
    handleScroll: {
        value: function() {

            this._scrollLeft = this.application.ninja.currentDocument.model.views.design.document.body.scrollLeft;
            this._scrollTop = this.application.ninja.currentDocument.model.views.design.document.body.scrollTop;

            this.userContentLeft = this._documentOffsetLeft - this._scrollLeft;
            this.userContentTop = this._documentOffsetTop - this._scrollTop;

            // Need to clear the snap cache and set up the drag plane
            //snapManager.setupDragPlaneFromPlane( workingPlane );
            this.stageDeps.snapManager._isCacheInvalid = true;
            this.updatedStage = true;
        }
    },


     /**
     * Toggles all Canvas on/off
     */
    hideCanvas: {
        value: function(hide) {

            if(hide) {
                this._canvas.style.visibility = "hidden";
                this._layoutCanvas.style.visibility = "hidden";
                this._drawingCanvas.style.visibility = "hidden";
                this._gridCanvas.style.visibility = "hidden";
            } else {
                this._canvas.style.visibility = "visible";
                this._layoutCanvas.style.visibility = "visible";
                this._drawingCanvas.style.visibility = "visible";
                this._gridCanvas.style.visibility = "visible";
            }
        }
    },

    /**
     * Center the Stage
     */
    centerStage: {
        value: function() {
            var designView = this.application.ninja.currentDocument.model.views.design;
            if(designView._template) {
                designView.document.body.scrollLeft = this._documentOffsetLeft - parseInt((this.canvas.width - designView._template.size.width)/2);
                designView.document.body.scrollTop = this._documentOffsetTop - parseInt((this.canvas.height - designView._template.size.height)/2);
            } else {
                designView.document.body.scrollLeft = this._documentOffsetLeft;
                designView.document.body.scrollTop = this._documentOffsetTop;
            }
            this.handleScroll();
        }
    },

    /**
     * Clears the drawing canvas
     */
    clearDrawingCanvas: {
        value: function() {
            this._drawingContext.clearRect(0, 0, this._drawingCanvas.width, this._drawingCanvas.height);
        }
    },

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

    clearGridCanvas: {
        value: function() {
            this._gridContext.clearRect(0, 0, this._gridCanvas.width, this._gridCanvas.height);
        }
    },

    clearAllCanvas: {
        value: function() {
            this._drawingContext.clearRect(0, 0, this._drawingCanvas.width, this._drawingCanvas.height);
            this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
            this._gridContext.clearRect(0, 0, this._gridCanvas.width, this._gridCanvas.height);
            this.layout.clearCanvas();
        }
    },

    SelectTool: {
        value: function(cursor) {
            this._drawingCanvas.style.cursor = cursor;
            this.clearDrawingCanvas();
        }
    },

    /**
     * GetElement: Returns the element or selectable element under the X,Y coordinates passed as an obj with x,y
     *
     * @param position: mouse event
     * @param selectable (default == null) if true this will return the current container element
     * @return: Returns the element or container or null if the the X,Y hits the exclusion list and tool cannot operate on stage
     */
    getElement: {
        value: function(position, selectable) {
            var point, element,
                docView = this.application.ninja.currentDocument.model.views.design;

            point = webkitConvertPointFromPageToNode(this.canvas, new WebKitPoint(position.pageX - docView.iframe.contentWindow.pageXOffset + this.documentOffsetLeft, position.pageY - docView.iframe.contentWindow.pageYOffset + this.documentOffsetTop));
            element = this.application.ninja.currentDocument.model.views.design.getElementFromPoint(point.x - this.userContentLeft,point.y - this.userContentTop);

            if(!element) debugger;
            // workaround Chrome 3d bug
            if(this.application.ninja.toolsData.selectedToolInstance._canSnap && this.application.ninja.currentDocument.inExclusion(element) !== -1) {
                point = webkitConvertPointFromPageToNode(this.canvas, new WebKitPoint(position.pageX, position.pageY));
                element = this.getElementUsingSnapping(point);
            }

            if(selectable) {

                if(this.application.ninja.currentDocument.inExclusion(element) !== -1) {
                    return this.application.ninja.currentSelectedContainer;
                }

                var activeContainerId = this.application.ninja.currentSelectedContainer.uuid;
                if(element.parentNode.uuid === activeContainerId) {
                    return element;
                } else {
                    var outerElement = element.parentNode;

                    while(outerElement.parentNode && outerElement.parentNode.uuid !== activeContainerId) {
                        // If element is higher up than current container then return
                        if(outerElement.id === "UserContent") return;
                            // else keep going up the chain
                            outerElement = outerElement.parentNode;
                        }

                    return outerElement;
                }

            } else {
                return element;
            }
        }
    },

    /**
     * getElementUsingSnapping: Returns the object at point using snap manager
     *
     * @param: point
     * @return: Returns the Object in the user document under the point
     */
    getElementUsingSnapping: {
        value: function(point) {
            this.stageDeps.snapManager.enableElementSnap( true );
            var hitRec = this.stageDeps.snapManager.snap(point.x, point.y, true);
            this.stageDeps.snapManager.enableElementSnap( this.stageDeps.snapManager.elementSnapEnabledAppLevel() );
            if (hitRec) {
                return hitRec.getElement();
            } else {
                return null;
            }
        }
    },


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

            drawUtils.updatePlanes();

            //TODO Set this variable in the needs draw so that it does not have to be calculated again for each draw for selection change
            if(this.application.ninja.selectedElements.length) {
                // drawUtils.drawSelectionBounds handles the single selection case as well,
                // so we don't have to special-case the single selection case.
                // TODO drawUtils.drawSelectionBounds expects an array of elements.
                // TODO If we use the routine to only draw selection bounds, then we may want to change it
                // TODO to work on _element instead of re-creating a new Array here.
                var selArray = new Array();

                for(var i = 0; this.application.ninja.selectedElements[i];i++) {
                    var curElement = this.application.ninja.selectedElements[i];

                    // Add element to array that is used to calculate 3d-bounding box of all elements
                    selArray.push( curElement );
                    // Draw bounding box around selected item.
                    this.drawElementBoundingBox(curElement);

                    // Draw the element normal
                    drawUtils.drawElementNormal(curElement);
                }


                if(this.showSelectionBounds && this.application.ninja.selectedElements.length > 1) {
                    drawUtils.drawSelectionBounds( selArray );
                }
            }

            NJevent("selectionDrawn");
        }

    },




    /**
     * draw3DSelectionRectangle -- Draws a 3D rectangle used for marquee selection
     *                               Uses the _canvasDrawingPrefs for line thickness and color
     *
     * @params: x, y, w, h
     */
    draw3DSelectionRectangle: {
        value:function(x0,y0, x1,y1, x2,y2, x3,y3) {
//            this.clearCanvas();
            this.clearDrawingCanvas();
            this._drawingContext.strokeStyle = this._canvasDrawingPrefs.color;
            this._drawingContext.lineWidth = this._canvasDrawingPrefs.thickness;

			//this._drawingContext.strokeRect(x,y,w,h);
			this._drawingContext.beginPath();
			this._drawingContext.moveTo( x0 + 0.5, y0 + 0.5 );
			this._drawingContext.lineTo( x1 + 0.5, y1 + 0.5 );
			this._drawingContext.lineTo( x2 + 0.5, y2 + 0.5 );
			this._drawingContext.lineTo( x3 + 0.5, y3 + 0.5 );
			this._drawingContext.lineTo( x0 + 0.5, y0 + 0.5 );

			this._drawingContext.closePath();
			this._drawingContext.stroke();

            this._drawingContext.font = "10px sans-serif";
            this._drawingContext.textAlign = "right";

            // GET USER CONTENT COORDINATES
            var userCoor = this.toUserContentCoordinates(x0, y0);

            var textWidth = this._drawingContext.measureText(userCoor[0]).width;
            this._drawingContext.fillText("X: " + Math.round(userCoor[0]), x0+textWidth+4, y0 - 5);
            this._drawingContext.fillText("Y: " + Math.round(userCoor[1]), x0-5, y0+10);

            // When in 'Shift Mode' there is no Mouse Position for that event
            var txtX, txtY = 0;
            var point = webkitConvertPointFromPageToNode(this.canvas, new WebKitPoint(event.pageX, event.pageY));
            (point.x) ? txtX = point.x : txtX = this.application.ninja.toolsData.selectedToolInstance.downPoint.x;
            (point.y) ? txtY = point.y : txtY = this.application.ninja.toolsData.selectedToolInstance.downPoint.y;


            var h = Math.round(Math.abs(y2-y0));
            var w = Math.round(Math.abs(x2-x0));
            this._drawingContext.fillText("H: " + h, txtX + 38, txtY - 4);
            this._drawingContext.fillText("W: " + w, txtX - 5, txtY + 12);
        }
    },

    /**
     * Draws selection highlight and reg. point for a given element
     */
    drawElementBoundingBox: {
        value: function(elt) {
            this.stageDeps.viewUtils.setViewportObj( elt );
            var bounds3D = this.stageDeps.viewUtils.getElementViewBounds3D( elt );

            // convert the local bounds to the world

//            for (var j=0;  j<4;  j++) {
//                bounds3D[j] = this.localToGlobal(bounds3D, j, elt);
//            }

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

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

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

                    tmpPt[0] += this._scrollLeft*(zoomFactor - 1);
                    tmpPt[1] += this._scrollTop*(zoomFactor - 1);
                }
                bounds3D[j] = tmpPt;
            }

            // draw it
            this.context.strokeStyle = this._canvasSelectionPrefs.color;
            this.context.lineWidth = this._canvasSelectionPrefs.thickness;


            this.context.beginPath();

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

            // This more granular approach lets us specify different gaps for the selection around the element
            this.context.lineTo( bounds3D[0][0] - 0.5 ,  bounds3D[0][1] - 0.5 );
            this.context.lineTo( bounds3D[1][0] - 0.5 ,  bounds3D[1][1] + 0.5 );
            this.context.lineTo( bounds3D[2][0] + 0.5  ,  bounds3D[2][1] + 0.5 );
            this.context.lineTo( bounds3D[3][0] + 0.5  ,  bounds3D[3][1] + 0.5 );

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

    /**
     * draw3DProjectedAndUnprojectedRectangles -- Draws a 3D rectangle used for marquee selection.
     *												Draws a second rectangle to indicate the projected
     *												location of the created geometry
     *												Uses the _canvasDrawingPrefs for line thickness and color
     *
     * @params: x, y, w, h
     */
    draw3DProjectedAndUnprojectedRectangles: {
        value:function(unProjPts,  projPts) {
            this.clearDrawingCanvas();
            this._drawingContext.strokeStyle = this._canvasDrawingPrefs.color;
            this._drawingContext.lineWidth = this._canvasDrawingPrefs.thickness;

			this._drawingContext.beginPath();
			var	x0 = unProjPts[0][0],		y0 = unProjPts[0][1],
				x1 = unProjPts[1][0],		y1 = unProjPts[1][1],
				x2 = unProjPts[2][0],		y2 = unProjPts[2][1],
				x3 = unProjPts[3][0],		y3 = unProjPts[3][1];
			this._drawingContext.moveTo( x0 + 0.5, y0 + 0.5 );
			this._drawingContext.lineTo( x1 + 0.5, y1 + 0.5 );
			this._drawingContext.lineTo( x2 + 0.5, y2 + 0.5 );
			this._drawingContext.lineTo( x3 + 0.5, y3 + 0.5 );
			this._drawingContext.lineTo( x0 + 0.5, y0 + 0.5 );

			this._drawingContext.closePath();
			this._drawingContext.stroke();

            this.stageDeps.snapManager.drawDashedLine( projPts[0], projPts[1], this._drawingContext );
			this.stageDeps.snapManager.drawDashedLine( projPts[1], projPts[2], this._drawingContext );
			this.stageDeps.snapManager.drawDashedLine( projPts[2], projPts[3], this._drawingContext );
			this.stageDeps.snapManager.drawDashedLine( projPts[3], projPts[0], this._drawingContext );

            this._drawingContext.font = "10px sans-serif";
            this._drawingContext.textAlign = "right";

            // GET USER CONTENT COORDINATES
            var userCoor = this.toUserContentCoordinates(x0, y0);

            var textWidth = this._drawingContext.measureText(userCoor[0]).width;
            this._drawingContext.fillText("X: " + Math.round(userCoor[0]), x0+textWidth+4, y0 - 5);
            this._drawingContext.fillText("Y: " + Math.round(userCoor[1]), x0-5, y0+10);

            // When in 'Shift Mode' there is no Mouse Position for that event
            var txtX, txtY = 0;
            var point = webkitConvertPointFromPageToNode(this.canvas, new WebKitPoint(event.pageX, event.pageY));
            (point.x) ? txtX = point.x : txtX = this.application.ninja.toolsData.selectedToolInstance.downPoint.x;
            (point.y) ? txtY = point.y : txtY = this.application.ninja.toolsData.selectedToolInstance.downPoint.y;

            var h = Math.round(Math.abs(y2-y0));
            var w = Math.round(Math.abs(x2-x0));
            this._drawingContext.fillText("H: " + h, txtX + 38, txtY - 4);
            this._drawingContext.fillText("W: " + w, txtX - 5, txtY + 12);
        }
    },

    /**
     * drawLine -- Draws a line in 3D space
     *                               Uses the _canvasDrawingPrefs for line thickness and color
     *
     * @params: x0, y0, x1, y1
     */
    drawLine: {
        value:function(x0, y0, x1, y1, strokeSize, strokeColor) {
            this.clearDrawingCanvas();
            var origStrokeStyle = this._drawingContext.strokeStyle;
            var origLineWidth = this._drawingContext.lineWidth;
            this._drawingContext.strokeStyle = strokeColor;
            this._drawingContext.lineWidth = strokeSize;

            this._drawingContext.beginPath();
            this._drawingContext.moveTo( x0 + 0.5, y0 + 0.5 );
            this._drawingContext.lineTo( x1 + 0.5, y1 + 0.5 );

            this._drawingContext.closePath();
            this._drawingContext.stroke();

            this._drawingContext.font = "10px sans-serif";
            this._drawingContext.textAlign = "right";

            // GET USER CONTENT COORDINATES
            var userCoor = this.toUserContentCoordinates(x0, y0);

            var textWidth = this._drawingContext.measureText(userCoor[0]).width;
            this._drawingContext.fillText("X: " + Math.round(userCoor[0]), x0+textWidth+4, y0 - 5);
            this._drawingContext.fillText("Y: " + Math.round(userCoor[1]), x0-5, y0+10);

            // When in 'Shift Mode' there is no Mouse Position for that event
            var txtX, txtY = 0;
            var point = webkitConvertPointFromPageToNode(this.canvas, new WebKitPoint(event.pageX, event.pageY));
            (point.x) ? txtX = point.x : txtX = this.application.ninja.toolsData.selectedToolInstance.downPoint.x;
            (point.y) ? txtY = point.y : txtY = this.application.ninja.toolsData.selectedToolInstance.downPoint.y;

            var h = Math.round(Math.abs(y1-y0));
            var w = Math.round(Math.abs(x1-x0));
            this._drawingContext.fillText("H: " + h, txtX + 38, txtY - 4);
            this._drawingContext.fillText("W: " + w, txtX - 5, txtY + 12);

            this._drawingContext.strokeStyle = origStrokeStyle;
            this._drawingContext.lineWidth = origLineWidth;
        }
    },

    toUserContentCoordinates: {
        value: function(x,y) {
            return [x - this._userContentLeft, y - this._userContentTop];
        }
    },

    toViewportCoordinates: {
        value: function(x,y) {
            return [x + this._userContentLeft, y + this._userContentTop];
        }
    },

    setStageAsViewport: {
        value: function() {
            this.stageDeps.viewUtils.setViewportObj(this.application.ninja.currentDocument.model.documentRoot);
        }
    },

    setZoom: {
        value: function(value) {
            if(!this._firstDraw)
            {
                var userContent = this.application.ninja.currentDocument.model.documentRoot;
                if (userContent)
                {
                    var w = this._canvas.width,
                        h = this._canvas.height;
					var globalPt = [w/2, h/2, 0];

                    this.stageDeps.viewUtils.setStageZoom( globalPt,  value/100 );

                    //TODO - Maybe move to mediator.
					var newVal = value/100.0;
					if (newVal >= 1)
						this.application.ninja.currentDocument.model.views.design.iframe.style.zoom = value/100;

                    this.updatedStage = true;

                    NJevent( "zoomChange");
                }
            }
        }
    },

	getPlaneForView:
	{
		value: function( side )
		{
			var plane = [0,0,1,0];
            switch(side)
			{
                case "top":
					plane = [0,1,0,0];
 					plane[3] = this.application.ninja.currentDocument.model.documentRoot.offsetHeight / 2.0;
                   break;

                case "side":
					plane = [1,0,0,0];
 					plane[3] = this.application.ninja.currentDocument.model.documentRoot.offsetWidth / 2.0;
                   break;

                case "front":
                    plane = [0,0,1,0];
                    break;

				default:
					console.log( "unrecognized view in snapManager.getPlaneForView: " + side );
					break;
            }

			return plane;
		}
	},

    setStageView: {
        value: function(side) {
            var mat,
                currentDoc = this.application.ninja.currentDocument.model.documentRoot,
                isDrawingGrid = this.application.ninja.appModel.show3dGrid;
            // Stage 3d Props.
            currentDoc.elementModel.props3D.ResetTranslationValues();
            currentDoc.elementModel.props3D.ResetRotationValues();


            switch(side){
                case "top":
                    mat = Matrix.RotationX(Math.PI * 270.0/180.0);
                    drawUtils.drawXY = drawUtils.drawYZ = false;
                    drawUtils.drawXZ = isDrawingGrid;
                    break;

                case "side":
                    mat = Matrix.RotationY(Math.PI * 270/180);
                    drawUtils.drawXY = drawUtils.drawXZ = false;
                    drawUtils.drawYZ = isDrawingGrid;
                    break;

                case "front":
                    mat = Matrix.I(4);
                    drawUtils.drawYZ = drawUtils.drawXZ = false;
                    drawUtils.drawXY = isDrawingGrid;
                    break;
            }
			workingPlane = this.getPlaneForView( side );

            this.stageDeps.viewUtils.setMatrixForElement(currentDoc, mat, false);

            drawUtils.setWorkingPlane(workingPlane);

            // Todo: Figure out where to put the DrawHandles
            //stageManagerModule.stageManager._toolsList.action("DrawHandles");

            this.stageDeps.snapManager.updateWorkingPlaneFromView();
        }
    },

    saveScroll:{
       value: function(){
           this.application.ninja.documentController.activeDocument.savedLeftScroll = this._iframeContainer.scrollLeft;
           this.application.ninja.documentController.activeDocument.savedTopScroll = this._iframeContainer.scrollTop;
       }
    },

    restoreScroll:{
       value: function(){
           this._iframeContainer.scrollLeft = this.application.ninja.documentController.activeDocument.savedLeftScroll;
           this._scrollLeft = this.application.ninja.documentController.activeDocument.savedLeftScroll;
           this._iframeContainer.scrollTop = this.application.ninja.documentController.activeDocument.savedTopScroll;
           this._scrollTop = this.application.ninja.documentController.activeDocument.savedTopScroll;
       }
   },

    showRulers:{
        value:function(){
            this.application.ninja.rulerTop.style.display = "block";
            this.application.ninja.rulerLeft.style.display = "block";
        }
    },
    hideRulers:{
        value:function(){
            this.application.ninja.rulerTop.style.display = "none";
            this.application.ninja.rulerLeft.style.display = "none";
        }
    },
    showCodeViewBar:{
        value:function(isCodeView){
            if(isCodeView === true) {
                this.application.ninja.editorViewOptions.element.style.display = "block";
                this.application.ninja.documentBar.element.style.display = "none";
            } else {
                this.application.ninja.documentBar.element.style.display = "block";
                this.application.ninja.editorViewOptions.element.style.display = "none";
            }
        }
    },

    collapseAllPanels:{
        value:function(){
            this.application.ninja.panelSplitter.collapse();
            this.application.ninja.timelineSplitter.collapse();
            this.application.ninja.toolsSplitter.collapse();
            this.application.ninja.optionsSplitter.collapse();
        }
    },
    restoreAllPanels:{
        value:function(){
            this.application.ninja.panelSplitter.restore();
            this.application.ninja.timelineSplitter.restore();
            this.application.ninja.toolsSplitter.restore();
            this.application.ninja.optionsSplitter.restore();
        }
    },

    initialize3DOnOpenDocument: {
        value: function() {

            workingPlane = [0,0,1,0];

            this.snapManager._isCacheInvalid = true;
            this.snapManager.setupDragPlaneFromPlane (workingPlane);

            this.drawUtils.initializeFromDocument();
        }
    }

});