/* <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").Montage;
var Component = require("ui/component").Component;
var dom = require("ui/dom");
var Point = require("core/geometry/point").Point;
var Effect = require("effect/effect").Effect;

var DesaturateEffect = require("effect/desaturate-effect").DesaturateEffect;
var InvertEffect = require("effect/invert-effect").InvertEffect;
var SepiaEffect = require("effect/sepia-effect").SepiaEffect;
var MultiplyEffect = require("effect/multiply-effect").MultiplyEffect;

exports.PhotoEditor = Montage.create(Component, {

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

    _image: {
        enumerable: false,
        get: function() {
            return this.__image;
        },
        set: function(value) {

            if (this.__image === value) {
                return;
            }

            if (this.__image) {
                this.__image.element.removeEventListener("load", this, false);
            }

            this.__image = value;
            this.__image.element.identifier = "editorImage";

            if (this.__image) {
                this.__image.element.addEventListener("load", this, false);
            }

        }
    },

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

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

    prepareForActivationEvents: {
        value: function() {
            if (window.Touch) {
                this._canvas.addEventListener("touchstart", this, false);
            } else {
                this._canvas.addEventListener("mousedown", this, false);
            }
        }
    },

    handleMousedown: {
        value: function(event) {
            event.preventDefault();
            this._pointerIdentifier = "mouse";
            this._canvas.addEventListener("mousemove", this, false);
            document.addEventListener("mouseup", this, false);

            this._pickColor(event.clientX, event.clientY);

            this.needsDraw = true;
        }
    },

    handleTouchstart: {
        value: function(event) {

            if (this._pointerIdentifier) {
                return;
            }

            event.preventDefault();

            var pickTouch = event.changedTouches[0];
            this._pointerIdentifier = pickTouch.identifier;
            this._canvas.addEventListener("touchmove", this, false);
            document.addEventListener("touchend", this, false);
            document.addEventListener("touchcancel", this, false);

            this._pickColor(pickTouch.clientX, pickTouch.clientY);
        }
    },

    handleMouseup: {
        value: function() {
            this._pointerIdentifier = null;
            this._canvas.removeEventListener("mousemove", this, false);
            document.removeEventListener("mouseup", this, false);

            var colorPickEvent = document.createEvent("CustomEvent");
            colorPickEvent.initCustomEvent("colorpickend", true, true, null);
            document.application.dispatchEvent(colorPickEvent);

            this.needsDraw = true;
        }
    },

    handleMousemove: {
        enumerable: false,
        value: function(event) {

            if (!this._pointerIdentifier) {
                return;
            }

            this._pickColor(event.clientX, event.clientY);
        }
    },

    handleTouchmove: {
        enumerable: false,
        value: function(event) {

            var i = 0,
                iTouch,
                foundTouch = null

            for(; (iTouch = event.changedTouches[i]); i++) {
                if (iTouch.identifier === this._pointerIdentifier) {
                    foundTouch = iTouch;
                    break;
                }
            }

            if (!foundTouch) {
                return;
            }

            this._pickColor(foundTouch.clientX, foundTouch.clientY);
        }
    },

    handleTouchend: {
        value: function() {
            var i = 0,
                iTouch,
                foundTouch = null

            for(; (iTouch = event.changedTouches[i]); i++) {
                if (iTouch.identifier === this._pointerIdentifier) {
                    foundTouch = iTouch;
                    break;
                }
            }

            if (!foundTouch) {
                return;
            }

            this._pointerIdentifier = null;
            var colorPickEvent = document.createEvent("CustomEvent");
            colorPickEvent.initCustomEvent("colorpickend", true, true, null);
            document.application.dispatchEvent(colorPickEvent);
        }
    },

    _pickColor: {
        value: function(x, y) {
            var gridExtent = 20,
                halfGridExtent = 10,
                canvas = this._canvas,
                context = canvas.getContext('2d'),
                canvasPoint = dom.convertPointFromPageToNode(canvas, Point.create().init(x, y)),
                pickedPixel = context.getImageData(canvasPoint.x, canvasPoint.y, 1, 1),
                focusGrid = context.getImageData(canvasPoint.x - halfGridExtent, canvasPoint.y - halfGridExtent, gridExtent, gridExtent),
                colorPickEvent;

            colorPickEvent = document.createEvent("CustomEvent");
            colorPickEvent.initCustomEvent("colorpick", true, true, null);
            colorPickEvent.color = pickedPixel.data;
            colorPickEvent.focusGrid = focusGrid;
            colorPickEvent.clientX = x;
            colorPickEvent.clientY = y;
            colorPickEvent.canvasX = canvasPoint.x;
            colorPickEvent.canvasY = canvasPoint.y;

            document.application.dispatchEvent(colorPickEvent);
        }
    },

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

    src: {
        enumerable: false,
        get: function() {
            return this._src;
        },
        set: function(value) {

            if (value === this._src) {
                return;
            }

            this._src = value;

            this._needToRefreshImageData = true;
        }
    },
    handleEditorImageLoad: {
        enumerable: false,
        value: function(event) {
            this.needsDraw = true;
        }
    },

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

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

    willDraw: {
        value: function() {
            this._width = this._image.element.offsetWidth;
            this._height = this._image.element.offsetHeight;
        }
    },

    // TODO Eventually we need to maintain a stack of effects to apply to the image inside the editor
    // I don't want to complicate this for the demo right now though
    _inverted: {
        enumerable: false,
        value: false
    },

    inverted: {
        enumerable: false,
        get: function() {
            return this._inverted;
        },
        set: function(value) {

            if (value === this._inverted) {
                return;
            }

            this._inverted = value;
            this.needsDraw = true;
        }
    },

    _desaturated: {
        enumerable: false,
        value: false
    },

    desaturated: {
        enumerable: false,
        get: function() {
            return this._desaturated;
        },
        set: function(value) {

            if (value === this._desaturated) {
                return;
            }

            this._desaturated = value;
            this.needsDraw = true;
        }
    },

    _sepiaToned: {
        enumerable: false,
        value: false
    },

    sepia: {
        enumerable: false,
        get: function() {
            return this._sepiaToned;
        },
        set: function(value) {

            if (value === this._sepiaToned) {
                return;
            }

            this._sepiaToned = value;
            this.needsDraw = true;
        }
    },

    _multiplyEffect: {
        enumerable: false,
        value: false
    },

    multiply: {
        enumerable: false,
        get: function() {
            return this._multiplyEffect;
        },
        set: function(value) {

            if (value === this._multiplyEffect) {
                return;
            }

            this._multiplyEffect = value;
            this.needsDraw = true;
        }
    },

    _multiplyMultiplier: {
        enumerable: false,
        value: 1
    },

    multiplier: {
        enumerable: false,
        get: function() {
            return this._multiplyMultiplier;
        },
        set: function(value) {

            if (value === this._multiplyMultiplier) {
                return;
            }

            this._multiplyMultiplier = value;

            if (this._multiplyEffect) {
                this.needsDraw = true;
            }
        }
    },

    prepareForDraw: {
        value: function() {
            // TODO this is a workaround for a problem with our deserialization in iOS concerning
            // canvas elements. Debugging points to some issue with adoptNode. Either way,
            // if we don't do this it takes two draw cycles to actually get the canvas rendering.
            var newCanvas = this._canvas.cloneNode(true);
            this.element.replaceChild(newCanvas, this._canvas);
            this._canvas = newCanvas;
        }
    },

    draw: {
        value: function() {

            // Don't draw unless we have something to actually draw
            if (!this._width || !this._height) {
                return;
            }

            // TODO should only draw the canvas if the canvas data is dirty
            // flipping classnames should be cheap
            if (this._pointerIdentifier) {
                this.element.classList.add("pickingColor");
            } else {
                this.element.classList.remove("pickingColor");
            }

            var canvas = this._canvas,
                image = this._image.element,
                context;

            canvas.width = this._width;
            canvas.height = this._height;

            context = canvas.getContext('2d');
            context.drawImage(image, 0, 0);

            var imgd = context.getImageData(0, 0, this._width, this._height),
                pixels = imgd.data,
                pixelCount = pixels.length;

            if (this.inverted) {
                InvertEffect.applyEffect(pixels, pixelCount);
            }

            if (this.desaturated) {
                DesaturateEffect.applyEffect(pixels, pixelCount);
            }

            if (this.sepia) {
                SepiaEffect.applyEffect(pixels, pixelCount);
            }

            if (this.multiply) {
                MultiplyEffect.applyEffect(pixels, pixelCount, this.multiplier);
            }

            context.putImageData(imgd, 0, 0);
        }

    }

});