/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</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, {

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

    // 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 },
    switchedFromCodeDoc: { value : false },

    drawLayout: { value : false },
    updatePlanes: { value : false },
    draw3DInfo: { value : false },

    // TO REVIEW
    zoomFactor: {
        get: function() {
            if (this._viewport && this._viewport.style && this._viewport.style.zoom) {
                return Number(this._viewport.style.zoom);
            } else {
                return 1;
            }
        }
    },

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

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

    _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) {
                // TODO GET THE SCROLL SIZE FROM THE CSS -- 11 px
                this._canvas.width = this._layoutCanvas.width = this._drawingCanvas.width = this._gridCanvas.width = this.bindingView.width = this.element.offsetWidth - 11;
                this._canvas.height = this._layoutCanvas.height = this._drawingCanvas.height = this._gridCanvas.height =  this.bindingView.height = this.element.offsetHeight - 11;// - 26 - 26;

                if(this.currentDocument) {
                    if(this.currentDocument.currentView === "design") {
                        this.drawLayout = true;
                        this.updatePlanes = true;
                        this.draw3DInfo = true;
                        this.needsDrawSelection = true;
                    }
                    if(this.currentDocument.model && this.currentDocument.model.documentRoot) {
                        this.currentDocument.model.documentRoot.elementModel.setProperty("offsetCache", false);
                    }
                }
            }
        }
    },

    _updatedStage: { value: false },

    updatedStage: {
        get: function() {
            return this._updatedStage;
        },
        set: function(value) {
            this._updatedStage = value;
            if(value) {
                if(this.currentDocument) {
                    if(this.currentDocument.currentView === "design") {
                        this.drawLayout = true;
                        this.updatePlanes = true;
                        this.draw3DInfo = true;
                        this.needsDrawSelection = true;
                    }
                    if(this.currentDocument.model && this.currentDocument.model.documentRoot) {
                        this.currentDocument.model.documentRoot.elementModel.setProperty("offsetCache", false);
                    }
                }
            }
        }
    },

    _needsDrawSelection: { value: false },

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

    _needsDrawTool: { value: null },

    needsDrawTool: {
        get: function() {
            return this._needsDrawTool;
        },
        set: function(value) {
            this._needsDrawTool = 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 **/
    // selection bounds, 3d normals and the overall 3d selection box use this canvas
    _canvas: {
        value: null,
        serializable: true
    },

    canvas: {
        get: function() {
            return this._canvas;
        }
    },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    // 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);
            }

            if(this.currentDocument && (this.currentDocument.currentView === "design")) {
                this.currentDocument.model.scrollLeft = this._scrollLeft;
                this.currentDocument.model.scrollTop = this._scrollTop;
                this.currentDocument.model.userPaddingLeft = this._userPaddingLeft;
                this.currentDocument.model.userPaddingTop = this._userPaddingTop;
                this.currentDocument.model.documentOffsetLeft = this._documentOffsetLeft;
                this.currentDocument.model.documentOffsetTop = this._documentOffsetTop;
                this.currentDocument.model.userContentLeft = this._userContentLeft;
                this.currentDocument.model.userContentTop = this._userContentTop;
                this.currentDocument.model.templateLeft = this.templateLeft;
                this.currentDocument.model.templateTop = this.templateTop;
                this.currentDocument.model.minLeftElement = this.minLeftElement;
                this.currentDocument.model.minTopElement = this.minTopElement;

                //call configure false with the old document on the selected tool to tear down down any temp. stuff
                this.application.ninja.toolsData.selectedToolInstance._configure(false);
            } else if(this.currentDocument && (this.currentDocument.currentView === "code")) {
                this.switchedFromCodeDoc = true;   // Switching from code document affects stage's size and scrollbar
            }

            this._currentDocument = value;

            if(!value) {
                this.hideRulers();
                this.hideCanvas(true);
                drawUtils._eltArray.length = 0;
                drawUtils._planesArray.length = 0;
            } else if(this._currentDocument.currentView === "design") {
                this.restoreAllPanels(this.switchedFromCodeDoc);
                this.switchedFromCodeDoc = false;
                this.hideCanvas(false);
                this.showRulers();

                this.clearAllCanvas();
                this.initWithDocument();
            } else {
                this.collapseAllPanels();
                this.hideCanvas(true);
                this.hideRulers();
            }
        }
    },

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

    templateLeft: { value: 0 },
    templateTop: { value: 0 },

    // keep track of the elements that determine the minimum left and top scrollable amount
    minLeftElement: { value: null },
    minTopElement: { value: null },

    userPaddingLeft: {
        get: function() { return this._userPaddingLeft; },
        set: function(value) {
            this._userPaddingLeft = value;
            this._documentOffsetLeft = -value;
            this.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.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._needsDrawTool) {
//                console.log("drawTool");
                this.application.ninja.toolsData.selectedToolInstance.HandleMouseMove(this._needsDrawTool);
                this.needsDrawTool = null;
            }
        }
    },

    draw: {
        value: function() {
            if(!this.currentDocument) return;

            if(this.draw3DInfo) {
//                console.log("draw3DInfo");
                if(this.updatePlanes) {
//                    console.log("updatePlanes");
                    drawUtils.updatePlanes();
                    this.stageDeps.snapManager._isCacheInvalid = true;
                    this.updatePlanes = false;
                }
                if(this.appModel.show3dGrid) {
//                    console.log("show3dGrid");
                    this.stageDeps.snapManager.updateWorkingPlaneFromView();
                }
//                console.log("drawWorkingPlane");
                drawUtils.drawWorkingPlane();
                this.draw3DInfo = false;
            }
            if(this.drawLayout) {
                this.layout.clearCanvas();
//                console.log("drawLayout");
                this.layout.draw();
                this.drawLayout = false;
            }

            if(this._needsDrawSelection) {
                this.clearCanvas();
                if(this.currentDocument.model.domContainer !== this.currentDocument.model.documentRoot) {
                    this.drawDomContainer(this.currentDocument.model.domContainer);
                }
                //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(this.application.ninja.selectedElements, this.showSelectionBounds);
                }
                NJevent("selectionDrawn");
                this.needsDrawSelection = false;
            }
        }

    },

    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( "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);

            this.addPropertyChangeListener("currentDocument.model.domContainer", this, true);
//            this.addPropertyChangeListener("currentDocument.model.domContainer", this);

        }
    },

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

            if(model.scrollLeft != null) {
                didSwitch = true;
                this._userPaddingLeft = this.currentDocument.model.userPaddingLeft;
                this._userPaddingTop = this.currentDocument.model.userPaddingTop;
                this._documentOffsetLeft = this.currentDocument.model.documentOffsetLeft;
                this._documentOffsetTop  = this.currentDocument.model.documentOffsetTop;
                this._userContentLeft = this.currentDocument.model.userContentLeft;
                this._userContentTop = this.currentDocument.model.userContentTop;
                this._scrollLeft = this.currentDocument.model.scrollLeft;
                this._scrollTop = this.currentDocument.model.scrollTop;
                this.templateLeft = this.currentDocument.model.templateLeft;
                this.templateTop = this.currentDocument.model.templateTop;
                this.minLeftElement = this.currentDocument.model.minLeftElement;
                this.minTopElement = this.currentDocument.model.minTopElement;
            } else {
                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.templateLeft = 0;
                this.templateTop = 0;
                this.minLeftElement = null;
                this.minTopElement = null;
            }

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

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

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

            this.initialize3DOnOpenDocument(!didSwitch);

            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;
                    this.templateLeft = -initialLeft;
                }
                if(initialTop > this.documentOffsetTop) {
                    this.userPaddingTop = -initialTop;
                    this.templateTop = -initialTop;
                }
            }

            if(didSwitch) {
                this.currentDocument.model.views.design.document.body.scrollLeft = this.currentDocument.model.scrollLeft;
                this.currentDocument.model.views.design.document.body.scrollTop = this.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;
                }
            }
            /*
            else if(notification.currentPropertyPath === "currentDocument.model.domContainer") {
                if()
            }
            */
        }
    },

    handleWillChange: {
        value: function(notification) {
//            console.log("stage -> container is about to change");
        }
    },

    enableMouseInOut: {
        value: function() {
            document.addEventListener("mouseup", this, true);
            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);
        }
    },

    captureMouseup: {
        value: function(event) {
            var target = event._event.target.getAttribute("data-montage-id");

            if(target && target === "drawingCanvas") {
                return true;
            } else {
                this.handleAppMouseUp(event);
                return true;
            }
        }
    },

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

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

    handleMousedown: {
        value: function(event) {

            // Increase the canvas to cover the scroll bars
            this._drawingCanvas.height = this._drawingCanvas.height + 11;
            this._drawingCanvas.width = this._drawingCanvas.width + 11;

            // 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) {
            // Restore canvas to un-cover the scroll bars.
            this._drawingCanvas.height = this._drawingCanvas.height - 11;
            this._drawingCanvas.width = this._drawingCanvas.width - 11;
            // If the mouse up comes from dismissing the context menu return

            if(this.contextMenu) {
                this.contextMenu = false;
                return;
            }

            this.disableMouseInOut();
            document.removeEventListener("mouseup", this, true);

            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.currentDocument.model.views.design.document.body.scrollTop -= 20;
            } else {
                this.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);
            // Set a flag here, and tell the tool to handle mouse move in our draw cycle
            this.needsDrawTool = event;
        }
    },

    handleAppMouseUp: {
        value: function(event) {
            if(this.outFlag) {
                this._drawingCanvas.height = this._drawingCanvas.height - 11;
                this._drawingCanvas.width = this._drawingCanvas.width - 11;

                if(this.application.ninja.toolsData.selectedToolInstance.isDrawing) {
                    this.application.ninja.toolsData.selectedToolInstance.HandleLeftButtonUp(event);
                }
                this.disableMouseInOut();
                this.outFlag = false;
            }

            document.removeEventListener("mouseup", this, true);
        }
    },

    handleSelectionChange: {
        value: function(event) {
            this.showSelectionBounds = true;
            this.needsDrawSelection = true;
            this.drawLayout = true;
            this.layout.handleSelectionChange(event);
            // TODO - Some tools currently depend on selectionDrawn event at this point, so force a draw here
            if(this.drawNow) {
                this.draw();
                this.drawNow = false;
            }
        }
    },

    handleElementChanging: {
        value: function(event) {
            // TODO - This assumes only selected elements can have their properties modified
            this.draw3DInfo = true;
            this.showSelectionBounds = false;
            this.needsDrawSelection = true;
        }
    },

    handleElementChange: {
        value: function(event) {
            // TODO - This assumes only selected elements can have their properties modified
            this.draw3DInfo = true;
            this.snapManager._isCacheInvalid = true;
            this.showSelectionBounds = true;
            this.needsDrawSelection = true;
        }
    },

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

            this._scrollLeft = this.currentDocument.model.views.design.document.body.scrollLeft;
            this._scrollTop = this.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.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.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.currentDocument.model.views.design.getElementFromPoint(point.x - this.userContentLeft,point.y - this.userContentTop);

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

            if(selectable) {

                if(this.currentDocument.inExclusion(element) !== -1) {
                    return this.currentDocument.model.domContainer;
                }

                var activeContainerId = this.currentDocument.model.domContainer.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;
            }
        }
    },

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

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

    drawDomContainer: {
        value: function(elt) {


            this.stageDeps.viewUtils.setViewportObj( elt );
            var bounds3D = this.stageDeps.viewUtils.getElementViewBounds3D( elt );

            // convert the local bounds to the world



            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 3 outlines
//            for(var i = 0; i < 3)

            this.context.save();
            // draw it
            this.context.strokeStyle = "#ff0000";
            this.context.lineWidth = 1;


            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();

            this.context.restore();

/*

            this.context.save();
            // draw it
            this.context.strokeStyle = "rgba(0,11,61,0.8)";
            this.context.lineWidth = 1;


            this.context.beginPath();

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

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

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

            this.context.restore();


            this.context.save();
            // draw it
            this.context.strokeStyle = "rgba(255,0,0,0.3)";
            this.context.lineWidth = 1;


            this.context.beginPath();

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

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

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

            this.context.restore();
            */
        }
    },

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

            var h = Math.round(Math.abs(y1-y0));
            var w = Math.round(Math.abs(x1-x0));
            this._drawingContext.fillText("H: " + h, x1 + 38, y1 - 4);
            this._drawingContext.fillText("W: " + w, x1 - 5, y1 + 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.currentDocument.model.documentRoot);
        }
    },

    setZoom: {
        value: function(value) {
            if(!this._firstDraw)
            {
                var userContent = this.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.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.currentDocument.model.documentRoot.offsetHeight / 2.0;
                   break;

                case "side":
                    plane = [1,0,0,0];
                    plane[3] = this.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.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();
        }
    },

    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";
        }
    },

    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(onSwitchDocument){
            this.application.ninja.panelSplitter.restore(onSwitchDocument);
            this.application.ninja.timelineSplitter.restore(onSwitchDocument);
            this.application.ninja.toolsSplitter.restore(onSwitchDocument);
            this.application.ninja.optionsSplitter.restore(onSwitchDocument);
        }
    },

    initialize3DOnOpenDocument: {
        value: function(adjustScrollOffsets) {

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

            this.viewUtils.setStageElement(this.currentDocument.model.documentRoot);
            this.viewUtils.setRootElement(this.currentDocument.model.documentRoot.parentNode);

            this.snapManager._isCacheInvalid = true;
            this.snapManager.currentStage = this.currentDocument.model.documentRoot;
            this.snapManager.setupDragPlaneFromPlane (workingPlane);

            this.drawUtils.initializeFromDocument(adjustScrollOffsets);
        }
    },

    _timelinePlay: {
        value: false
    },

    timelinePlay: {
        get: function() {
            return this._timelinePlay;
        },
        set: function(value) {
            if(this._timelinePlay !== value) {
                this._timelinePlay = value;
                this._timelinePlay ? this.playTimeline() : this.stopTimeline();
            }
        }
    },

    playTimeline: {
        value: function() {
            this._canvas.style.visibility = "hidden";
            this._layoutCanvas.style.visibility = "hidden";
            this._gridCanvas.style.visibility = "hidden";

            this._drawingCanvas.removeEventListener("mousedown", this, false);
            this._drawingCanvas.removeEventListener("mouseup", this, false);
            this._drawingCanvas.removeEventListener("dblclick", this, false);
            this._drawingCanvas.removeEventListener("mousewheel", this, false);
            this._drawingCanvas.removeEventListener("mousemove", this, false);

            this.clearDrawingCanvas();
            this._drawingContext.save();
            this._drawingContext.strokeStyle = "rgba(255,0,0,1)";
            this._drawingContext.lineWidth = 4;

            this._drawingContext.strokeRect(0, 0, this._drawingCanvas.width, this._drawingCanvas.height);

            document.removeEventListener("keydown", this, false);
            document.removeEventListener("keyup", this, false);
            this.bindingView.hide = true;
            this.application.ninja.stylesController._stageStylesheet.rules[0].selectorText = "nj-css-garbage-selector";
        }
    },

    stopTimeline: {
        value: function() {
            this._canvas.style.visibility = "visible";
            this._layoutCanvas.style.visibility = "visible";
            this._gridCanvas.style.visibility = "visible";

            this._drawingCanvas.addEventListener("mousedown", this, false);
            this._drawingCanvas.addEventListener("mouseup", this, false);
            this._drawingCanvas.addEventListener("dblclick", this, false);
            this._drawingCanvas.addEventListener("mousewheel", this, false);
            this._drawingCanvas.addEventListener("mousemove", this, false);

            this.clearDrawingCanvas();
            this._drawingContext.restore();

            document.addEventListener("keydown", this, false);
            document.addEventListener("keyup", this, false);
            this.bindingView.hide = false;
            this.application.ninja.stylesController._stageStylesheet.rules[0].selectorText = "*";
        }
    }

});