if (pipeline)
{
this.current = pipeline;

/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Class = require('../../utils/Class');
var CONST = require('./pipelines/const');
var CustomMap = require('../../structs/Map');

//  Default Phaser 3 Pipelines
var BitmapMaskPipeline = require('./pipelines/BitmapMaskPipeline');
var GraphicsPipeline = require('./pipelines/GraphicsPipeline');
var LightPipeline = require('./pipelines/LightPipeline');
var MultiPipeline = require('./pipelines/MultiPipeline');
var PointLightPipeline = require('./pipelines/PointLightPipeline');
var RopePipeline = require('./pipelines/RopePipeline');
var SinglePipeline = require('./pipelines/SinglePipeline');
var UtilityPipeline = require('./pipelines/UtilityPipeline');

/**
 * @classdesc
 * The Pipeline Manager is responsible for the creation, activation, running and destruction
 * of WebGL Pipelines in Phaser 3.
 *
 * The `WebGLRenderer` owns a single instance of the Pipeline Manager, which you can access
 * via the `WebGLRenderer.pipelines` property.
 *
 * By default, there are 5 pipelines installed into the Pipeline Manager when Phaser boots:
 *
 * 1. The Multi Pipeline. Responsible for all multi-texture rendering, i.e. Sprites, Shapes.
 * 2. The Single Pipeline. Responsible for rendering Game Objects that explicitly require one bound texture.
 * 3. The Rope Pipeline. Responsible for rendering the Rope Game Object.
 * 4. The Light Pipeline. Responsible for rendering the Light Game Object.
 * 5. The Bitmap Mask Pipeline. Responsible for Bitmap Mask rendering.
 *
 * You can add your own custom pipeline via the `PipelineManager.add` method. Pipelines are
 * identified by unique string-based keys.
 *
 * @class PipelineManager
 * @memberof Phaser.Renderer.WebGL
 * @constructor
 * @since 3.50.0
 *
 * @param {Phaser.Renderer.WebGL.WebGLRenderer} renderer - A reference to the WebGL Renderer that owns this Pipeline Manager.
 */
var PipelineManager = new Class({

    initialize:

    function PipelineManager (renderer)
    {
        /**
         * A reference to the Game instance.
         *
         * @name Phaser.Renderer.WebGL.PipelineManager#game
         * @type {Phaser.Game}
         * @since 3.50.0
         */
        this.game = renderer.game;

        /**
         * A reference to the WebGL Renderer instance.
         *
         * @name Phaser.Renderer.WebGL.PipelineManager#renderer
         * @type {Phaser.Renderer.WebGL.WebGLRenderer}
         * @since 3.50.0
         */
        this.renderer = renderer;

        /**
         * This map stores all pipeline classes available in this manager.
         *
         * The Utility Class must always come first.
         *
         * @name Phaser.Renderer.WebGL.PipelineManager#classes
         * @type {Phaser.Structs.Map.<string, Class>}
         * @since 3.50.0
         */
        this.classes = new CustomMap([
            [ CONST.UTILITY_PIPELINE, UtilityPipeline ],
            [ CONST.MULTI_PIPELINE, MultiPipeline ],
            [ CONST.BITMAPMASK_PIPELINE, BitmapMaskPipeline ],
            [ CONST.SINGLE_PIPELINE, SinglePipeline ],
            [ CONST.ROPE_PIPELINE, RopePipeline ],
            [ CONST.LIGHT_PIPELINE, LightPipeline ],
            [ CONST.POINTLIGHT_PIPELINE, PointLightPipeline ],
            [ CONST.GRAPHICS_PIPELINE, GraphicsPipeline ]
        ]);

        /**
         * This map stores all Post FX Pipeline classes available in this manager.
         *
         * @name Phaser.Renderer.WebGL.PipelineManager#postPipelineClasses
         * @type {Phaser.Structs.Map.<string, Class>}
         * @since 3.50.0
         */
        this.postPipelineClasses = new CustomMap();

        /**
         * This map stores all pipeline instances in this manager.
         *
         * This is populated with the default pipelines in the `boot` method.
         *
         * @name Phaser.Renderer.WebGL.PipelineManager#pipelines
         * @type {Phaser.Structs.Map.<string, Phaser.Renderer.WebGL.WebGLPipeline>}
         * @since 3.50.0
         */
        this.pipelines = new CustomMap();

        /**
         * Current pipeline in use by the WebGLRenderer.
         *
         * @name Phaser.Renderer.WebGL.PipelineManager#current
         * @type {Phaser.Renderer.WebGL.WebGLPipeline}
         * @default null
         * @since 3.50.0
         */
        this.current = null;

        /**
         * The previous WebGLPipeline that was in use.
         *
         * This is set when `clearPipeline` is called and restored in `rebindPipeline` if none is given.
         *
         * @name Phaser.Renderer.WebGL.PipelineManager#previous
         * @type {Phaser.Renderer.WebGL.WebGLPipeline}
         * @default null
         * @since 3.50.0
         */
        this.previous = null;

        /**
         * A constant-style reference to the Multi Pipeline Instance.
         *
         * This is the default Phaser 3 pipeline and is used by the WebGL Renderer to manage
         * camera effects and more. This property is set during the `boot` method.
         *
         * @name Phaser.Renderer.WebGL.PipelineManager#MULTI_PIPELINE
         * @type {Phaser.Renderer.WebGL.Pipelines.MultiPipeline}
         * @default null
         * @since 3.50.0
         */
        this.MULTI_PIPELINE = null;

        /**
         * A constant-style reference to the Bitmap Mask Pipeline Instance.
         *
         * This is the default Phaser 3 mask pipeline and is used Game Objects using
         * a Bitmap Mask. This property is set during the `boot` method.
         *
         * @name Phaser.Renderer.WebGL.PipelineManager#BITMAPMASK_PIPELINE
         * @type {Phaser.Renderer.WebGL.Pipelines.BitmapMaskPipeline}
         * @default null
         * @since 3.50.0
         */
        this.BITMAPMASK_PIPELINE = null;

        /**
         * A constant-style reference to the Utility Pipeline Instance.
         *
         * @name Phaser.Renderer.WebGL.PipelineManager#UTILITY_PIPELINE
         * @type {Phaser.Renderer.WebGL.Pipelines.UtilityPipeline}
         * @default null
         * @since 3.50.0
         */
        this.UTILITY_PIPELINE = null;

        /**
         * A reference to the Full Frame 1 Render Target that belongs to the
         * Utility Pipeline. This property is set during the `boot` method.
         *
         * This Render Target is the full size of the renderer.
         *
         * You can use this directly in Post FX Pipelines for multi-target effects.
         * However, be aware that these targets are shared between all post fx pipelines.
         *
         * @name Phaser.Renderer.WebGL.PipelineManager#fullFrame1
         * @type {Phaser.Renderer.WebGL.RenderTarget}
         * @default null
         * @since 3.50.0
         */
        this.fullFrame1;

        /**
         * A reference to the Full Frame 2 Render Target that belongs to the
         * Utility Pipeline. This property is set during the `boot` method.
         *
         * This Render Target is the full size of the renderer.
         *
         * You can use this directly in Post FX Pipelines for multi-target effects.
         * However, be aware that these targets are shared between all post fx pipelines.
         *
         * @name Phaser.Renderer.WebGL.PipelineManager#fullFrame2
         * @type {Phaser.Renderer.WebGL.RenderTarget}
         * @default null
         * @since 3.50.0
         */
        this.fullFrame2;

        /**
         * A reference to the Half Frame 1 Render Target that belongs to the
         * Utility Pipeline. This property is set during the `boot` method.
         *
         * This Render Target is half the size of the renderer.
         *
         * You can use this directly in Post FX Pipelines for multi-target effects.
         * However, be aware that these targets are shared between all post fx pipelines.
         *
         * @name Phaser.Renderer.WebGL.PipelineManager#halfFrame1
         * @type {Phaser.Renderer.WebGL.RenderTarget}
         * @default null
         * @since 3.50.0
         */
        this.halfFrame1;

        /**
         * A reference to the Half Frame 2 Render Target that belongs to the
         * Utility Pipeline. This property is set during the `boot` method.
         *
         * This Render Target is half the size of the renderer.
         *
         * You can use this directly in Post FX Pipelines for multi-target effects.
         * However, be aware that these targets are shared between all post fx pipelines.
         *
         * @name Phaser.Renderer.WebGL.PipelineManager#halfFrame2
         * @type {Phaser.Renderer.WebGL.RenderTarget}
         * @default null
         * @since 3.50.0
         */
        this.halfFrame2;
    },

    /**
     * Internal boot handler, called by the WebGLRenderer durings its boot process.
     *
     * Adds all of the default pipelines, based on the game config, and then calls
     * the `boot` method on each one of them.
     *
     * Finally, the default pipeline is set.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#boot
     * @since 3.50.0
     *
     * @param {Phaser.Types.Core.PipelineConfig} [pipelineConfig] - The pipeline configuration object as set in the Game Config.
     */
    boot: function (pipelineConfig)
    {
        //  Install each of the default pipelines

        var instance;
        var pipelineName;

        var _this = this;
        var game = this.game;

        this.classes.each(function (pipelineName, pipeline)
        {
            instance = _this.add(pipelineName, new pipeline({ game: game }));

            if (pipelineName === CONST.UTILITY_PIPELINE)
            {
                _this.UTILITY_PIPELINE = instance;

                //  FBO references
                _this.fullFrame1 = instance.fullFrame1;
                _this.fullFrame2 = instance.fullFrame2;
                _this.halfFrame1 = instance.halfFrame1;
                _this.halfFrame2 = instance.halfFrame2;
            }
        });

        //  Our const-like references
        this.MULTI_PIPELINE = this.get(CONST.MULTI_PIPELINE);
        this.BITMAPMASK_PIPELINE = this.get(CONST.BITMAPMASK_PIPELINE);

        //  And now the ones in the config, if any

        if (pipelineConfig)
        {
            for (pipelineName in pipelineConfig)
            {
                var pipelineClass = pipelineConfig[pipelineName];

                instance = new pipelineClass(game);

                if (instance.isPostFX)
                {
                    this.postPipelineClasses.set(pipelineName, pipelineClass);
                }
                else if (!this.has(pipelineName))
                {
                    this.classes.set(pipelineName, pipelineClass);

                    this.add(pipelineName, instance);
                }
            }
        }
    },

    /**
     * Adds a pipeline instance to this Pipeline Manager.
     *
     * The name of the instance must be unique within this manager.
     *
     * Make sure to pass an instance to this method, not a base class.
     *
     * For example, you should pass it like this:
     *
     * ```javascript
     * this.add('yourName', new CustomPipeline());`
     * ```
     *
     * and **not** like this:
     *
     * ```javascript
     * this.add('yourName', CustomPipeline);`
     * ```
     *
     * To add a **Post Pipeline**, see `addPostPipeline` instead.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#add
     * @since 3.50.0
     *
     * @param {string} name - A unique string-based key for the pipeline within the manager.
     * @param {Phaser.Renderer.WebGL.WebGLPipeline} pipeline - A pipeline _instance_ which must extend `WebGLPipeline`.
     *
     * @return {Phaser.Renderer.WebGL.WebGLPipeline} The pipeline instance that was passed.
     */
    add: function (name, pipeline)
    {
        if (pipeline.isPostFX)
        {
            console.warn(name + ' is a Post Pipeline. Use `addPostPipeline` instead');

            return;
        }

        var pipelines = this.pipelines;
        var renderer = this.renderer;

        if (!pipelines.has(name))
        {
            pipeline.name = name;
            pipeline.manager = this;

            pipelines.set(name, pipeline);
        }
        else
        {
            console.warn('Pipeline exists: ' + name);
        }

        if (!pipeline.hasBooted)
        {
            pipeline.boot();
        }

        if (renderer.width !== 0 && renderer.height !== 0)
        {
            pipeline.resize(renderer.width, renderer.height);
        }

        return pipeline;
    },

    /**
     * Adds a Post Pipeline to this Pipeline Manager.
     *
     * Make sure to pass a base class to this method, not an instance.
     *
     * For example, you should pass it like this:
     *
     * ```javascript
     * this.addPostPipeline('yourName', CustomPipeline);`
     * ```
     *
     * and **not** like this:
     *
     * ```javascript
     * this.addPostPipeline('yourName', new CustomPipeline());`
     * ```
     *
     * To add a regular pipeline, see the `add` method instead.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#addPostPipeline
     * @since 3.50.0
     *
     * @param {string} name - A unique string-based key for the pipeline within the manager.
     * @param {function} pipeline - A pipeline class which must extend `PostFXPipeline`.
     *
     * @return {this} This Pipeline Manager.
     */
    addPostPipeline: function (name, pipeline)
    {
        if (!this.postPipelineClasses.has(name))
        {
            this.postPipelineClasses.set(name, pipeline);
        }
    },

    /**
     * Flushes the current pipeline, if one is bound.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#flush
     * @since 3.50.0
     */
    flush: function ()
    {
        if (this.current)
        {
            this.current.flush();
        }
    },

    /**
     * Checks if a pipeline is present in this Pipeline Manager.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#has
     * @since 3.50.0
     *
     * @param {(string|Phaser.Renderer.WebGL.WebGLPipeline)} pipeline - Either the string-based name of the pipeline to get, or a pipeline instance to look-up.
     *
     * @return {boolean} `true` if the given pipeline is loaded, otherwise `false`.
     */
    has: function (pipeline)
    {
        var pipelines = this.pipelines;

        if (typeof pipeline === 'string')
        {
            return pipelines.has(pipeline);
        }
        else if (pipelines.contains(pipeline))
        {
            return true;
        }

        return false;
    },

    /**
     * Returns the pipeline instance based on the given name, or instance.
     *
     * If no instance, or matching name, exists in this manager, it returns `undefined`.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#get
     * @since 3.50.0
     *
     * @param {(string|Phaser.Renderer.WebGL.WebGLPipeline)} pipeline - Either the string-based name of the pipeline to get, or a pipeline instance to look-up.
     *
     * @return {Phaser.Renderer.WebGL.WebGLPipeline} The pipeline instance, or `undefined` if not found.
     */
    get: function (pipeline)
    {
        var pipelines = this.pipelines;

        if (typeof pipeline === 'string')
        {
            return pipelines.get(pipeline);
        }
        else if (pipelines.contains(pipeline))
        {
            return pipeline;
        }
    },

    /**
     * Returns a _new instance_ of the post pipeline based on the given name, or class.
     *
     * If no instance, or matching name, exists in this manager, it returns `undefined`.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#getPostPipeline
     * @since 3.50.0
     *
     * @param {(string|function|Phaser.Renderer.WebGL.Pipelines.PostFXPipeline)} pipeline - Either the string-based name of the pipeline to get, or a pipeline instance, or class to look-up.
     * @param {Phaser.GameObjects.GameObject} [gameObject] - If this post pipeline is being installed into a Game Object or Camera, this is a reference to it.
     *
     * @return {Phaser.Renderer.WebGL.Pipelines.PostFXPipeline} The pipeline instance, or `undefined` if not found.
     */
    getPostPipeline: function (pipeline, gameObject)
    {
        var pipelineClasses = this.postPipelineClasses;

        var instance;

        if (typeof pipeline === 'string')
        {
            instance = pipelineClasses.get(pipeline);
        }
        else if (typeof pipeline === 'function')
        {
            //  A class
            if (pipelineClasses.contains(pipeline))
            {
                instance = pipeline;
            }
        }
        else if (typeof pipeline === 'object')
        {
            //  Instance
            instance = pipelineClasses.get(pipeline.name);
        }

        if (instance)
        {
            var newPipeline = new instance(this.game);

            if (gameObject)
            {
                newPipeline.gameObject = gameObject;
            }

            return newPipeline;
        }
    },

    /**
     * Removes a pipeline instance based on the given name.
     *
     * If no pipeline matches the name, this method does nothing.
     *
     * Note that the pipeline will not be flushed or destroyed, it's simply removed from
     * this manager.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#remove
     * @since 3.50.0
     *
     * @param {string} name - The name of the pipeline to be removed.
     * @param {boolean} [removeClass=true] - Remove the pipeline class as well as the instance?
     * @param {boolean} [removePostPipelineClass=true] - Remove the post pipeline class as well as the instance?
     */
    remove: function (name, removeClass, removePostPipelineClass)
    {
        if (removeClass === undefined) { removeClass = true; }
        if (removePostPipelineClass === undefined) { removePostPipelineClass = true; }

        this.pipelines.delete(name);

        if (removeClass)
        {
            this.classes.delete(name);
        }

        if (removePostPipelineClass)
        {
            this.postPipelineClasses.delete(name);
        }
    },

    /**
     * Sets the current pipeline to be used by the `WebGLRenderer`.
     *
     * This method accepts a pipeline instance as its parameter, not the name.
     *
     * If the pipeline isn't already the current one it will call `WebGLPipeline.bind` and then `onBind`.
     *
     * You cannot set Post FX Pipelines using this method. To use a Post FX Pipeline, you should
     * apply it to either a Camera, Container or other supporting Game Object.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#set
     * @since 3.50.0
     *
     * @param {Phaser.Renderer.WebGL.WebGLPipeline} pipeline - The pipeline instance to be set as current.
     * @param {Phaser.GameObjects.GameObject} [gameObject] - The Game Object that invoked this pipeline, if any.
     * @param {Phaser.Renderer.WebGL.WebGLShader} [currentShader] - The shader to set as being current.
     *
     * @return {Phaser.Renderer.WebGL.WebGLPipeline} The pipeline that was set, or undefined if it couldn't be set.
     */
    set: function (pipeline, gameObject, currentShader)
    {
        if (pipeline.isPostFX)
        {
            return;
        }

        if (!this.isCurrent(pipeline, currentShader))
        {
            this.flush();

            if (this.current)
            {
                this.current.unbind();
            }

            this.current = pipeline;

            pipeline.bind(currentShader);
        }

        pipeline.onBind(gameObject);

        return pipeline;
    },

    /**
     * This method is called by the `WebGLPipeline.batchQuad` method, right before a quad
     * belonging to a Game Object is about to be added to the batch. It causes a batch
     * flush, then calls the `preBatch` method on the post-fx pipelines belonging to the
     * Game Object.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#preBatch
     * @since 3.50.0
     *
     * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object about to be batched.
     */
    preBatch: function (gameObject)
    {
        if (gameObject.hasPostPipeline)
        {
            this.flush();

            var pipelines = gameObject.postPipelines;

            //  Iterate in reverse because we need them stacked in the order they're in the array
            for (var i = pipelines.length - 1; i >= 0; i--)
            {
                var pipeline = pipelines[i];

                if (pipeline.active)
                {
                    pipeline.preBatch(gameObject);
                }
            }
        }
    },

    /**
     * This method is called by the `WebGLPipeline.batchQuad` method, right after a quad
     * belonging to a Game Object has been added to the batch. It causes a batch
     * flush, then calls the `postBatch` method on the post-fx pipelines belonging to the
     * Game Object.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#postBatch
     * @since 3.50.0
     *
     * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object that was just added to the batch.
     */
    postBatch: function (gameObject)
    {
        if (gameObject.hasPostPipeline)
        {
            this.flush();

            var pipelines = gameObject.postPipelines;

            for (var i = 0; i < pipelines.length; i++)
            {
                var pipeline = pipelines[i];

                if (pipeline.active)
                {
                    pipeline.postBatch(gameObject);
                }
            }
        }
    },

    /**
     * Called at the start of the `WebGLRenderer.preRenderCamera` method.
     *
     * If the Camera has post pipelines set, it will flush the batch and then call the
     * `preBatch` method on the post-fx pipelines belonging to the Camera.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#preBatchCamera
     * @since 3.50.0
     *
     * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera about to be rendered.
     */
    preBatchCamera: function (camera)
    {
        if (camera.hasPostPipeline)
        {
            this.flush();

            var pipelines = camera.postPipelines;

            //  Iterate in reverse because we need them stacked in the order they're in the array
            for (var i = pipelines.length - 1; i >= 0; i--)
            {
                var pipeline = pipelines[i];

                if (pipeline.active)
                {
                    pipeline.preBatch(camera);
                }
            }
        }
    },

    /**
     * Called at the end of the `WebGLRenderer.postRenderCamera` method.
     *
     * If the Camera has post pipelines set, it will flush the batch and then call the
     * `postBatch` method on the post-fx pipelines belonging to the Camera.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#postBatchCamera
     * @since 3.50.0
     *
     * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera that was just rendered.
     */
    postBatchCamera: function (camera)
    {
        if (camera.hasPostPipeline)
        {
            this.flush();

            var pipelines = camera.postPipelines;

            for (var i = 0; i < pipelines.length; i++)
            {
                var pipeline = pipelines[i];

                if (pipeline.active)
                {
                    pipeline.postBatch(camera);
                }
            }
        }
    },

    /**
     * Checks to see if the given pipeline is already the active pipeline, both within this
     * Pipeline Manager and also has the same shader set in the Renderer.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#isCurrent
     * @since 3.50.0
     *
     * @param {Phaser.Renderer.WebGL.WebGLPipeline} pipeline - The pipeline instance to be checked.
     * @param {Phaser.Renderer.WebGL.WebGLShader} [currentShader] - The shader to set as being current.
     *
     * @return {boolean} `true` if the given pipeline is already the current pipeline, otherwise `false`.
     */
    isCurrent: function (pipeline, currentShader)
    {
        var renderer = this.renderer;
        var current = this.current;

        if (current && !currentShader)
        {
            currentShader = current.currentShader;
        }

        return !(current !== pipeline || currentShader.program !== renderer.currentProgram);
    },

    /**
     * Copy the `source` Render Target to the `target` Render Target.
     *
     * You can optionally set the brightness factor of the copy.
     *
     * The difference between this method and `drawFrame` is that this method
     * uses a faster copy shader, where only the brightness can be modified.
     * If you need color level manipulation, see `drawFrame` instead.
     *
     * The copy itself is handled by the Utility Pipeline.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#copyFrame
     * @since 3.50.0
     *
     * @param {Phaser.Renderer.WebGL.RenderTarget} source - The source Render Target.
     * @param {Phaser.Renderer.WebGL.RenderTarget} [target] - The target Render Target.
     * @param {number} [brightness=1] - The brightness value applied to the frame copy.
     * @param {boolean} [clear=true] - Clear the target before copying?
     * @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target?
     *
     * @return {this} This Pipeline Manager instance.
     */
    copyFrame: function (source, target, brightness, clear, clearAlpha)
    {
        var pipeline = this.setUtility(this.UTILITY_PIPELINE.copyShader);

        pipeline.copyFrame(source, target, brightness, clear, clearAlpha);

        return this;
    },

    /**
     * Pops the framebuffer from the renderers FBO stack and sets that as the active target,
     * then draws the `source` Render Target to it. It then resets the renderer textures.
     *
     * This should be done when you need to draw the _final_ results of a pipeline to the game
     * canvas, or the next framebuffer in line on the FBO stack. You should only call this once
     * in the `onDraw` handler and it should be the final thing called. Be careful not to call
     * this if you need to actually use the pipeline shader, instead of the copy shader. In
     * those cases, use the `bindAndDraw` method.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#copyToGame
     * @since 3.50.0
     *
     * @param {Phaser.Renderer.WebGL.RenderTarget} source - The Render Target to draw from.
     */
    copyToGame: function (source)
    {
        var pipeline = this.setUtility(this.UTILITY_PIPELINE.copyShader);

        pipeline.copyToGame(source);

        return this;
    },

    /**
     * Copy the `source` Render Target to the `target` Render Target, using the
     * given Color Matrix.
     *
     * The difference between this method and `copyFrame` is that this method
     * uses a color matrix shader, where you have full control over the luminance
     * values used during the copy. If you don't need this, you can use the faster
     * `copyFrame` method instead.
     *
     * The copy itself is handled by the Utility Pipeline.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#drawFrame
     * @since 3.50.0
     *
     * @param {Phaser.Renderer.WebGL.RenderTarget} source - The source Render Target.
     * @param {Phaser.Renderer.WebGL.RenderTarget} [target] - The target Render Target.
     * @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target?
     * @param {Phaser.Display.ColorMatrix} [colorMatrix] - The Color Matrix to use when performing the draw.
     *
     * @return {this} This Pipeline Manager instance.
     */
    drawFrame: function (source, target, clearAlpha, colorMatrix)
    {
        var pipeline = this.setUtility(this.UTILITY_PIPELINE.colorMatrixShader);

        pipeline.drawFrame(source, target, clearAlpha, colorMatrix);

        return this;
    },

    /**
     * Draws the `source1` and `source2` Render Targets to the `target` Render Target
     * using a linear blend effect, which is controlled by the `strength` parameter.
     *
     * The draw itself is handled by the Utility Pipeline.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#blendFrames
     * @since 3.50.0
     *
     * @param {Phaser.Renderer.WebGL.RenderTarget} source1 - The first source Render Target.
     * @param {Phaser.Renderer.WebGL.RenderTarget} source2 - The second source Render Target.
     * @param {Phaser.Renderer.WebGL.RenderTarget} [target] - The target Render Target.
     * @param {number} [strength=1] - The strength of the blend.
     * @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target?
     *
     * @return {this} This Pipeline Manager instance.
     */
    blendFrames: function (source1, source2, target, strength, clearAlpha)
    {
        var pipeline = this.setUtility(this.UTILITY_PIPELINE.linearShader);

        pipeline.blendFrames(source1, source2, target, strength, clearAlpha);

        return this;
    },

    /**
     * Draws the `source1` and `source2` Render Targets to the `target` Render Target
     * using an additive blend effect, which is controlled by the `strength` parameter.
     *
     * The draw itself is handled by the Utility Pipeline.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#blendFramesAdditive
     * @since 3.50.0
     *
     * @param {Phaser.Renderer.WebGL.RenderTarget} source1 - The first source Render Target.
     * @param {Phaser.Renderer.WebGL.RenderTarget} source2 - The second source Render Target.
     * @param {Phaser.Renderer.WebGL.RenderTarget} [target] - The target Render Target.
     * @param {number} [strength=1] - The strength of the blend.
     * @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target?
     *
     * @return {this} This Pipeline Manager instance.
     */
    blendFramesAdditive: function (source1, source2, target, strength, clearAlpha)
    {
        var pipeline = this.setUtility(this.UTILITY_PIPELINE.addShader);

        pipeline.blendFrames(source1, source2, target, strength, clearAlpha);

        return this;
    },

    /**
     * Clears the given Render Target.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#clearFrame
     * @since 3.50.0
     *
     * @param {Phaser.Renderer.WebGL.RenderTarget} target - The Render Target to clear.
     * @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target?
     */
    clearFrame: function (target, clearAlpha)
    {
        this.UTILITY_PIPELINE.clearFrame(target, clearAlpha);

        return this;
    },

    /**
     * Returns `true` if the current pipeline is forced to use texture unit zero.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#forceZero
     * @since 3.50.0
     *
     * @return {boolean} `true` if the current pipeline is forced to use texture unit zero.
     */
    forceZero: function ()
    {
        return (this.current && this.current.forceZero);
    },

    /**
     * Sets the Multi Pipeline to be the currently bound pipeline.
     *
     * This is the default Phaser 3 rendering pipeline.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#setMulti
     * @since 3.50.0
     *
     * @return {Phaser.Renderer.WebGL.Pipelines.MultiPipeline} The Multi Pipeline instance.
     */
    setMulti: function ()
    {
        return this.set(this.MULTI_PIPELINE);
    },

    /**
     * Sets the Utility Pipeline to be the currently bound pipeline.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#setUtility
     * @since 3.50.0
     *
     * @param {Phaser.Renderer.WebGL.WebGLShader} [currentShader] - The shader to set as being current.
     *
     * @return {Phaser.Renderer.WebGL.Pipelines.UtilityPipeline} The Utility Pipeline instance.
     */
    setUtility: function (currentShader)
    {
        return this.UTILITY_PIPELINE.bind(currentShader);
    },

    /**
     * Use this to reset the gl context to the state that Phaser requires to continue rendering.
     *
     * Calling this will:
     *
     * * Disable `DEPTH_TEST`, `CULL_FACE` and `STENCIL_TEST`.
     * * Clear the depth buffer and stencil buffers.
     * * Reset the viewport size.
     * * Reset the blend mode.
     * * Bind a blank texture as the active texture on texture unit zero.
     * * Rebinds the given pipeline instance.
     *
     * You should call this if you have previously called `clear`, and then wish to return
     * rendering control to Phaser again.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#rebind
     * @since 3.50.0
     *
     * @param {Phaser.Renderer.WebGL.WebGLPipeline} [pipeline] - The pipeline instance to be rebound. If not given, the previous pipeline will be bound.
     */
    rebind: function (pipeline)
    {
        if (pipeline === undefined && this.previous)
        {
            pipeline = this.previous;
        }

        if (!pipeline)
        {
            return;
        }

        var renderer = this.renderer;
        var gl = renderer.gl;

        gl.disable(gl.DEPTH_TEST);
        gl.disable(gl.CULL_FACE);

        if (renderer.hasActiveStencilMask())
        {
            gl.clear(gl.DEPTH_BUFFER_BIT);
        }
        else
        {
            //  If there wasn't a stencil mask set before this call, we can disable it safely
            gl.disable(gl.STENCIL_TEST);
            gl.clear(gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
        }

        gl.viewport(0, 0, renderer.width, renderer.height);

        renderer.currentProgram = null;

        renderer.setBlendMode(0, true);

        this.current = pipeline;

        pipeline.rebind();

        renderer.resetTextures();
    },

    /**
     * Flushes the current pipeline being used and then clears it, along with the
     * the current shader program and vertex buffer from the `WebGLRenderer`.
     *
     * Then resets the blend mode to NORMAL.
     *
     * Call this before jumping to your own gl context handler, and then call `rebind` when
     * you wish to return control to Phaser again.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#clear
     * @since 3.50.0
     */
    clear: function ()
    {
        var renderer = this.renderer;

        this.flush();

        if (this.current)
        {
            this.current.unbind();
            this.previous = this.current;
            this.current = null;
        }
        else
        {
            this.previous = null;
        }

        renderer.currentProgram = null;

        renderer.setBlendMode(0, true);
    },

    /**
     * Destroy the Pipeline Manager, cleaning up all related resources and references.
     *
     * @method Phaser.Renderer.WebGL.PipelineManager#destroy
     * @since 3.50.0
     */
    destroy: function ()
    {
        this.flush();

        this.classes.clear();
        this.postPipelineClasses.clear();
        this.pipelines.clear();

        this.renderer = null;
        this.game = null;
        this.classes = null;
        this.postPipelineClasses = null;
        this.pipelines = null;
        this.current = null;
        this.previous = null;
    }

});

module.exports = PipelineManager;


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Triangle = require('./Triangle');

/**
 * Builds an equilateral triangle. In the equilateral triangle, all the sides are the same length (congruent) and all the angles are the same size (congruent).
 * The x/y specifies the top-middle of the triangle (x1/y1) and length is the length of each side.
 *
 * @function Phaser.Geom.Triangle.BuildEquilateral
 * @since 3.0.0
 *
 * @param {number} x - x coordinate of the top point of the triangle.
 * @param {number} y - y coordinate of the top point of the triangle.
 * @param {number} length - Length of each side of the triangle.
 *
 * @return {Phaser.Geom.Triangle} The Triangle object of the given size.
 */
var BuildEquilateral = function (x, y, length)
{
    var height = length * (Math.sqrt(3) / 2);

    var x1 = x;
    var y1 = y;

    var x2 = x + (length / 2);
    var y2 = y + height;

    var x3 = x - (length / 2);
    var y3 = y + height;

    return new Triangle(x1, y1, x2, y2, x3, y3);
};

module.exports = BuildEquilateral;


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

/**
 * The Scene Systems Destroy Event.
 *
 * This event is dispatched by a Scene during the Scene Systems destroy process.
 *
 * Listen to it from a Scene using `this.scene.events.on('destroy', listener)`.
 *
 * You should destroy any resources that may be in use by your Scene in this event handler.
 *
 * @event Phaser.Scenes.Events#DESTROY
 * @since 3.0.0
 *
 * @param {Phaser.Scenes.Systems} sys - A reference to the Scene Systems class of the Scene that emitted this event.
 */
module.exports = 'destroy';


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

/**
 * The Scene Create Event.
 *
 * This event is dispatched by a Scene after it has been created by the Scene Manager.
 *
 * If a Scene has a `create` method then this event is emitted _after_ that has run.
 *
 * If there is a transition, this event will be fired after the `TRANSITION_START` event.
 *
 * Listen to it from a Scene using `this.scene.events.on('create', listener)`.
 *
 * @event Phaser.Scenes.Events#CREATE
 * @since 3.17.0
 *
 * @param {Phaser.Scene} scene - A reference to the Scene that emitted this event.
 */
module.exports = 'create';


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Class = require('../../utils/Class');
var Components = require('../components');
var GameObject = require('../GameObject');
var GetFastValue = require('../../utils/object/GetFastValue');
var Extend = require('../../utils/object/Extend');
var SetValue = require('../../utils/object/SetValue');
var ShaderRender = require('./ShaderRender');
var TransformMatrix = require('../components/TransformMatrix');

/**
 * @classdesc
 * A Shader Game Object.
 *
 * This Game Object allows you to easily add a quad with its own shader into the display list, and manipulate it
 * as you would any other Game Object, including scaling, rotating, positioning and adding to Containers. Shaders
 * can be masked with either Bitmap or Geometry masks and can also be used as a Bitmap Mask for a Camera or other
 * Game Object. They can also be made interactive and used for input events.
 *
 * It works by taking a reference to a `Phaser.Display.BaseShader` instance, as found in the Shader Cache. These can
 * be created dynamically at runtime, or loaded in via the GLSL File Loader:
 *
 * ```javascript
 * function preload ()
 * {
 *     this.load.glsl('fire', 'shaders/fire.glsl.js');
 * }
 *
 * function create ()
 * {
 *     this.add.shader('fire', 400, 300, 512, 512);
 * }
 * ```
 *
 * Please see the Phaser 3 Examples GitHub repo for examples of loading and creating shaders dynamically.
 *
 * Due to the way in which they work, you cannot directly change the alpha or blend mode of a Shader. This should
 * be handled via exposed uniforms in the shader code itself.
 *
 * By default a Shader will be created with a standard set of uniforms. These were added to match those
 * found on sites such as ShaderToy or GLSLSandbox, and provide common functionality a shader may need,
 * such as the timestamp, resolution or pointer position. You can replace them by specifying your own uniforms
 * in the Base Shader.
 *
 * These Shaders work by halting the current pipeline during rendering, creating a viewport matched to the
 * size of this Game Object and then renders a quad using the bound shader. At the end, the pipeline is restored.
 *
 * Because it blocks the pipeline it means it will interrupt any batching that is currently going on, so you should
 * use these Game Objects sparingly. If you need to have a fully batched custom shader, then please look at using
 * a custom pipeline instead. However, for background or special masking effects, they are extremely effective.
 *
 * @class Shader
 * @extends Phaser.GameObjects.GameObject
 * @memberof Phaser.GameObjects
 * @constructor
 * @webglOnly
 * @since 3.17.0
 *
 * @extends Phaser.GameObjects.Components.ComputedSize
 * @extends Phaser.GameObjects.Components.Depth
 * @extends Phaser.GameObjects.Components.GetBounds
 * @extends Phaser.GameObjects.Components.Mask
 * @extends Phaser.GameObjects.Components.Origin
 * @extends Phaser.GameObjects.Components.ScrollFactor
 * @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 {(string|Phaser.Display.BaseShader)} key - The key of the shader to use from the shader cache, or a BaseShader instance.
 * @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 {number} [width=128] - The width of the Game Object.
 * @param {number} [height=128] - The height of the Game Object.
 * @param {string[]} [textures] - Optional array of texture keys to bind to the iChannel0...3 uniforms. The textures must already exist in the Texture Manager.
 * @param {any} [textureData] - Additional texture data if you want to create shader with none NPOT textures.
 */
var Shader = new Class({

    Extends: GameObject,

    Mixins: [
        Components.ComputedSize,
        Components.Depth,
        Components.GetBounds,
        Components.Mask,
        Components.Origin,
        Components.ScrollFactor,
        Components.Transform,
        Components.Visible,
        ShaderRender
    ],

    initialize:

    function Shader (scene, key, x, y, width, height, textures, textureData)
    {
        if (x === undefined) { x = 0; }
        if (y === undefined) { y = 0; }
        if (width === undefined) { width = 128; }
        if (height === undefined) { height = 128; }

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

        /**
         * This Game Object cannot have a blend mode, so skip all checks.
         *
         * @name Phaser.GameObjects.Shader#blendMode
         * @type {number}
         * @private
         * @since 3.17.0
         */
        this.blendMode = -1;

        /**
         * The underlying shader object being used.
         * Empty by default and set during a call to the `setShader` method.
         *
         * @name Phaser.GameObjects.Shader#shader
         * @type {Phaser.Display.BaseShader}
         * @since 3.17.0
         */
        this.shader;

        var renderer = scene.sys.renderer;

        /**
         * A reference to the current renderer.
         * Shaders only work with the WebGL Renderer.
         *
         * @name Phaser.GameObjects.Shader#renderer
         * @type {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)}
         * @since 3.17.0
         */
        this.renderer = renderer;

        /**
         * The WebGL context belonging to the renderer.
         *
         * @name Phaser.GameObjects.Shader#gl
         * @type {WebGLRenderingContext}
         * @since 3.17.0
         */
        this.gl = renderer.gl;

        /**
         * Raw byte buffer of vertices this Shader uses.
         *
         * @name Phaser.GameObjects.Shader#vertexData
         * @type {ArrayBuffer}
         * @since 3.17.0
         */
        this.vertexData = new ArrayBuffer(6 * (Float32Array.BYTES_PER_ELEMENT * 2));

        /**
         * The WebGL vertex buffer object this shader uses.
         *
         * @name Phaser.GameObjects.Shader#vertexBuffer
         * @type {WebGLBuffer}
         * @since 3.17.0
         */
        this.vertexBuffer = renderer.createVertexBuffer(this.vertexData.byteLength, this.gl.STREAM_DRAW);

        /**
         * The WebGL shader program this shader uses.
         *
         * @name Phaser.GameObjects.Shader#program
         * @type {WebGLProgram}
         * @since 3.17.0
         */
        this.program = null;

        /**
         * Uint8 view to the vertex raw buffer. Used for uploading vertex buffer resources to the GPU.
         *
         * @name Phaser.GameObjects.Shader#bytes
         * @type {Uint8Array}
         * @since 3.17.0
         */
        this.bytes = new Uint8Array(this.vertexData);

        /**
         * Float32 view of the array buffer containing the shaders vertices.
         *
         * @name Phaser.GameObjects.Shader#vertexViewF32
         * @type {Float32Array}
         * @since 3.17.0
         */
        this.vertexViewF32 = new Float32Array(this.vertexData);

        /**
         * A temporary Transform Matrix, re-used internally during batching.
         *
         * @name Phaser.GameObjects.Shader#_tempMatrix1
         * @private
         * @type {Phaser.GameObjects.Components.TransformMatrix}
         * @since 3.17.0
         */
        this._tempMatrix1 = new TransformMatrix();

        /**
         * A temporary Transform Matrix, re-used internally during batching.
         *
         * @name Phaser.GameObjects.Shader#_tempMatrix2
         * @private
         * @type {Phaser.GameObjects.Components.TransformMatrix}
         * @since 3.17.0
         */
        this._tempMatrix2 = new TransformMatrix();

        /**
         * A temporary Transform Matrix, re-used internally during batching.
         *
         * @name Phaser.GameObjects.Shader#_tempMatrix3
         * @private
         * @type {Phaser.GameObjects.Components.TransformMatrix}
         * @since 3.17.0
         */
        this._tempMatrix3 = new TransformMatrix();

        /**
         * The view matrix the shader uses during rendering.
         *
         * @name Phaser.GameObjects.Shader#viewMatrix
         * @type {Float32Array}
         * @readonly
         * @since 3.17.0
         */
        this.viewMatrix = new Float32Array([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]);

        /**
         * The projection matrix the shader uses during rendering.
         *
         * @name Phaser.GameObjects.Shader#projectionMatrix
         * @type {Float32Array}
         * @readonly
         * @since 3.17.0
         */
        this.projectionMatrix = new Float32Array([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]);

        /**
         * The default uniform mappings. These can be added to (or replaced) by specifying your own uniforms when
         * creating this shader game object. The uniforms are updated automatically during the render step.
         *
         * The defaults are:
         *
         * `resolution` (2f) - Set to the size of this shader.
         * `time` (1f) - The elapsed game time, in seconds.
         * `mouse` (2f) - If a pointer has been bound (with `setPointer`), this uniform contains its position each frame.
         * `date` (4fv) - A vec4 containing the year, month, day and time in seconds.
         * `sampleRate` (1f) - Sound sample rate. 44100 by default.
         * `iChannel0...3` (sampler2D) - Input channels 0 to 3. `null` by default.
         *
         * @name Phaser.GameObjects.Shader#uniforms
         * @type {any}
         * @since 3.17.0
         */
        this.uniforms = {};

        /**
         * The pointer bound to this shader, if any.
         * Set via the chainable `setPointer` method, or by modifying this property directly.
         *
         * @name Phaser.GameObjects.Shader#pointer
         * @type {Phaser.Input.Pointer}
         * @since 3.17.0
         */
        this.pointer = null;

        /**
         * The cached width of the renderer.
         *
         * @name Phaser.GameObjects.Shader#_rendererWidth
         * @type {number}
         * @private
         * @since 3.17.0
         */
        this._rendererWidth = renderer.width;

        /**
         * The cached height of the renderer.
         *
         * @name Phaser.GameObjects.Shader#_rendererHeight
         * @type {number}
         * @private
         * @since 3.17.0
         */
        this._rendererHeight = renderer.height;

        /**
         * Internal texture count tracker.
         *
         * @name Phaser.GameObjects.Shader#_textureCount
         * @type {number}
         * @private
         * @since 3.17.0
         */
        this._textureCount = 0;

        /**
         * A reference to the GL Frame Buffer this Shader is drawing to.
         * This property is only set if you have called `Shader.setRenderToTexture`.
         *
         * @name Phaser.GameObjects.Shader#framebuffer
         * @type {?WebGLFramebuffer}
         * @since 3.19.0
         */
        this.framebuffer = null;

        /**
         * A reference to the WebGLTexture this Shader is rendering to.
         * This property is only set if you have called `Shader.setRenderToTexture`.
         *
         * @name Phaser.GameObjects.Shader#glTexture
         * @type {?WebGLTexture}
         * @since 3.19.0
         */
        this.glTexture = null;

        /**
         * A flag that indicates if this Shader has been set to render to a texture instead of the display list.
         *
         * This property is `true` if you have called `Shader.setRenderToTexture`, otherwise it's `false`.
         *
         * A Shader that is rendering to a texture _does not_ appear on the display list.
         *
         * @name Phaser.GameObjects.Shader#renderToTexture
         * @type {boolean}
         * @readonly
         * @since 3.19.0
         */
        this.renderToTexture = false;

        /**
         * A reference to the Phaser.Textures.Texture that has been stored in the Texture Manager for this Shader.
         *
         * This property is only set if you have called `Shader.setRenderToTexture`, otherwise it is `null`.
         *
         * @name Phaser.GameObjects.Shader#texture
         * @type {Phaser.Textures.Texture}
         * @since 3.19.0
         */
        this.texture = null;

        this.setPosition(x, y);
        this.setSize(width, height);
        this.setOrigin(0.5, 0.5);
        this.setShader(key, textures, textureData);
    },

    /**
     * Compares the renderMask with the renderFlags to see if this Game Object will render or not.
     * Also checks the Game Object against the given Cameras exclusion list.
     *
     * @method Phaser.GameObjects.Shader#willRender
     * @since 3.0.0
     *
     * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera to check against this Game Object.
     *
     * @return {boolean} True if the Game Object should be rendered, otherwise false.
     */
    willRender: function (camera)
    {
        if (this.renderToTexture)
        {
            return true;
        }
        else
        {
            return !(GameObject.RENDER_MASK !== this.renderFlags || (this.cameraFilter !== 0 && (this.cameraFilter & camera.id)));
        }
    },

    /**
     * Changes this Shader so instead of rendering to the display list it renders to a
     * WebGL Framebuffer and WebGL Texture instead. This allows you to use the output
     * of this shader as an input for another shader, by mapping a sampler2D uniform
     * to it.
     *
     * After calling this method the `Shader.framebuffer` and `Shader.glTexture` properties
     * are populated.
     *
     * Additionally, you can provide a key to this method. Doing so will create a Phaser Texture
     * from this Shader and save it into the Texture Manager, allowing you to then use it for
     * any texture-based Game Object, such as a Sprite or Image:
     *
     * ```javascript
     * var shader = this.add.shader('myShader', x, y, width, height);
     *
     * shader.setRenderToTexture('doodle');
     *
     * this.add.image(400, 300, 'doodle');
     * ```
     *
     * Note that it stores an active reference to this Shader. That means as this shader updates,
     * so does the texture and any object using it to render with. Also, if you destroy this
     * shader, be sure to clear any objects that may have been using it as a texture too.
     *
     * You can access the Phaser Texture that is created via the `Shader.texture` property.
     *
     * 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.Shader#setRenderToTexture
     * @since 3.19.0
     *
     * @param {string} [key] - The unique key to store the texture as within the global Texture Manager.
     * @param {boolean} [flipY=false] - Does this texture need vertically flipping before rendering? This should usually be set to `true` if being fed from a buffer.
     *
     * @return {this} This Shader instance.
     */
    setRenderToTexture: function (key, flipY)
    {
        if (flipY === undefined) { flipY = false; }

        if (!this.renderToTexture)
        {
            var width = this.width;
            var height = this.height;
            var renderer = this.renderer;

            this.glTexture = renderer.createTextureFromSource(null, width, height, 0);

            this.glTexture.flipY = flipY;

            this.framebuffer = renderer.createFramebuffer(width, height, this.glTexture, false);

            this._rendererWidth = width;
            this._rendererHeight = height;

            this.renderToTexture = true;

            this.projOrtho(0, this.width, this.height, 0);

            if (key)
            {
                this.texture = this.scene.sys.textures.addGLTexture(key, this.glTexture, width, height);
            }
        }

        //  And now render at least once, so our texture isn't blank on the first update

        if (this.shader)
        {
            renderer.pipelines.clear();

            this.load();
            this.flush();

            renderer.pipelines.rebind();
        }

        return this;
    },

    /**
     * Sets the fragment and, optionally, the vertex shader source code that this Shader will use.
     * This will immediately delete the active shader program, if set, and then create a new one
     * with the given source. Finally, the shader uniforms are initialized.
     *
     * @method Phaser.GameObjects.Shader#setShader
     * @since 3.17.0
     *
     * @param {(string|Phaser.Display.BaseShader)} key - The key of the shader to use from the shader cache, or a BaseShader instance.
     * @param {string[]} [textures] - Optional array of texture keys to bind to the iChannel0...3 uniforms. The textures must already exist in the Texture Manager.
     * @param {any} [textureData] - Additional texture data.
     *
     * @return {this} This Shader instance.
     */
    setShader: function (key, textures, textureData)
    {
        if (textures === undefined) { textures = []; }

        if (typeof key === 'string')
        {
            var cache = this.scene.sys.cache.shader;

            if (!cache.has(key))
            {
                console.warn('Shader missing: ' + key);
                return this;
            }

            this.shader = cache.get(key);
        }
        else
        {
            this.shader = key;
        }

        var gl = this.gl;
        var renderer = this.renderer;

        if (this.program)
        {
            gl.deleteProgram(this.program);
        }

        var program = renderer.createProgram(this.shader.vertexSrc, this.shader.fragmentSrc);

        //  The default uniforms available within the vertex shader
        gl.uniformMatrix4fv(gl.getUniformLocation(program, 'uViewMatrix'), false, this.viewMatrix);
        gl.uniformMatrix4fv(gl.getUniformLocation(program, 'uProjectionMatrix'), false, this.projectionMatrix);
        gl.uniform2f(gl.getUniformLocation(program, 'uResolution'), this.width, this.height);

        this.program = program;

        var d = new Date();

        //  The default uniforms available within the fragment shader
        var defaultUniforms = {
            resolution: { type: '2f', value: { x: this.width, y: this.height } },
            time: { type: '1f', value: 0 },
            mouse: { type: '2f', value: { x: this.width / 2, y: this.height / 2 } },
            date: { type: '4fv', value: [ d.getFullYear(), d.getMonth(), d.getDate(), d.getHours() * 60 * 60 + d.getMinutes() * 60 + d.getSeconds() ] },
            sampleRate: { type: '1f', value: 44100.0 },
            iChannel0: { type: 'sampler2D', value: null, textureData: { repeat: true } },
            iChannel1: { type: 'sampler2D', value: null, textureData: { repeat: true } },
            iChannel2: { type: 'sampler2D', value: null, textureData: { repeat: true } },
            iChannel3: { type: 'sampler2D', value: null, textureData: { repeat: true } }
        };

        if (this.shader.uniforms)
        {
            this.uniforms = Extend(true, {}, this.shader.uniforms, defaultUniforms);
        }
        else
        {
            this.uniforms = defaultUniforms;
        }

        for (var i = 0; i < 4; i++)
        {
            if (textures[i])
            {
                this.setSampler2D('iChannel' + i, textures[i], i, textureData);
            }
        }

        this.initUniforms();

        this.projOrtho(0, this._rendererWidth, this._rendererHeight, 0);

        return this;
    },

    /**
     * Binds a Phaser Pointer object to this Shader.
     *
     * The screen position of the pointer will be set in to the shaders `mouse` uniform
     * automatically every frame. Call this method with no arguments to unbind the pointer.
     *
     * @method Phaser.GameObjects.Shader#setPointer
     * @since 3.17.0
     *
     * @param {Phaser.Input.Pointer} [pointer] - The Pointer to bind to this shader.
     *
     * @return {this} This Shader instance.
     */
    setPointer: function (pointer)
    {
        this.pointer = pointer;

        return this;
    },

    /**
     * Sets this shader to use an orthographic projection matrix.
     * This matrix is stored locally in the `projectionMatrix` property,
     * as well as being bound to the `uProjectionMatrix` uniform.
     *
     * @method Phaser.GameObjects.Shader#projOrtho
     * @since 3.17.0
     *
     * @param {number} left - The left value.
     * @param {number} right - The right value.
     * @param {number} bottom - The bottom value.
     * @param {number} top - The top value.
     */
    projOrtho: function (left, right, bottom, top)
    {
        var near = -1000;
        var far = 1000;

        var leftRight = 1 / (left - right);
        var bottomTop = 1 / (bottom - top);
        var nearFar = 1 / (near - far);

        var pm = this.projectionMatrix;

        pm[0] = -2 * leftRight;
        pm[5] = -2 * bottomTop;
        pm[10] = 2 * nearFar;
        pm[12] = (left + right) * leftRight;
        pm[13] = (top + bottom) * bottomTop;
        pm[14] = (far + near) * nearFar;

        var program = this.program;

        var gl = this.gl;
        var renderer = this.renderer;

        renderer.setProgram(program);

        gl.uniformMatrix4fv(gl.getUniformLocation(program, 'uProjectionMatrix'), false, this.projectionMatrix);

        this._rendererWidth = right;
        this._rendererHeight = bottom;
    },

    // Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/
    // http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf

    /**
     * Initializes all of the uniforms this shader uses.
     *
     * @method Phaser.GameObjects.Shader#initUniforms
     * @private
     * @since 3.17.0
     */
    initUniforms: function ()
    {
        var gl = this.gl;
        var map = this.renderer.glFuncMap;
        var program = this.program;

        this._textureCount = 0;

        for (var key in this.uniforms)
        {
            var uniform = this.uniforms[key];

            var type = uniform.type;
            var data = map[type];

            uniform.uniformLocation = gl.getUniformLocation(program, key);

            if (type !== 'sampler2D')
            {
                uniform.glMatrix = data.matrix;
                uniform.glValueLength = data.length;
                uniform.glFunc = data.func;
            }
        }
    },

    /**
     * Sets a sampler2D uniform on this shader where the source texture is a WebGLTexture.
     *
     * This allows you to feed the output from one Shader into another:
     *
     * ```javascript
     * let shader1 = this.add.shader(baseShader1, 0, 0, 512, 512).setRenderToTexture();
     * let shader2 = this.add.shader(baseShader2, 0, 0, 512, 512).setRenderToTexture('output');
     *
     * shader1.setSampler2DBuffer('iChannel0', shader2.glTexture, 512, 512);
     * shader2.setSampler2DBuffer('iChannel0', shader1.glTexture, 512, 512);
     * ```
     *
     * In the above code, the result of baseShader1 is fed into Shader2 as the `iChannel0` sampler2D uniform.
     * The result of baseShader2 is then fed back into shader1 again, creating a feedback loop.
     *
     * If you wish to use an image from the Texture Manager as a sampler2D input for this shader,
     * see the `Shader.setSampler2D` method.
     *
     * @method Phaser.GameObjects.Shader#setSampler2DBuffer
     * @since 3.19.0
     *
     * @param {string} uniformKey - The key of the sampler2D uniform to be updated, i.e. `iChannel0`.
     * @param {WebGLTexture} texture - A WebGLTexture reference.
     * @param {number} width - The width of the texture.
     * @param {number} height - The height of the texture.
     * @param {number} [textureIndex=0] - The texture index.
     * @param {any} [textureData] - Additional texture data.
     *
     * @return {this} This Shader instance.
     */
    setSampler2DBuffer: function (uniformKey, texture, width, height, textureIndex, textureData)
    {
        if (textureIndex === undefined) { textureIndex = 0; }
        if (textureData === undefined) { textureData = {}; }

        var uniform = this.uniforms[uniformKey];

        uniform.value = texture;

        textureData.width = width;
        textureData.height = height;

        uniform.textureData = textureData;

        this._textureCount = textureIndex;

        this.initSampler2D(uniform);

        return this;
    },

    /**
     * Sets a sampler2D uniform on this shader.
     *
     * The textureKey given is the key from the Texture Manager cache. You cannot use a single frame
     * from a texture, only the full image. Also, lots of shaders expect textures to be power-of-two sized.
     *
     * If you wish to use another Shader as a sampler2D input for this shader, see the `Shader.setSampler2DBuffer` method.
     *
     * @method Phaser.GameObjects.Shader#setSampler2D
     * @since 3.17.0
     *
     * @param {string} uniformKey - The key of the sampler2D uniform to be updated, i.e. `iChannel0`.
     * @param {string} textureKey - The key of the texture, as stored in the Texture Manager. Must already be loaded.
     * @param {number} [textureIndex=0] - The texture index.
     * @param {any} [textureData] - Additional texture data.
     *
     * @return {this} This Shader instance.
     */
    setSampler2D: function (uniformKey, textureKey, textureIndex, textureData)
    {
        if (textureIndex === undefined) { textureIndex = 0; }

        var textureManager = this.scene.sys.textures;

        if (textureManager.exists(textureKey))
        {
            var frame = textureManager.getFrame(textureKey);
            var uniform = this.uniforms[uniformKey];
            var source = frame.source;

            uniform.textureKey = textureKey;
            uniform.source = source.image;
            uniform.value = frame.glTexture;

            if (source.isGLTexture)
            {
                if (!textureData)
                {
                    textureData = {};
                }

                textureData.width = source.width;
                textureData.height = source.height;
            }

            if (textureData)
            {
                uniform.textureData = textureData;
            }

            this._textureCount = textureIndex;

            this.initSampler2D(uniform);
        }

        return this;
    },

    /**
     * Sets a property of a uniform already present on this shader.
     *
     * To modify the value of a uniform such as a 1f or 1i use the `value` property directly:
     *
     * ```javascript
     * shader.setUniform('size.value', 16);
     * ```
     *
     * You can use dot notation to access deeper values, for example:
     *
     * ```javascript
     * shader.setUniform('resolution.value.x', 512);
     * ```
     *
     * The change to the uniform will take effect the next time the shader is rendered.
     *
     * @method Phaser.GameObjects.Shader#setUniform
     * @since 3.17.0
     *
     * @param {string} key - The key of the uniform to modify. Use dots for deep properties, i.e. `resolution.value.x`.
     * @param {any} value - The value to set into the uniform.
     *
     * @return {this} This Shader instance.
     */
    setUniform: function (key, value)
    {
        SetValue(this.uniforms, key, value);

        return this;
    },

    /**
     * Returns the uniform object for the given key, or `null` if the uniform couldn't be found.
     *
     * @method Phaser.GameObjects.Shader#getUniform
     * @since 3.17.0
     *
     * @param {string} key - The key of the uniform to return the value for.
     *
     * @return {any} A reference to the uniform object. This is not a copy, so modifying it will update the original object also.
     */
    getUniform: function (key)
    {
        return GetFastValue(this.uniforms, key, null);
    },

    /**
     * A short-cut method that will directly set the texture being used by the `iChannel0` sampler2D uniform.
     *
     * The textureKey given is the key from the Texture Manager cache. You cannot use a single frame
     * from a texture, only the full image. Also, lots of shaders expect textures to be power-of-two sized.
     *
     * @method Phaser.GameObjects.Shader#setChannel0
     * @since 3.17.0
     *
     * @param {string} textureKey - The key of the texture, as stored in the Texture Manager. Must already be loaded.
     * @param {any} [textureData] - Additional texture data.
     *
     * @return {this} This Shader instance.
     */
    setChannel0: function (textureKey, textureData)
    {
        return this.setSampler2D('iChannel0', textureKey, 0, textureData);
    },

    /**
     * A short-cut method that will directly set the texture being used by the `iChannel1` sampler2D uniform.
     *
     * The textureKey given is the key from the Texture Manager cache. You cannot use a single frame
     * from a texture, only the full image. Also, lots of shaders expect textures to be power-of-two sized.
     *
     * @method Phaser.GameObjects.Shader#setChannel1
     * @since 3.17.0
     *
     * @param {string} textureKey - The key of the texture, as stored in the Texture Manager. Must already be loaded.
     * @param {any} [textureData] - Additional texture data.
     *
     * @return {this} This Shader instance.
     */
    setChannel1: function (textureKey, textureData)
    {
        return this.setSampler2D('iChannel1', textureKey, 1, textureData);
    },

    /**
     * A short-cut method that will directly set the texture being used by the `iChannel2` sampler2D uniform.
     *
     * The textureKey given is the key from the Texture Manager cache. You cannot use a single frame
     * from a texture, only the full image. Also, lots of shaders expect textures to be power-of-two sized.
     *
     * @method Phaser.GameObjects.Shader#setChannel2
     * @since 3.17.0
     *
     * @param {string} textureKey - The key of the texture, as stored in the Texture Manager. Must already be loaded.
     * @param {any} [textureData] - Additional texture data.
     *
     * @return {this} This Shader instance.
     */
    setChannel2: function (textureKey, textureData)
    {
        return this.setSampler2D('iChannel2', textureKey, 2, textureData);
    },

    /**
     * A short-cut method that will directly set the texture being used by the `iChannel3` sampler2D uniform.
     *
     * The textureKey given is the key from the Texture Manager cache. You cannot use a single frame
     * from a texture, only the full image. Also, lots of shaders expect textures to be power-of-two sized.
     *
     * @method Phaser.GameObjects.Shader#setChannel3
     * @since 3.17.0
     *
     * @param {string} textureKey - The key of the texture, as stored in the Texture Manager. Must already be loaded.
     * @param {any} [textureData] - Additional texture data.
     *
     * @return {this} This Shader instance.
     */
    setChannel3: function (textureKey, textureData)
    {
        return this.setSampler2D('iChannel3', textureKey, 3, textureData);
    },

    /**
     * Internal method that takes a sampler2D uniform and prepares it for use by setting the
     * gl texture parameters.
     *
     * @method Phaser.GameObjects.Shader#initSampler2D
     * @private
     * @since 3.17.0
     *
     * @param {any} uniform - The sampler2D uniform to process.
     */
    initSampler2D: function (uniform)
    {
        if (!uniform.value)
        {
            return;
        }

        var gl = this.gl;

        gl.activeTexture(gl.TEXTURE0 + this._textureCount);
        gl.bindTexture(gl.TEXTURE_2D, uniform.value);

        //  Extended texture data

        var data = uniform.textureData;

        if (data)
        {
            // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/texImage2D

            //  mag / minFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST
            //  wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT
            //  format can be: gl.LUMINANCE or gl.RGBA

            var magFilter = gl[GetFastValue(data, 'magFilter', 'linear').toUpperCase()];
            var minFilter = gl[GetFastValue(data, 'minFilter', 'linear').toUpperCase()];
            var wrapS = gl[GetFastValue(data, 'wrapS', 'repeat').toUpperCase()];
            var wrapT = gl[GetFastValue(data, 'wrapT', 'repeat').toUpperCase()];
            var format = gl[GetFastValue(data, 'format', 'rgba').toUpperCase()];

            if (data.repeat)
            {
                wrapS = gl.REPEAT;
                wrapT = gl.REPEAT;
            }

            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY);

            if (data.width)
            {
                var width = GetFastValue(data, 'width', 512);
                var height = GetFastValue(data, 'height', 2);
                var border = GetFastValue(data, 'border', 0);

                //  texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels)
                gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null);
            }
            else
            {
                //  texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels)
                gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.source);
            }

            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT);
        }

        this.renderer.setProgram(this.program);

        gl.uniform1i(uniform.uniformLocation, this._textureCount);

        this._textureCount++;
    },

    /**
     * Synchronizes all of the uniforms this shader uses.
     * Each uniforms gl function is called in turn.
     *
     * @method Phaser.GameObjects.Shader#syncUniforms
     * @private
     * @since 3.17.0
     */
    syncUniforms: function ()
    {
        var gl = this.gl;

        var uniforms = this.uniforms;
        var uniform;
        var length;
        var glFunc;
        var location;
        var value;
        var textureCount = 0;

        for (var key in uniforms)
        {
            uniform = uniforms[key];

            glFunc = uniform.glFunc;
            length = uniform.glValueLength;
            location = uniform.uniformLocation;
            value = uniform.value;

            if (value === null)
            {
                continue;
            }

            if (length === 1)
            {
                if (uniform.glMatrix)
                {
                    glFunc.call(gl, location, uniform.transpose, value);
                }
                else
                {
                    glFunc.call(gl, location, value);
                }
            }
            else if (length === 2)
            {
                glFunc.call(gl, location, value.x, value.y);
            }
            else if (length === 3)
            {
                glFunc.call(gl, location, value.x, value.y, value.z);
            }
            else if (length === 4)
            {
                glFunc.call(gl, location, value.x, value.y, value.z, value.w);
            }
            else if (uniform.type === 'sampler2D')
            {
                gl.activeTexture(gl['TEXTURE' + textureCount]);

                gl.bindTexture(gl.TEXTURE_2D, value);

                gl.uniform1i(location, textureCount);

                textureCount++;
            }
        }
    },

    /**
     * Called automatically during render.
     *
     * This method performs matrix ITRS and then stores the resulting value in the `uViewMatrix` uniform.
     * It then sets up the vertex buffer and shader, updates and syncs the uniforms ready
     * for flush to be called.
     *
     * @method Phaser.GameObjects.Shader#load
     * @since 3.17.0
     *
     * @param {Phaser.GameObjects.Components.TransformMatrix} [matrix2D] - The transform matrix to use during rendering.
     */
    load: function (matrix2D)
    {
        //  ITRS

        var gl = this.gl;
        var width = this.width;
        var height = this.height;
        var renderer = this.renderer;
        var program = this.program;
        var vm = this.viewMatrix;

        if (!this.renderToTexture)
        {
            var x = -this._displayOriginX;
            var y = -this._displayOriginY;

            vm[0] = matrix2D[0];
            vm[1] = matrix2D[1];
            vm[4] = matrix2D[2];
            vm[5] = matrix2D[3];
            vm[8] = matrix2D[4];
            vm[9] = matrix2D[5];
            vm[12] = vm[0] * x + vm[4] * y;
            vm[13] = vm[1] * x + vm[5] * y;
        }

        //  Update vertex shader uniforms

        gl.useProgram(program);

        gl.uniformMatrix4fv(gl.getUniformLocation(program, 'uViewMatrix'), false, vm);
        gl.uniform2f(gl.getUniformLocation(program, 'uResolution'), this.width, this.height);

        //  Update fragment shader uniforms

        var uniforms = this.uniforms;
        var res = uniforms.resolution;

        res.value.x = width;
        res.value.y = height;

        uniforms.time.value = renderer.game.loop.getDuration();

        var pointer = this.pointer;

        if (pointer)
        {
            var mouse = uniforms.mouse;

            var px = pointer.x / width;
            var py = 1 - pointer.y / height;

            mouse.value.x = px.toFixed(2);
            mouse.value.y = py.toFixed(2);
        }

        this.syncUniforms();
    },

    /**
     * Called automatically during render.
     *
     * Sets the active shader, loads the vertex buffer and then draws.
     *
     * @method Phaser.GameObjects.Shader#flush
     * @since 3.17.0
     */
    flush: function ()
    {
        //  Bind

        var width = this.width;
        var height = this.height;
        var program = this.program;

        var gl = this.gl;
        var vertexBuffer = this.vertexBuffer;
        var renderer = this.renderer;
        var vertexSize = Float32Array.BYTES_PER_ELEMENT * 2;

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

            gl.clearColor(0, 0, 0, 0);

            gl.clear(gl.COLOR_BUFFER_BIT);
        }

        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);

        var location = gl.getAttribLocation(program, 'inPosition');

        if (location !== -1)
        {
            gl.enableVertexAttribArray(location);

            gl.vertexAttribPointer(location, 2, gl.FLOAT, false, vertexSize, 0);
        }

        //  Draw

        var vf = this.vertexViewF32;

        vf[3] = height;
        vf[4] = width;
        vf[5] = height;
        vf[8] = width;
        vf[9] = height;
        vf[10] = width;

        //  Flush

        var vertexCount = 6;

        gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.bytes.subarray(0, vertexCount * vertexSize));

        gl.drawArrays(gl.TRIANGLES, 0, vertexCount);

        if (this.renderToTexture)
        {
            renderer.setFramebuffer(null, false);
        }
    },

    /**
     * A NOOP method so you can pass a Shader to a Container.
     * Calling this method will do nothing. It is intentionally empty.
     *
     * @method Phaser.GameObjects.Shader#setAlpha
     * @private
     * @since 3.17.0
     */
    setAlpha: function ()
    {
    },

    /**
     * A NOOP method so you can pass a Shader to a Container.
     * Calling this method will do nothing. It is intentionally empty.
     *
     * @method Phaser.GameObjects.Shader#setBlendMode
     * @private
     * @since 3.17.0
     */
    setBlendMode: function ()
    {
    },

    /**
     * Internal destroy handler, called as part of the destroy process.
     *
     * @method Phaser.GameObjects.Shader#preDestroy
     * @protected
     * @since 3.17.0
     */
    preDestroy: function ()
    {
        var gl = this.gl;

        gl.deleteProgram(this.program);
        gl.deleteBuffer(this.vertexBuffer);

        if (this.renderToTexture)
        {
            this.renderer.deleteFramebuffer(this.framebuffer);

            this.texture.destroy();

            this.framebuffer = null;
            this.glTexture = null;
            this.texture = null;
        }
    }

});

module.exports = Shader;


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

/**
 * @namespace Phaser.Tweens.Events
 */

module.exports = {

    TIMELINE_COMPLETE: require('./TIMELINE_COMPLETE_EVENT'),
    TIMELINE_LOOP: require('./TIMELINE_LOOP_EVENT'),
    TIMELINE_PAUSE: require('./TIMELINE_PAUSE_EVENT'),
    TIMELINE_RESUME: require('./TIMELINE_RESUME_EVENT'),
    TIMELINE_START: require('./TIMELINE_START_EVENT'),
    TIMELINE_UPDATE: require('./TIMELINE_UPDATE_EVENT'),
    TWEEN_ACTIVE: require('./TWEEN_ACTIVE_EVENT'),
    TWEEN_COMPLETE: require('./TWEEN_COMPLETE_EVENT'),
    TWEEN_LOOP: require('./TWEEN_LOOP_EVENT'),
    TWEEN_REPEAT: require('./TWEEN_REPEAT_EVENT'),
    TWEEN_START: require('./TWEEN_START_EVENT'),
    TWEEN_STOP: require('./TWEEN_STOP_EVENT'),
    TWEEN_UPDATE: require('./TWEEN_UPDATE_EVENT'),
    TWEEN_YOYO: require('./TWEEN_YOYO_EVENT')

};


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var GetFastValue = require('../../utils/object/GetFastValue');

/**
 * Gets the tiles in the given rectangular area (in tile coordinates) of the layer.
 *
 * @function Phaser.Tilemaps.Components.GetTilesWithin
 * @since 3.0.0
 *
 * @param {number} tileX - The left most tile index (in tile coordinates) to use as the origin of the area.
 * @param {number} tileY - The top most tile index (in tile coordinates) to use as the origin of the area.
 * @param {number} width - How many tiles wide from the `tileX` index the area will be.
 * @param {number} height - How many tiles tall from the `tileY` index the area will be.
 * @param {Phaser.Types.Tilemaps.FilteringOptions} filteringOptions - Optional filters to apply when getting the tiles.
 * @param {Phaser.Tilemaps.LayerData} layer - The Tilemap Layer to act upon.
 *
 * @return {Phaser.Tilemaps.Tile[]} Array of Tile objects.
 */
var GetTilesWithin = function (tileX, tileY, width, height, filteringOptions, layer)
{
    if (tileX === undefined) { tileX = 0; }
    if (tileY === undefined) { tileY = 0; }
    if (width === undefined) { width = layer.width; }
    if (height === undefined) { height = layer.height; }
    if (!filteringOptions) { filteringOptions = {}; }

    var isNotEmpty = GetFastValue(filteringOptions, 'isNotEmpty', false);
    var isColliding = GetFastValue(filteringOptions, 'isColliding', false);
    var hasInterestingFace = GetFastValue(filteringOptions, 'hasInterestingFace', false);

    // Clip x, y to top left of map, while shrinking width/height to match.
    if (tileX < 0)
    {
        width += tileX;
        tileX = 0;
    }

    if (tileY < 0)
    {
        height += tileY;
        tileY = 0;
    }

    // Clip width and height to bottom right of map.
    if (tileX + width > layer.width)
    {
        width = Math.max(layer.width - tileX, 0);
    }

    if (tileY + height > layer.height)
    {
        height = Math.max(layer.height - tileY, 0);
    }

    var results = [];

    for (var ty = tileY; ty < tileY + height; ty++)
    {
        for (var tx = tileX; tx < tileX + width; tx++)
        {
            var tile = layer.data[ty][tx];

            if (tile !== null)
            {
                if (isNotEmpty && tile.index === -1)
                {
                    continue;
                }

                if (isColliding && !tile.collides)
                {
                    continue;
                }

                if (hasInterestingFace && !tile.hasInterestingFace)
                {
                    continue;
                }

                results.push(tile);
            }
        }
    }

    return results;
};

module.exports = GetTilesWithin;


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

/**
 * @namespace Phaser.Math.Interpolation
 */

module.exports = {

    Bezier: require('./BezierInterpolation'),
    CatmullRom: require('./CatmullRomInterpolation'),
    CubicBezier: require('./CubicBezierInterpolation'),
    Linear: require('./LinearInterpolation'),
    QuadraticBezier: require('./QuadraticBezierInterpolation'),
    SmoothStep: require('./SmoothStepInterpolation'),
    SmootherStep: require('./SmootherStepInterpolation')

};