/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2018 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var Camera = require('../../cameras/2d/BaseCamera');
var CanvasPool = require('../../display/canvas/CanvasPool');
var Class = require('../../utils/Class');
var Components = require('../components');
var CONST = require('../../const');
var Frame = require('../../textures/Frame');
var GameObject = require('../GameObject');
var Render = require('./RenderTextureRender');
var UUID = require('../../utils/string/UUID');

/**
 * @classdesc
 * A Render Texture.
 * 
 * A Render Texture is a special texture that allows any number of Game Objects to be drawn to it. You can take many complex objects and
 * draw them all to this one texture, which can they be used as the texture for other Game Object's. It's a way to generate dynamic
 * textures at run-time that are WebGL friendly and don't invoke expensive GPU uploads.
 *
 * @class RenderTexture
 * @extends Phaser.GameObjects.GameObject
 * @memberOf Phaser.GameObjects
 * @constructor
 * @since 3.2.0
 *
 * @extends Phaser.GameObjects.Components.Alpha
 * @extends Phaser.GameObjects.Components.BlendMode
 * @extends Phaser.GameObjects.Components.ComputedSize
 * @extends Phaser.GameObjects.Components.Depth
 * @extends Phaser.GameObjects.Components.Flip
 * @extends Phaser.GameObjects.Components.GetBounds
 * @extends Phaser.GameObjects.Components.Mask
 * @extends Phaser.GameObjects.Components.Origin
 * @extends Phaser.GameObjects.Components.Pipeline
 * @extends Phaser.GameObjects.Components.ScaleMode
 * @extends Phaser.GameObjects.Components.ScrollFactor
 * @extends Phaser.GameObjects.Components.Tint
 * @extends Phaser.GameObjects.Components.Transform
 * @extends Phaser.GameObjects.Components.Visible
 *
 * @param {Phaser.Scene} scene - The Scene to which this Game Object belongs. A Game Object can only belong to one Scene at a time.
 * @param {number} [x=0] - The horizontal position of this Game Object in the world.
 * @param {number} [y=0] - The vertical position of this Game Object in the world.
 * @param {integer} [width=32] - The width of the Render Texture.
 * @param {integer} [height=32] - The height of the Render Texture.
 */
var RenderTexture = new Class({

    Extends: GameObject,

    Mixins: [
        Components.Alpha,
        Components.BlendMode,
        Components.ComputedSize,
        Components.Crop,
        Components.Depth,
        Components.Flip,
        Components.GetBounds,
        Components.Mask,
        Components.Origin,
        Components.Pipeline,
        Components.ScaleMode,
        Components.ScrollFactor,
        Components.Tint,
        Components.Transform,
        Components.Visible,
        Render
    ],

    initialize:

    function RenderTexture (scene, x, y, width, height)
    {
        if (x === undefined) { x = 0; }
        if (y === undefined) { y = 0; }
        if (width === undefined) { width = 32; }
        if (height === undefined) { height = 32; }

        GameObject.call(this, scene, 'RenderTexture');

        /**
         * A reference to either the Canvas or WebGL Renderer that the Game instance is using.
         *
         * @name Phaser.GameObjects.RenderTexture#renderer
         * @type {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)}
         * @since 3.2.0
         */
        this.renderer = scene.sys.game.renderer;

        /**
         * A reference to the Texture Manager.
         *
         * @name Phaser.GameObjects.RenderTexture#textureManager
         * @type {Phaser.Textures.TextureManager}
         * @since 3.12.0
         */
        this.textureManager = scene.sys.textures;

        /**
         * The tint of the Render Texture when rendered.
         *
         * @name Phaser.GameObjects.RenderTexture#globalTint
         * @type {number}
         * @default 0xffffff
         * @since 3.2.0
         */
        this.globalTint = 0xffffff;

        /**
         * The alpha of the Render Texture when rendered.
         *
         * @name Phaser.GameObjects.RenderTexture#globalAlpha
         * @type {number}
         * @default 1
         * @since 3.2.0
         */
        this.globalAlpha = 1;

        /**
         * The HTML Canvas Element that the Render Texture is drawing to.
         * This is only populated if Phaser is running with the Canvas Renderer.
         *
         * @name Phaser.GameObjects.RenderTexture#canvas
         * @type {HTMLCanvasElement}
         * @since 3.2.0
         */
        this.canvas = CanvasPool.create2D(this, width, height);

        /**
         * A reference to the Rendering Context belonging to the Canvas Element this Render Texture is drawing to.
         *
         * @name Phaser.GameObjects.RenderTexture#context
         * @type {CanvasRenderingContext2D}
         * @since 3.2.0
         */
        this.context = this.canvas.getContext('2d');

        /**
         * A reference to the GL Frame Buffer this Render Texture is drawing to.
         * This is only set if Phaser is running with the WebGL Renderer.
         *
         * @name Phaser.GameObjects.RenderTexture#framebuffer
         * @type {?WebGLFramebuffer}
         * @since 3.2.0
         */
        this.framebuffer = null;

        /**
         * The internal crop data object, as used by `setCrop` and passed to the `Frame.setCropUVs` method.
         *
         * @name Phaser.GameObjects.RenderTexture#_crop
         * @type {object}
         * @private
         * @since 3.12.0
         */
        this._crop = this.resetCropObject();

        /**
         * The Texture corresponding to this Render Texture.
         *
         * @name Phaser.GameObjects.RenderTexture#texture
         * @type {Phaser.Textures.Texture}
         * @since 3.12.0
         */
        this.texture = scene.sys.textures.addCanvas(UUID(), this.canvas);

        /**
         * The Frame corresponding to this Render Texture.
         *
         * @name Phaser.GameObjects.RenderTexture#frame
         * @type {Phaser.Textures.Frame}
         * @since 3.12.0
         */
        this.frame = this.texture.get();
        
        /**
         * Internal saved texture flag.
         *
         * @name Phaser.GameObjects.RenderTexture#_saved
         * @type {boolean}
         * @private
         * @since 3.12.0
         */
        this._saved = false;

        /**
         * An internal Camera that can be used to move around the Render Texture.
         * Control it just like you would any Scene Camera. The difference is that it only impacts the placement of what
         * is drawn to the Render Texture. You can scroll, zoom and rotate this Camera.
         *
         * @name Phaser.GameObjects.RenderTexture#camera
         * @type {Phaser.Cameras.Scene2D.BaseCamera}
         * @since 3.12.0
         */
        this.camera = new Camera(0, 0, width, height);

        /**
         * Is this Render Texture dirty or not? If not it won't spend time clearing or filling itself.
         *
         * @name Phaser.GameObjects.RenderTexture#dirty
         * @type {boolean}
         * @since 3.12.0
         */
        this.dirty = false;

        /**
         * A reference to the WebGL Rendering Context.
         *
         * @name Phaser.GameObjects.RenderTexture#gl
         * @type {WebGLRenderingContext}
         * @default null
         * @since 3.0.0
         */
        this.gl = null;

        var renderer = this.renderer;

        if (renderer.type === CONST.WEBGL)
        {
            var gl = renderer.gl;

            this.gl = gl;
            this.drawGameObject = this.batchGameObjectWebGL;
            this.framebuffer = renderer.createFramebuffer(width, height, this.frame.source.glTexture, false);
        }
        else if (renderer.type === CONST.CANVAS)
        {
            this.drawGameObject = this.batchGameObjectCanvas;
        }

        this.camera.setScene(scene);

        this.setPosition(x, y);
        this.setSize(width, height);
        this.setOrigin(0, 0);
        this.initPipeline();
    },

    /**
     * Sets the size of this Game Object.
     * 
     * @method Phaser.GameObjects.Components.Size#setSize
     * @since 3.0.0
     *
     * @param {number} width - The width of this Game Object.
     * @param {number} height - The height of this Game Object.
     * 
     * @return {this} This Game Object instance.
     */
    setSize: function (width, height)
    {
        return this.resize(width, height);
    },

    /**
     * Resizes the Render Texture to the new dimensions given.
     *
     * In WebGL it will destroy and then re-create the frame buffer being used by the Render Texture.
     * In Canvas it will resize the underlying canvas element.
     * Both approaches will erase everything currently drawn to the Render Texture.
     *
     * If the dimensions given are the same as those already being used, calling this method will do nothing.
     *
     * @method Phaser.GameObjects.RenderTexture#resize
     * @since 3.10.0
     *
     * @param {number} width - The new width of the Render Texture.
     * @param {number} [height] - The new height of the Render Texture. If not specified, will be set the same as the `width`.
     *
     * @return {this} This Render Texture.
     */
    resize: function (width, height)
    {
        if (height === undefined) { height = width; }

        if (width !== this.width || height !== this.height)
        {
            this.canvas.width = width;
            this.canvas.height = height;

            if (this.gl)
            {
                var gl = this.gl;

                this.renderer.deleteTexture(this.frame.source.glTexture);
                this.renderer.deleteFramebuffer(this.framebuffer);

                this.frame.source.glTexture = this.renderer.createTexture2D(0, gl.NEAREST, gl.NEAREST, gl.CLAMP_TO_EDGE, gl.CLAMP_TO_EDGE, gl.RGBA, null, width, height, false);
                this.framebuffer = this.renderer.createFramebuffer(width, height, this.frame.source.glTexture, false);

                this.frame.glTexture = this.frame.source.glTexture;
            }

            this.frame.source.width = width;
            this.frame.source.height = height;

            this.camera.setSize(width, height);

            this.frame.setSize(width, height);

            this.width = width;
            this.height = height;
        }

        return this;
    },

    /**
     * Set the tint to use when rendering this Render Texture.
     *
     * @method Phaser.GameObjects.RenderTexture#setGlobalTint
     * @since 3.2.0
     *
     * @param {integer} tint - The tint value.
     *
     * @return {this} This Render Texture.
     */
    setGlobalTint: function (tint)
    {
        this.globalTint = tint;

        return this;
    },

    /**
     * Set the alpha to use when rendering this Render Texture.
     *
     * @method Phaser.GameObjects.RenderTexture#setGlobalAlpha
     * @since 3.2.0
     *
     * @param {number} alpha - The alpha value.
     *
     * @return {this} This Render Texture.
     */
    setGlobalAlpha: function (alpha)
    {
        this.globalAlpha = alpha;

        return this;
    },

    /**
     * Stores a copy of this Render Texture in the Texture Manager using the given key.
     * 
     * After doing this, any texture based Game Object, such as a Sprite, can use the contents of this
     * Render Texture by using the texture key:
     * 
     * ```javascript
     * var rt = this.add.renderTexture(0, 0, 128, 128);
     * 
     * // Draw something to the Render Texture
     * 
     * rt.saveTexture('doodle');
     * 
     * this.add.image(400, 300, 'doodle');
     * ```
     * 
     * Updating the contents of this Render Texture will automatically update _any_ Game Object
     * that is using it as a texture. Calling `saveTexture` again will not save another copy
     * of the same texture, it will just rename the key of the existing copy.
     * 
     * By default it will create a single base texture. You can add frames to the texture
     * by using the `Texture.add` method. After doing this, you can then allow Game Objects
     * to use a specific frame from a Render Texture.
     *
     * @method Phaser.GameObjects.RenderTexture#saveTexture
     * @since 3.12.0
     *
     * @param {string} key - The unique key to store the texture as within the global Texture Manager.
     *
     * @return {Phaser.Textures.Texture} The Texture that was saved.
     */
    saveTexture: function (key)
    {
        this.textureManager.renameTexture(this.texture.key, key);
        
        this._saved = true;

        return this.texture;
    },

    /**
     * Fills the Render Texture with the given color.
     *
     * @method Phaser.GameObjects.RenderTexture#fill
     * @since 3.2.0
     *
     * @param {number} rgb - The color to fill the Render Texture with.
     * @param {number} [alpha=1] - The alpha value used by the fill.
     *
     * @return {this} This Render Texture instance.
     */
    fill: function (rgb, alpha)
    {
        if (alpha === undefined) { alpha = 1; }

        var ur = ((rgb >> 16)|0) & 0xff;
        var ug = ((rgb >> 8)|0) & 0xff;
        var ub = (rgb|0) & 0xff;

        if (this.gl)
        {
            this.renderer.setFramebuffer(this.framebuffer);

            var gl = this.gl;
    
            gl.clearColor(ur / 255.0, ug / 255.0, ub / 255.0, alpha);
    
            gl.clear(gl.COLOR_BUFFER_BIT);
    
            this.renderer.setFramebuffer(null);
        }
        else
        {
            this.context.fillStyle = 'rgb(' + ur + ',' + ug + ',' + ub + ')';
            this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
        }

        return this;
    },

    /**
     * Clears the Render Texture.
     *
     * @method Phaser.GameObjects.RenderTexture#clear
     * @since 3.2.0
     *
     * @return {this} This Render Texture instance.
     */
    clear: function ()
    {
        if (this.dirty)
        {
            if (this.gl)
            {
                this.renderer.setFramebuffer(this.framebuffer);

                var gl = this.gl;
        
                gl.clearColor(0, 0, 0, 0);
        
                gl.clear(gl.COLOR_BUFFER_BIT);
        
                this.renderer.setFramebuffer(null);
            }
            else
            {
                var ctx = this.context;

                ctx.save();
                ctx.setTransform(1, 0, 0, 1, 0, 0);
                ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
                ctx.restore();
            }

            this.dirty = false;
        }

        return this;
    },

    /**
     * Draws the given object, or an array of objects, to this Render Texture.
     * 
     * It can accept any of the following:
     * 
     * * Any renderable Game Object, such as a Sprite, Text, Graphics or TileSprite.
     * * Dynamic and Static Tilemap Layers.
     * * A Group. The contents of which will be iterated and drawn in turn.
     * * A Container. The contents of which will be iterated fully, and drawn in turn.
     * * A Scene's Display List. Pass in `Scene.children` to draw the whole list.
     * * Another Render Texture.
     * * A Texture Frame instance.
     * * A string. This is used to look-up a texture from the Texture Manager.
     * 
     * Note: You cannot draw a Render Texture to itself.
     * 
     * If passing in a Group or Container it will only draw children that return `true`
     * when their `willRender()` method is called. I.e. a Container with 10 children,
     * 5 of which have `visible=false` will only draw the 5 visible ones.
     * 
     * If passing in an array of Game Objects it will draw them all, regardless if
     * they pass a `willRender` check or not.
     * 
     * You can pass in a string in which case it will look for a texture in the Texture
     * Manager matching that string, and draw the base frame. If you need to specify
     * exactly which frame to draw then use the method `drawFrame` instead.
     * 
     * You can pass in the `x` and `y` coordinates to draw the objects at. The use of
     * the coordinates differ based on what objects are being drawn. If the object is
     * a Group, Container or Display List, the coordinates are _added_ to the positions
     * of the children. For all other types of object, the coordinates are exact.
     * 
     * The `alpha` and `tint` values are only used by Texture Frames.
     * Game Objects use their own alpha and tint values when being drawn.
     * 
     * Calling this method causes the WebGL batch to flush, so it can write the texture
     * data to the framebuffer being used internally. The batch is flushed at the end,
     * after the entries have been iterated. So if you've a bunch of objects to draw,
     * try and pass them in an array in one single call, rather than making lots of
     * separate calls.
     *
     * @method Phaser.GameObjects.RenderTexture#draw
     * @since 3.2.0
     *
     * @param {any} entries - Any renderable Game Object, or Group, Container, Display List, other Render Texture, Texture Frame or an array of any of these.
     * @param {number} [x] - The x position to draw the Frame at, or the offset applied to the object.
     * @param {number} [y] - The y position to draw the Frame at, or the offset applied to the object.
     * @param {number} [alpha] -  The alpha value. Only used for Texture Frames and if not specified defaults to the `globalAlpha` property. Game Objects use their own current alpha value.
     * @param {number} [tint] -  WebGL only. The tint color value. Only used for Texture Frames and if not specified defaults to the `globalTint` property. Game Objects use their own current tint value.
     *
     * @return {this} This Render Texture instance.
     */
    draw: function (entries, x, y, alpha, tint)
    {
        if (alpha === undefined) { alpha = this.globalAlpha; }

        if (tint === undefined)
        {
            tint = (this.globalTint >> 16) + (this.globalTint & 0xff00) + ((this.globalTint & 0xff) << 16);
        }
        else
        {
            tint = (tint >> 16) + (tint & 0xff00) + ((tint & 0xff) << 16);
        }

        if (!Array.isArray(entries))
        {
            entries = [ entries ];
        }

        var gl = this.gl;

        this.camera.preRender(1, 1, 1);

        if (gl)
        {
            this.renderer.setFramebuffer(this.framebuffer);

            var pipeline = this.pipeline;
    
            pipeline.projOrtho(0, this.width, 0, this.height, -1000.0, 1000.0);

            this.batchList(entries, x, y, alpha, tint);

            pipeline.flush();

            this.renderer.setFramebuffer(null);

            pipeline.projOrtho(0, pipeline.width, pipeline.height, 0, -1000.0, 1000.0);
        }
        else
        {
            this.renderer.setContext(this.context);

            this.batchList(entries, x, y, alpha, tint);

            this.renderer.setContext();
        }

        this.dirty = true;

        return this;
    },

    /**
     * Draws the Texture Frame to the Render Texture at the given position.
     * 
     * Textures are referenced by their string-based keys, as stored in the Texture Manager.
     * 
     * ```javascript
     * var rt = this.add.renderTexture(0, 0, 800, 600);
     * rt.drawFrame(key, frame);
     * ```
     * 
     * You can optionally provide a position, alpha and tint value to apply to the frame
     * before it is drawn.
     * 
     * Calling this method will cause a batch flush, so if you've got a stack of things to draw
     * in a tight loop, try using the `draw` method instead.
     * 
     * If you need to draw a Sprite to this Render Texture, use the `draw` method instead.
     *
     * @method Phaser.GameObjects.RenderTexture#drawFrame
     * @since 3.12.0
     *
     * @param {string} key - The key of the texture to be used, as stored in the Texture Manager.
     * @param {(string|integer)} [frame] - The name or index of the frame within the Texture.
     * @param {number} [x=0] - The x position to draw the frame at.
     * @param {number} [y=0] - The y position to draw the frame at.
     * @param {number} [alpha] - The alpha to use. If not specified it uses the `globalAlpha` property.
     * @param {number} [tint] - WebGL only. The tint color to use. If not specified it uses the `globalTint` property.
     *
     * @return {this} This Render Texture instance.
     */
    drawFrame: function (key, frame, x, y, alpha, tint)
    {
        if (x === undefined) { x = 0; }
        if (y === undefined) { y = 0; }
        if (alpha === undefined) { alpha = this.globalAlpha; }

        if (tint === undefined)
        {
            tint = (this.globalTint >> 16) + (this.globalTint & 0xff00) + ((this.globalTint & 0xff) << 16);
        }
        else
        {
            tint = (tint >> 16) + (tint & 0xff00) + ((tint & 0xff) << 16);
        }

        var gl = this.gl;
        var textureFrame = this.textureManager.getFrame(key, frame);

        if (textureFrame)
        {
            this.camera.preRender(1, 1, 1);

            if (gl)
            {
                this.renderer.setFramebuffer(this.framebuffer);
    
                var pipeline = this.pipeline;
        
                pipeline.projOrtho(0, this.width, 0, this.height, -1000.0, 1000.0);
        
                pipeline.batchTextureFrame(textureFrame, x, y, tint, alpha, this.camera.matrix, null);
            
                pipeline.flush();
        
                this.renderer.setFramebuffer(null);
        
                pipeline.projOrtho(0, pipeline.width, pipeline.height, 0, -1000.0, 1000.0);
            }
            else
            {
                this.batchTextureFrame(textureFrame, x, y, alpha, tint);
            }

            this.dirty = true;
        }

        return this;
    },

    /**
     * Internal method that handles the drawing of an array of children.
     *
     * @method Phaser.GameObjects.RenderTexture#batchList
     * @private
     * @since 3.12.0
     *
     * @param {array} children - The array of Game Objects to draw.
     * @param {number} x - The x position to offset the Game Object by.
     * @param {number} y - The y position to offset the Game Object by.
     * @param {number} [alpha] - The alpha to use. If not specified it uses the `globalAlpha` property.
     * @param {number} [tint] - The tint color to use. If not specified it uses the `globalTint` property.
     */
    batchList: function (children, x, y, alpha, tint)
    {
        for (var i = 0; i < children.length; i++)
        {
            var entry = children[i];

            if (!entry || entry === this)
            {
                continue;
            }

            if (entry.renderWebGL || entry.renderCanvas)
            {
                //  Game Objects
                this.drawGameObject(entry, x, y);
            }
            else if (entry.isParent || entry.list)
            {
                //  Groups / Display Lists
                this.batchGroup(entry.getChildren(), x, y);
            }
            else if (typeof entry === 'string')
            {
                //  Texture key
                this.batchTextureFrameKey(entry, null, x, y, alpha, tint);
            }
            else if (entry instanceof Frame)
            {
                //  Texture Frame instance
                this.batchTextureFrame(entry, x, y, alpha, tint);
            }
            else if (Array.isArray(entry))
            {
                //  Another Array
                this.batchList(entry, x, y, alpha, tint);
            }
        }
    },

    /**
     * Internal method that handles the drawing a Phaser Group contents.
     *
     * @method Phaser.GameObjects.RenderTexture#batchGroup
     * @private
     * @since 3.12.0
     *
     * @param {array} children - The array of Game Objects to draw.
     * @param {number} x - The x position to offset the Game Object by.
     * @param {number} y - The y position to offset the Game Object by.
     */
    batchGroup: function (children, x, y)
    {
        if (x === undefined) { x = 0; }
        if (y === undefined) { y = 0; }

        for (var i = 0; i < children.length; i++)
        {
            var entry = children[i];

            if (entry.willRender())
            {
                var tx = entry.x + x;
                var ty = entry.y + y;

                this.drawGameObject(entry, tx, ty);
            }
        }
    },

    /**
     * Internal method that handles drawing a single Phaser Game Object to this Render Texture using WebGL.
     *
     * @method Phaser.GameObjects.RenderTexture#batchGameObjectWebGL
     * @private
     * @since 3.12.0
     *
     * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object to draw.
     * @param {number} x - The x position to draw the Game Object at.
     * @param {number} y - The y position to draw the Game Object at.
     */
    batchGameObjectWebGL: function (gameObject, x, y)
    {
        if (x === undefined) { x = gameObject.x; }
        if (y === undefined) { y = gameObject.y; }

        var prevX = gameObject.x;
        var prevY = gameObject.y;

        this.renderer.setBlendMode(gameObject.blendMode);

        gameObject.setPosition(x, y);

        gameObject.renderWebGL(this.renderer, gameObject, 0, this.camera, null);

        gameObject.setPosition(prevX, prevY);
    },

    /**
     * Internal method that handles drawing a single Phaser Game Object to this Render Texture using Canvas.
     *
     * @method Phaser.GameObjects.RenderTexture#batchGameObjectCanvas
     * @private
     * @since 3.12.0
     *
     * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object to draw.
     * @param {number} x - The x position to draw the Game Object at.
     * @param {number} y - The y position to draw the Game Object at.
     */
    batchGameObjectCanvas: function (gameObject, x, y)
    {
        if (x === undefined) { x = gameObject.x; }
        if (y === undefined) { y = gameObject.y; }

        var prevX = gameObject.x;
        var prevY = gameObject.y;

        gameObject.setPosition(x, y);

        gameObject.renderCanvas(this.renderer, gameObject, 0, this.camera, null);

        gameObject.setPosition(prevX, prevY);
    },

    /**
     * Internal method that handles the drawing of an array of children.
     *
     * @method Phaser.GameObjects.RenderTexture#batchTextureFrameKey
     * @private
     * @since 3.12.0
     *
     * @param {string} key - The key of the texture to be used, as stored in the Texture Manager.
     * @param {(string|integer)} [frame] - The name or index of the frame within the Texture.
     * @param {number} x - The x position to offset the Game Object by.
     * @param {number} y - The y position to offset the Game Object by.
     * @param {number} [alpha] - The alpha to use. If not specified it uses the `globalAlpha` property.
     * @param {number} [tint] - The tint color to use. If not specified it uses the `globalTint` property.
     * 
     * @return {boolean} `true` if the frame was found and drawn, otherwise `false`.
     */
    batchTextureFrameKey: function (key, frame, x, y, alpha, tint)
    {
        var textureFrame = this.textureManager.getFrame(key, frame);

        if (textureFrame)
        {
            this.batchTextureFrame(textureFrame, x, y, alpha, tint);
        }
    },

    /**
     * Internal method that handles the drawing of a Texture Frame to this Render Texture.
     *
     * @method Phaser.GameObjects.RenderTexture#batchTextureFrame
     * @private
     * @since 3.12.0
     *
     * @param {Phaser.Textures.Frame} textureFrame - The Texture Frame to draw.
     * @param {number} x - The x position to draw the Frame at.
     * @param {number} y - The y position to draw the Frame at.
     * @param {number} [tint] - A tint color to be applied to the frame drawn to the Render Texture.
     */
    batchTextureFrame: function (textureFrame, x, y, alpha, tint)
    {
        if (x === undefined) { x = 0; }
        if (y === undefined) { y = 0; }

        if (this.gl)
        {
            this.pipeline.batchTextureFrame(textureFrame, x, y, tint, alpha, this.camera.matrix, null);
        }
        else
        {
            var ctx = this.context;
            var cd = textureFrame.canvasData;
            var source = textureFrame.source.image;
    
            var matrix = this.camera.matrix;
    
            ctx.globalAlpha = this.globalAlpha;

            ctx.setTransform(matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);

            ctx.drawImage(source, cd.x, cd.y, cd.width, cd.height, x, y, cd.width, cd.height);
        }
    },

    /**
     * Internal destroy handler, called as part of the destroy process.
     *
     * @method Phaser.GameObjects.RenderTexture#preDestroy
     * @protected
     * @since 3.9.0
     */
    preDestroy: function ()
    {
        if (!this._saved)
        {
            CanvasPool.remove(this.canvas);

            if (this.gl)
            {
                this.renderer.deleteFramebuffer(this.framebuffer);
            }

            this.texture.destroy();
        }
    }

});

module.exports = RenderTexture;
