/**
* @author       Mat Groves http://matgroves.com @Doormat23
* @author       Richard Davey <rich@photonstorm.com>
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* The base class for all objects that are rendered. Contains properties for position, scaling,
* rotation, masks and cache handling.
* 
* This is an abstract class and should not be used on its own, rather it should be extended.
*
* It is used internally by the likes of PIXI.Sprite.
*
* @class PIXI.DisplayObject
* @constructor
*/
PIXI.DisplayObject = function () {

    /**
    * The coordinates, in pixels, of this DisplayObject, relative to its parent container.
    * 
    * The value of this property does not reflect any positioning happening further up the display list.
    * To obtain that value please see the `worldPosition` property.
    * 
    * @property {PIXI.Point} position
    * @default
    */
    this.position = new PIXI.Point(0, 0);

    /**
    * The scale of this DisplayObject. A scale of 1:1 represents the DisplayObject
    * at its default size. A value of 0.5 would scale this DisplayObject by half, and so on.
    * 
    * The value of this property does not reflect any scaling happening further up the display list.
    * To obtain that value please see the `worldScale` property.
    * 
    * @property {PIXI.Point} scale
    * @default
    */
    this.scale = new PIXI.Point(1, 1);

    /**
    * The pivot point of this DisplayObject that it rotates around. The values are expressed
    * in pixel values.
    * @property {PIXI.Point} pivot
    * @default
    */
    this.pivot = new PIXI.Point(0, 0);

    /**
    * The rotation of this DisplayObject. The value is given, and expressed, in radians, and is based on
    * a right-handed orientation.
    * 
    * The value of this property does not reflect any rotation happening further up the display list.
    * To obtain that value please see the `worldRotation` property.
    * 
    * @property {number} rotation
    * @default
    */
    this.rotation = 0;

    /**
    * The alpha value of this DisplayObject. A value of 1 is fully opaque. A value of 0 is transparent.
    * Please note that an object with an alpha value of 0 is skipped during the render pass.
    * 
    * The value of this property does not reflect any alpha values set further up the display list.
    * To obtain that value please see the `worldAlpha` property.
    * 
    * @property {number} alpha
    * @default
    */
    this.alpha = 1;

    /**
    * The visibility of this DisplayObject. A value of `false` makes the object invisible.
    * A value of `true` makes it visible. Please note that an object with a visible value of
    * `false` is skipped during the render pass. Equally a DisplayObject with visible false will
    * not render any of its children.
    * 
    * The value of this property does not reflect any visible values set further up the display list.
    * To obtain that value please see the `worldVisible` property.
    * 
    * @property {boolean} visible
    * @default
    */
    this.visible = true;

    /**
     * This is the defined area that will pick up mouse / touch events. It is null by default.
     * Setting it is a neat way of optimising the hitTest function that the interactionManager will use (as it will not need to hit test all the children)
     *
     * @property hitArea
     * @type Rectangle|Circle|Ellipse|Polygon
     */
    this.hitArea = null;

    /**
    * Should this DisplayObject be rendered by the renderer? An object with a renderable value of
    * `false` is skipped during the render pass.
    * 
    * @property {boolean} renderable
    * @default
    */
    this.renderable = false;

    /**
    * The parent DisplayObjectContainer that this DisplayObject is a child of.
    * All DisplayObjects must belong to a parent in order to be rendered.
    * The root parent is the Stage object. This property is set automatically when the
    * DisplayObject is added to, or removed from, a DisplayObjectContainer.
    * 
    * @property {PIXI.DisplayObjectContainer} parent
    * @default
    * @readOnly
    */
    this.parent = null;

    /**
    * The multiplied alpha value of this DisplayObject. A value of 1 is fully opaque. A value of 0 is transparent.
    * This value is the calculated total, based on the alpha values of all parents of this DisplayObjects 
    * in the display list.
    * 
    * To obtain, and set, the local alpha value, see the `alpha` property.
    *
    * Note: This property is only updated at the end of the `updateTransform` call, once per render. Until 
    * that happens this property will contain values based on the previous frame. Be mindful of this if
    * accessing this property outside of the normal game flow, i.e. from an asynchronous event callback.
    * 
    * @property {number} worldAlpha
    * @readOnly
    */
    this.worldAlpha = 1;

    /**
    * The current transform of this DisplayObject.
    * 
    * This property contains the calculated total, based on the transforms of all parents of this 
    * DisplayObject in the display list.
    *
    * Note: This property is only updated at the end of the `updateTransform` call, once per render. Until 
    * that happens this property will contain values based on the previous frame. Be mindful of this if
    * accessing this property outside of the normal game flow, i.e. from an asynchronous event callback.
    *
    * @property {PIXI.Matrix} worldTransform
    * @readOnly
    */
    this.worldTransform = new PIXI.Matrix();

    /**
    * The coordinates, in pixels, of this DisplayObject within the world.
    * 
    * This property contains the calculated total, based on the positions of all parents of this 
    * DisplayObject in the display list.
    *
    * Note: This property is only updated at the end of the `updateTransform` call, once per render. Until 
    * that happens this property will contain values based on the previous frame. Be mindful of this if
    * accessing this property outside of the normal game flow, i.e. from an asynchronous event callback.
    * 
    * @property {PIXI.Point} worldPosition
    * @readOnly
    */
    this.worldPosition = new PIXI.Point(0, 0);

    /**
    * The global scale of this DisplayObject.
    * 
    * This property contains the calculated total, based on the scales of all parents of this 
    * DisplayObject in the display list.
    *
    * Note: This property is only updated at the end of the `updateTransform` call, once per render. Until 
    * that happens this property will contain values based on the previous frame. Be mindful of this if
    * accessing this property outside of the normal game flow, i.e. from an asynchronous event callback.
    * 
    * @property {PIXI.Point} worldScale
    * @readOnly
    */
    this.worldScale = new PIXI.Point(1, 1);

    /**
    * The rotation, in radians, of this DisplayObject.
    * 
    * This property contains the calculated total, based on the rotations of all parents of this 
    * DisplayObject in the display list.
    *
    * Note: This property is only updated at the end of the `updateTransform` call, once per render. Until 
    * that happens this property will contain values based on the previous frame. Be mindful of this if
    * accessing this property outside of the normal game flow, i.e. from an asynchronous event callback.
    * 
    * @property {number} worldRotation
    * @readOnly
    */
    this.worldRotation = 0;

    /**
    * The rectangular area used by filters when rendering a shader for this DisplayObject.
    *
    * @property {PIXI.Rectangle} filterArea
    * @type Rectangle
    * @default
    */
    this.filterArea = null;

    /**
    * @property {number} _sr - Cached rotation value.
    * @private
    */
    this._sr = 0;

    /**
    * @property {number} _cr - Cached rotation value.
    * @private
    */
    this._cr = 1;

    /**
    * @property {PIXI.Rectangle} _bounds - The cached bounds of this object.
    * @private
    */
    this._bounds = new PIXI.Rectangle(0, 0, 0, 0);

    /**
    * @property {PIXI.Rectangle} _currentBounds - The most recently calculated bounds of this object.
    * @private
    */
    this._currentBounds = null;

    /**
    * @property {PIXI.Rectangle} _mask - The cached mask of this object.
    * @private
    */
    this._mask = null;

    /**
    * @property {boolean} _cacheAsBitmap - Internal cache as bitmap flag.
    * @private
    */
    this._cacheAsBitmap = false;

    /**
    * @property {boolean} _cacheIsDirty - Internal dirty cache flag.
    * @private
    */
    this._cacheIsDirty = false;

};

PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject;

PIXI.DisplayObject.prototype = {

    /**
    * Destroy this DisplayObject.
    *
    * Removes any cached sprites, sets renderable flag to false, and nulls filters, bounds and mask.
    *
    * Also iteratively calls `destroy` on any children.
    *
    * @method PIXI.DisplayObject#destroy
    */
    destroy: function () {

        if (this.children)
        {
            var i = this.children.length;

            while (i--)
            {
                this.children[i].destroy();
            }

            this.children = [];
        }

        this.hitArea = null;
        this.parent = null;
        this.worldTransform = null;
        this.filterArea = null;
        this.renderable = false;

        this._bounds = null;
        this._currentBounds = null;
        this._mask = null;

        this._destroyCachedSprite();

    },

    /*
    * Updates the transform matrix this DisplayObject uses for rendering.
    *
    * If the object has no parent, and no parent parameter is provided, it will default to 
    * Phaser.Game.World as the parent transform to use. If that is unavailable the transform fails to take place.
    *
    * The `parent` parameter has priority over the actual parent. Use it as a parent override.
    * Setting it does **not** change the actual parent of this DisplayObject.
    *
    * Calling this method updates the `worldTransform`, `worldAlpha`, `worldPosition`, `worldScale` 
    * and `worldRotation` properties.
    *
    * If a `transformCallback` has been specified, it is called at the end of this method, and is passed
    * the new, updated, worldTransform property, along with the parent transform used.
    *
    * @method PIXI.DisplayObject#updateTransform
    * @param {PIXI.DisplayObjectContainer} [parent] - Optional parent to calculate this DisplayObjects transform from.
    * @return {PIXI.DisplayObject} - A reference to this DisplayObject.
    */
    updateTransform: function (parent) {

        if (!parent && !this.parent && !this.game)
        {
            return this;
        }

        var p = this.parent;

        if (parent)
        {
            p = parent;
        }
        else if (!this.parent)
        {
            p = this.game.world;
        }

        // create some matrix refs for easy access
        var pt = p.worldTransform;
        var wt = this.worldTransform;

        // temporary matrix variables
        var a, b, c, d, tx, ty;

        // so if rotation is between 0 then we can simplify the multiplication process..
        if (this.rotation % PIXI.PI_2)
        {
            // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes
            if (this.rotation !== this.rotationCache)
            {
                this.rotationCache = this.rotation;
                this._sr = Math.sin(this.rotation);
                this._cr = Math.cos(this.rotation);
            }

            // get the matrix values of the displayobject based on its transform properties..
            a  =  this._cr * this.scale.x;
            b  =  this._sr * this.scale.x;
            c  = -this._sr * this.scale.y;
            d  =  this._cr * this.scale.y;
            tx =  this.position.x;
            ty =  this.position.y;
            
            // check for pivot.. not often used so geared towards that fact!
            if (this.pivot.x || this.pivot.y)
            {
                tx -= this.pivot.x * a + this.pivot.y * c;
                ty -= this.pivot.x * b + this.pivot.y * d;
            }

            // concat the parent matrix with the objects transform.
            wt.a  = a  * pt.a + b  * pt.c;
            wt.b  = a  * pt.b + b  * pt.d;
            wt.c  = c  * pt.a + d  * pt.c;
            wt.d  = c  * pt.b + d  * pt.d;
            wt.tx = tx * pt.a + ty * pt.c + pt.tx;
            wt.ty = tx * pt.b + ty * pt.d + pt.ty;
        }
        else
        {
            // lets do the fast version as we know there is no rotation..
            a  = this.scale.x;
            d  = this.scale.y;

            tx = this.position.x - this.pivot.x * a;
            ty = this.position.y - this.pivot.y * d;

            wt.a  = a  * pt.a;
            wt.b  = a  * pt.b;
            wt.c  = d  * pt.c;
            wt.d  = d  * pt.d;
            wt.tx = tx * pt.a + ty * pt.c + pt.tx;
            wt.ty = tx * pt.b + ty * pt.d + pt.ty;
        }

        //  Set the World values
        this.worldAlpha = this.alpha * p.worldAlpha;
        this.worldPosition.set(wt.tx, wt.ty);
        this.worldScale.set(this.scale.x * Math.sqrt(wt.a * wt.a + wt.c * wt.c), this.scale.y * Math.sqrt(wt.b * wt.b + wt.d * wt.d));
        this.worldRotation = Math.atan2(-wt.c, wt.d);

        // reset the bounds each time this is called!
        this._currentBounds = null;

        //  Custom callback?
        if (this.transformCallback)
        {
            this.transformCallback.call(this.transformCallbackContext, wt, pt);
        }

        return this;

    },

    /**
    * To be overridden by classes that require it.
    *
    * @method PIXI.DisplayObject#preUpdate
    */
    preUpdate: function () {

    },

    /**
    * Generates a RenderTexture based on this DisplayObject, which can they be used to texture other Sprites.
    * This can be useful if your DisplayObject is static, or complicated, and needs to be reused multiple times.
    *
    * Please note that no garbage collection takes place on old textures. It is up to you to destroy old textures,
    * and references to them, so they don't linger in memory.
    *
    * @method PIXI.DisplayObject#generateTexture
    * @param {number} [resolution=1] - The resolution of the texture being generated.
    * @param {number} [scaleMode=PIXI.scaleModes.DEFAULT] - See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values.
    * @param {PIXI.CanvasRenderer|PIXI.WebGLRenderer} renderer - The renderer used to generate the texture.
    * @return {PIXI.RenderTexture} - A RenderTexture containing an image of this DisplayObject at the time it was invoked.
    */
    generateTexture: function (resolution, scaleMode, renderer) {

        var bounds = this.getLocalBounds();

        var renderTexture = new PIXI.RenderTexture(bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution);
        
        PIXI.DisplayObject._tempMatrix.tx = -bounds.x;
        PIXI.DisplayObject._tempMatrix.ty = -bounds.y;
        
        renderTexture.render(this, PIXI.DisplayObject._tempMatrix);

        return renderTexture;

    },

    /**
    * If this DisplayObject has a cached Sprite, this method generates and updates it.
    *
    * @method PIXI.DisplayObject#updateCache
    * @return {PIXI.DisplayObject} - A reference to this DisplayObject.
    */
    updateCache: function () {

        this._generateCachedSprite();

        return this;

    },

    /**
    * Calculates the global position of this DisplayObject, based on the position given.
    *
    * @method PIXI.DisplayObject#toGlobal
    * @param {PIXI.Point} position - The global position to calculate from.
    * @return {PIXI.Point} - A point object representing the position of this DisplayObject based on the global position given.
    */
    toGlobal: function (position) {

        this.updateTransform();

        return this.worldTransform.apply(position);

    },

    /**
    * Calculates the local position of this DisplayObject, relative to another point.
    *
    * @method PIXI.DisplayObject#toLocal
    * @param {PIXI.Point} position - The world origin to calculate from.
    * @param {PIXI.DisplayObject} [from] - An optional DisplayObject to calculate the global position from.
    * @return {PIXI.Point} - A point object representing the position of this DisplayObject based on the global position given.
    */
    toLocal: function (position, from) {

        if (from)
        {
            position = from.toGlobal(position);
        }

        this.updateTransform();

        return this.worldTransform.applyInverse(position);

    },

    /**
    * Internal method.
    *
    * @method PIXI.DisplayObject#_renderCachedSprite
    * @private
    * @param {Object} renderSession - The render session
    */
    _renderCachedSprite: function (renderSession) {

        this._cachedSprite.worldAlpha = this.worldAlpha;

        if (renderSession.gl)
        {
            PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession);
        }
        else
        {
            PIXI.Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession);
        }

    },

    /**
    * Internal method.
    *
    * @method PIXI.DisplayObject#_generateCachedSprite
    * @private
    */
    _generateCachedSprite: function () {

        this._cacheAsBitmap = false;

        var bounds = this.getLocalBounds();

        //  Round it off and force non-zero dimensions
        bounds.width = Math.max(1, Math.ceil(bounds.width));
        bounds.height = Math.max(1, Math.ceil(bounds.height));

        this.updateTransform();

        if (!this._cachedSprite)
        {
            var renderTexture = new PIXI.RenderTexture(bounds.width, bounds.height);
            this._cachedSprite = new PIXI.Sprite(renderTexture);
            this._cachedSprite.worldTransform = this.worldTransform;
        }
        else
        {
            this._cachedSprite.texture.resize(bounds.width, bounds.height);
        }

        //  Remove filters
        var tempFilters = this._filters;

        this._filters = null;
        this._cachedSprite.filters = tempFilters;

        PIXI.DisplayObject._tempMatrix.tx = -bounds.x;
        PIXI.DisplayObject._tempMatrix.ty = -bounds.y;

        this._cachedSprite.texture.render(this, PIXI.DisplayObject._tempMatrix, true);
        this._cachedSprite.anchor.x = -(bounds.x / bounds.width);
        this._cachedSprite.anchor.y = -(bounds.y / bounds.height);

        this._filters = tempFilters;

        this._cacheAsBitmap = true;

    },

    /**
    * Destroys a cached Sprite.
    *
    * @method PIXI.DisplayObject#_destroyCachedSprite
    * @private
    */
    _destroyCachedSprite: function () {

        if (!this._cachedSprite)
        {
            return;
        }

        this._cachedSprite.texture.destroy(true);

        this._cachedSprite = null;

    }

};

//  Alias for updateTransform. As used in DisplayObject container, etc.
PIXI.DisplayObject.prototype.displayObjectUpdateTransform = PIXI.DisplayObject.prototype.updateTransform;

Object.defineProperties(PIXI.DisplayObject.prototype, {

    /**
    * The horizontal position of the DisplayObject, in pixels, relative to its parent.
    * If you need the world position of the DisplayObject, use `DisplayObject.worldPosition` instead.
    * @name PIXI.DisplayObject#x
    * @property {number} x - The horizontal position of the DisplayObject, in pixels, relative to its parent.
    */
    'x': {

        get: function () {

            return this.position.x;

        },

        set: function (value) {

            this.position.x = value;

        }

    },

    /**
    * The vertical position of the DisplayObject, in pixels, relative to its parent.
    * If you need the world position of the DisplayObject, use `DisplayObject.worldPosition` instead.
    * @name PIXI.DisplayObject#y
    * @property {number} y - The vertical position of the DisplayObject, in pixels, relative to its parent.
    */
    'y': {

        get: function () {

            return this.position.y;

        },

        set: function (value) {

            this.position.y = value;

        }

    },

    /**
    * Indicates if this DisplayObject is visible, based on it, and all of its parents, `visible` property values.
    * @name PIXI.DisplayObject#worldVisible
    * @property {boolean} worldVisible - Indicates if this DisplayObject is visible, based on it, and all of its parents, `visible` property values.
    */
    'worldVisible': {

        get: function () {

            if (!this.visible)
            {
                return false;
            }
            else
            {
                var item = this.parent;

                if (!item)
                {
                    return this.visible;
                }
                else
                {
                    do
                    {
                        if (!item.visible)
                        {
                            return false;
                        }

                        item = item.parent;
                    }
                    while (item);

                }

                return true;
            }

        }

    },

    /**
    * Sets a mask for this DisplayObject. A mask is an instance of a Graphics object.
    * When applied it limits the visible area of this DisplayObject to the shape of the mask.
    * Under a Canvas renderer it uses shape clipping. Under a WebGL renderer it uses a Stencil Buffer.
    * To remove a mask, set this property to `null`.
    * 
    * @name PIXI.DisplayObject#mask
    * @property {PIXI.Graphics} mask - The mask applied to this DisplayObject. Set to `null` to remove an existing mask.
    */
    'mask': {

        get: function () {

            return this._mask;

        },

        set: function (value) {

            if (this._mask)
            {
                this._mask.isMask = false;
            }

            this._mask = value;

            if (value)
            {
                this._mask.isMask = true;
            }

        }

    },

    /**
    * Sets the filters for this DisplayObject. This is a WebGL only feature, and is ignored by the Canvas
    * Renderer. A filter is a shader applied to this DisplayObject. You can modify the placement of the filter
    * using `DisplayObject.filterArea`.
    * 
    * To remove filters, set this property to `null`.
    *
    * Note: You cannot have a filter set, and a MULTIPLY Blend Mode active, at the same time. Setting a 
    * filter will reset this DisplayObjects blend mode to NORMAL.
    * 
    * @name PIXI.DisplayObject#filters
    * @property {Array} filters - An Array of PIXI.AbstractFilter objects, or objects that extend them.
    */
    'filters': {

        get: function () {

            return this._filters;

        },

        set: function (value) {

            if (Array.isArray(value))
            {
                //  Put all the passes in one place.
                var passes = [];

                for (var i = 0; i < value.length; i++)
                {
                    var filterPasses = value[i].passes;

                    for (var j = 0; j < filterPasses.length; j++)
                    {
                        passes.push(filterPasses[j]);
                    }
                }

                //  Needed any more?
                this._filterBlock = { target: this, filterPasses: passes };
            }

            this._filters = value;

            if (this.blendMode && this.blendMode === PIXI.blendModes.MULTIPLY)
            {
                this.blendMode = PIXI.blendModes.NORMAL;
            }

        }

    },

    /**
    * Sets if this DisplayObject should be cached as a bitmap.
    *
    * When invoked it will take a snapshot of the DisplayObject, as it is at that moment, and store it 
    * in a RenderTexture. This is then used whenever this DisplayObject is rendered. It can provide a
    * performance benefit for complex, but static, DisplayObjects. I.e. those with lots of children.
    *
    * Cached Bitmaps do not track their parents. If you update a property of this DisplayObject, it will not
    * re-generate the cached bitmap automatically. To do that you need to call `DisplayObject.updateCache`.
    * 
    * To remove a cached bitmap, set this property to `null`.
    * 
    * @name PIXI.DisplayObject#cacheAsBitmap
    * @property {boolean} cacheAsBitmap - Cache this DisplayObject as a Bitmap. Set to `null` to remove an existing cached bitmap.
    */
    'cacheAsBitmap': {

        get: function () {

            return this._cacheAsBitmap;

        },

        set: function (value) {

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

            if (value)
            {
                this._generateCachedSprite();
            }
            else
            {
                this._destroyCachedSprite();
            }

            this._cacheAsBitmap = value;

        }

    }

});
