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

/**
* Phaser.LoaderParser parses data objects from Phaser.Loader that need more preparation before they can be inserted into the Cache.
*
* @class Phaser.LoaderParser
*/
Phaser.LoaderParser = {

    /**
    * Alias for xmlBitmapFont, for backwards compatibility.
    *
    * @method Phaser.LoaderParser.bitmapFont
    * @param {object} xml - XML data you want to parse.
    * @param {PIXI.BaseTexture} baseTexture - The BaseTexture this font uses.
    * @param {number} [xSpacing=0] - Additional horizontal spacing between the characters.
    * @param {number} [ySpacing=0] - Additional vertical spacing between the characters.
    * @return {object} The parsed Bitmap Font data.
    */
    bitmapFont: function (xml, baseTexture, xSpacing, ySpacing) {

        return this.xmlBitmapFont(xml, baseTexture, xSpacing, ySpacing);

    },

    /**
    * Parse a Bitmap Font from an XML file.
    *
    * @method Phaser.LoaderParser.xmlBitmapFont
    * @param {object} xml - XML data you want to parse.
    * @param {PIXI.BaseTexture} baseTexture - The BaseTexture this font uses.
    * @param {number} [xSpacing=0] - Additional horizontal spacing between the characters.
    * @param {number} [ySpacing=0] - Additional vertical spacing between the characters.
    * @param {Phaser.Frame} [frame] - Optional Frame, if this font is embedded in a texture atlas.
    * @return {object} The parsed Bitmap Font data.
    */
    xmlBitmapFont: function (xml, baseTexture, xSpacing, ySpacing, frame) {

        var data = {};
        var info = xml.getElementsByTagName('info')[0];
        var common = xml.getElementsByTagName('common')[0];

        data.font = info.getAttribute('face');
        data.size = parseInt(info.getAttribute('size'), 10);
        data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) + ySpacing;
        data.chars = {};

        var letters = xml.getElementsByTagName('char');

        var x = (frame) ? frame.x : 0;
        var y = (frame) ? frame.y : 0;

        for (var i = 0; i < letters.length; i++)
        {
            var charCode = parseInt(letters[i].getAttribute('id'), 10);

            data.chars[charCode] = {
                x: x + parseInt(letters[i].getAttribute('x'), 10),
                y: y + parseInt(letters[i].getAttribute('y'), 10),
                width: parseInt(letters[i].getAttribute('width'), 10),
                height: parseInt(letters[i].getAttribute('height'), 10),
                xOffset: parseInt(letters[i].getAttribute('xoffset'), 10),
                yOffset: parseInt(letters[i].getAttribute('yoffset'), 10),
                xAdvance: parseInt(letters[i].getAttribute('xadvance'), 10) + xSpacing,
                kerning: {}
            };
        }

        var kernings = xml.getElementsByTagName('kerning');

        for (i = 0; i < kernings.length; i++)
        {
            var first = parseInt(kernings[i].getAttribute('first'), 10);
            var second = parseInt(kernings[i].getAttribute('second'), 10);
            var amount = parseInt(kernings[i].getAttribute('amount'), 10);

            data.chars[second].kerning[first] = amount;
        }

        return this.finalizeBitmapFont(baseTexture, data);

    },

    /**
    * Parse a Bitmap Font from a JSON file.
    *
    * @method Phaser.LoaderParser.jsonBitmapFont
    * @param {object} json - JSON data you want to parse.
    * @param {PIXI.BaseTexture} baseTexture - The BaseTexture this font uses.
    * @param {number} [xSpacing=0] - Additional horizontal spacing between the characters.
    * @param {number} [ySpacing=0] - Additional vertical spacing between the characters.
    * @param {Phaser.Frame} [frame] - Optional Frame, if this font is embedded in a texture atlas.
    * @return {object} The parsed Bitmap Font data.
    */
    jsonBitmapFont: function (json, baseTexture, xSpacing, ySpacing, frame) {

        var data = {
            font: json.font.info._face,
            size: parseInt(json.font.info._size, 10),
            lineHeight: parseInt(json.font.common._lineHeight, 10) + ySpacing,
            chars: {}
        };

        var x = (frame) ? frame.x : 0;
        var y = (frame) ? frame.y : 0;

        json.font.chars["char"].forEach(

            function parseChar(letter) {

                var charCode = parseInt(letter._id, 10);

                data.chars[charCode] = {
                    x: x + parseInt(letter._x, 10),
                    y: y + parseInt(letter._y, 10),
                    width: parseInt(letter._width, 10),
                    height: parseInt(letter._height, 10),
                    xOffset: parseInt(letter._xoffset, 10),
                    yOffset: parseInt(letter._yoffset, 10),
                    xAdvance: parseInt(letter._xadvance, 10) + xSpacing,
                    kerning: {}
                };
            }

        );

        if (json.font.kernings && json.font.kernings.kerning)
        {
            json.font.kernings.kerning.forEach(

                function parseKerning(kerning) {

                    data.chars[kerning._second].kerning[kerning._first] = parseInt(kerning._amount, 10);

                }

            );
        }

        return this.finalizeBitmapFont(baseTexture, data);

    },

    /**
    * Finalize Bitmap Font parsing.
    *
    * @method Phaser.LoaderParser.finalizeBitmapFont
    * @private
    * @param {PIXI.BaseTexture} baseTexture - The BaseTexture this font uses.
    * @param {object} bitmapFontData - Pre-parsed bitmap font data.
    * @return {object} The parsed Bitmap Font data.
    */
    finalizeBitmapFont: function (baseTexture, bitmapFontData) {

        Object.keys(bitmapFontData.chars).forEach(

            function addTexture(charCode) {

                var letter = bitmapFontData.chars[charCode];

                letter.texture = new PIXI.Texture(baseTexture, new Phaser.Rectangle(letter.x, letter.y, letter.width, letter.height));

            }

        );

        return bitmapFontData;

    },

    /**
    * Extract PVR header from loaded binary
    *
    * @method Phaser.LoaderParser.pvr
    * @param {ArrayBuffer} arrayBuffer
    * @return {object} The parsed PVR file including texture data.
    */
    pvr: function (arrayBuffer) {

        // Reference: http://cdn.imgtec.com/sdk-documentation/PVR+File+Format.Specification.pdf
        // PVR 3 header structure
        // ---------------------------------------
        // address: 0, size: 4 bytes version
        // address: 4, size: 4 bytes flags
        // address: 8, size: 8 bytes pixel format
        // address: 16, size: 4 bytes color space
        // address: 20, size: 4 bytes channel type
        // address: 24, size: 4 bytes height
        // address: 28, size: 4 bytes width
        // address: 32, size: 4 bytes depth
        // address: 36, size: 4 bytes number of surfaces
        // address: 40, size: 4 bytes number of faces
        // address: 44, size: 4 bytes number of mipmaps
        // address: 48, size: 4 bytes meta data size
        // ---------------------------------------
        var uintArray = new Uint32Array(arrayBuffer.slice(0, 52)),
            byteArray = new Uint8Array(arrayBuffer),
            pvrHeader = null,
            pixelFormat = (uintArray[3] << 32 | uintArray[2]),
            compressionAlgorithm,
            glExtensionFormat = 0;

        if (uintArray[0] === 0x03525650 &&
            [ // Validate WebGL Pixel Format
                0, 1, 2, 3,
                6, 7, 9, 11
            ].indexOf(pixelFormat) >= 0
        ) {
            if (pixelFormat >= 0 && pixelFormat <= 3) {
                compressionAlgorithm = 'PVRTC';
            } else if (pixelFormat >= 7 && pixelFormat <= 11) {
                compressionAlgorithm = 'S3TC';
            } else if (pixelFormat === 6) {
                compressionAlgorithm = 'ETC1';
            }

            switch (pixelFormat) {
                case 0:
                    glExtensionFormat = 0x8C01;
                    break;
                case 1:
                    glExtensionFormat = 0x8C03;
                    break;
                case 2:
                    glExtensionFormat = 0x8C00;
                    break;
                case 3:
                    glExtensionFormat = 0x8C02;
                    break;
                case 6:
                    glExtensionFormat = 0x8D64;
                    break;
                case 7:
                    glExtensionFormat = 0x83F1;
                    break;
                case 9:
                    glExtensionFormat = 0x83F2;
                    break;
                case 11:
                    glExtensionFormat = 0x83F3;
                    break;
                default:
                    glExtensionFormat = -1;
            }

            pvrHeader = {
                complete: true,
                fileFormat: 'PVR',
                compressionAlgorithm: compressionAlgorithm,
                flags: uintArray[1],
                pixelFormat: pixelFormat,
                colorSpace: uintArray[4],
                channelType: uintArray[5],
                height: uintArray[6],
                width: uintArray[7],
                depth: uintArray[8],
                numberOfSurfaces: uintArray[9],
                numberOfFaces: uintArray[10],
                numberOfMipmaps: uintArray[11],
                metaDataSize: uintArray[12],
                textureData: byteArray.subarray(52 + uintArray[12], byteArray.byteLength),
                glExtensionFormat: glExtensionFormat
            };
        }

        return pvrHeader;

    },

    /**
    * Extract DDS header from loaded binary
    *
    * @method Phaser.LoaderParser.dds
    * @param {ArrayBuffer} arrayBuffer
    * @return {object} The parsed DDS file including texture data.
    */
    dds: function (arrayBuffer) {

        // Reference at: https://msdn.microsoft.com/en-us/library/windows/desktop/bb943982(v=vs.85).aspx
        // DDS header structure
        // ---------------------------------------
        // address: 0, size: 4 bytes Identifier 'DDS '
        // address: 4, size: 4 bytes size
        // address: 8, size: 4 bytes flags
        // address: 12, size: 4 bytes height
        // address: 16, size: 4 bytes width
        // address: 20, size: 4 bytes pitch or linear size
        // address: 24, size: 4 bytes depth
        // address: 28, size: 4 bytes mipmap count
        // address: 32, size: 44 bytes reserved space 1
        // address: 76, size: 4 pixel format size
        // address: 80, size: 4 pixel format flag
        // address: 84, size: 4 pixel format four CC
        // address: 88, size: 4 pixel format bit count
        // address: 92, size: 4 pixel format R bit mask
        // address: 96, size: 4 pixel format G bit mask
        // address: 100, size: 4 pixel format B bit mask
        // address: 104, size: 4 pixel format A bit mask
        // address: 108, size: 4 caps 1
        // address: 112, size: 4 caps 2
        // address: 116, size: 4 caps 3
        // address: 120, size: 4 caps 4
        // address: 124, size: 4 reserved 2
        // -- DXT10 extension
        // address: 130, size: 4 DXGI Format
        // address: 134, size: 4 resource dimension
        // address: 138, size: 4 misc flag
        // address: 142, size: 4 array size
        // address: 146, size: 4 misc flag 2
        // ---------------------------------------
        var byteArray = new Uint8Array(arrayBuffer),
            uintArray = new Uint32Array(arrayBuffer),
            ddsHeader = null;

        if (byteArray[0] === 0x44 &&
            byteArray[1] === 0x44 &&
            byteArray[2] === 0x53 &&
            byteArray[3] === 0x20) {
            ddsHeader = {
                complete: true,
                fileFormat: 'DDS',
                compressionAlgorithm: 'S3TC',
                size: uintArray[1],
                flags: uintArray[2],
                height: uintArray[3],
                width: uintArray[4],
                pitch: uintArray[5],
                depth: uintArray[6],
                mipmapCount: uintArray[7],
                formatSize: uintArray[19],
                formatFlag: uintArray[19],
                formatFourCC: [
                    String.fromCharCode(byteArray[84]),
                    String.fromCharCode(byteArray[85]),
                    String.fromCharCode(byteArray[86]),
                    String.fromCharCode(byteArray[87])
                ].join(''),
                formatBitCount: uintArray[21],
                formatRBitMask: uintArray[22],
                formatGBitMask: uintArray[23],
                formatBBitMask: uintArray[24],
                formatABitMask: uintArray[25],
                caps1: uintArray[26],
                caps2: uintArray[27],
                caps3: uintArray[28],
                caps4: uintArray[29],
                reserved2: uintArray[30],
                DXGIFormat: null,
                resourceDimension: null,
                miscFlag: null,
                arraySize: null,
                textureData: byteArray.subarray(uintArray[1] + 4, byteArray.byteLength)
            };
            if (ddsHeader.formatFourCC === 'DX10') {
                ddsHeader.DXGIFormat = uintArray[31];
                ddsHeader.resourceDimension = uintArray[32];
                ddsHeader.miscFlag = uintArray[33];
                ddsHeader.arraySize = uintArray[34];
                ddsHeader.miscFlag = uintArray[35];
            }
        }

        return ddsHeader;

    },

    /**
    * Extract KTX header from loaded binary
    *
    * @method Phaser.LoaderParser.ktx
    * @param {ArrayBuffer} arrayBuffer
    * @return {object} The parsed KTX file including texture data.
    */
    ktx: function (arrayBuffer) {

        // Reference: https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
        // KTX header structure
        // ---------------------------------------
        // address: 0, size 12 bytes: Identifier '«KTX 11»\r\n\x1A\n'
        // address: 12, size 4 bytes: endianness
        // address: 16, size 4 bytes: GL type
        // address: 20, size 4 bytes: GL type size
        // address: 24, size 4 bytes: GL format
        // address: 28, size 4 bytes: GL internal format
        // address: 32, size 4 bytes: GL base internal format
        // address: 36, size 4 bytes: pixel width
        // address: 40, size 4 bytes: pixel height
        // address: 44, size 4 bytes: pixel depth
        // address: 48, size 4 bytes: number of array elements
        // address: 52, size 4 bytes: number of faces
        // address: 56, size 4 bytes: number of mipmap levels
        // address: 60, size 4 bytes: bytes of key value data
        // address: 64, size 4 bytes: key and value bytes size
        // address: X, size 1 byte : key and value
        // address: X, size 1 byte : value padding
        // address: X, size 4 byte : image size
        // ---------------------------------------
        var byteArray = new Uint8Array(arrayBuffer),
            uintArray = new Uint32Array(arrayBuffer),
            ktxHeader = null,
            imageSizeOffset = 16 + (uintArray[15] / 4) | 0,
            imageSize = uintArray[imageSizeOffset],
            glInternalFormat = uintArray[7],
            compressionAlgorithm = 0;

        if (byteArray[0] === 0xAB && byteArray[1] === 0x4B &&
            byteArray[2] === 0x54 && byteArray[3] === 0x58 &&
            byteArray[4] === 0x20 && byteArray[5] === 0x31 &&
            byteArray[6] === 0x31 && byteArray[7] === 0xBB &&
            byteArray[8] === 0x0D && byteArray[9] === 0x0A &&
            byteArray[10] === 0x1A && byteArray[11] === 0x0A &&
            //Check if internal GL format is supported by WebGL
            [
                // ETC1
                0x8D64,
                // PVRTC
                0x8C00, 0x8C01, 0x8C02, 0x8C03,
                // DXTC | S3TC
                0x83F0, 0x83F1, 0x83F2, 0x83F3
            ].indexOf(glInternalFormat) >= 0) {
            switch (glInternalFormat) {
                case 0x8D64:
                    compressionAlgorithm = 'ETC1';
                    break;
                case 0x8C00:
                case 0x8C01:
                case 0x8C02:
                case 0x8C03:
                    compressionAlgorithm = 'PVRTC';
                    break;
                case 0x83F0:
                case 0x83F1:
                case 0x83F2:
                case 0x83F3:
                    compressionAlgorithm = 'S3TC';
                    break;
            }

            ktxHeader = {
                complete: true,
                fileFormat: 'KTX',
                compressionAlgorithm: compressionAlgorithm,
                endianness: uintArray[3],
                glType: uintArray[4],
                glTypeSize: uintArray[5],
                glFormat: uintArray[6],
                glInternalFormat: uintArray[7],
                glBaseInternalFormat: uintArray[8],
                width: uintArray[9],
                height: uintArray[10],
                pixelDepth: uintArray[11],
                numberOfArrayElements: uintArray[12],
                numberOfFaces: uintArray[13],
                numberOfMipmapLevels: uintArray[14],
                bytesOfKeyValueData: uintArray[15],
                keyAndValueByteSize: uintArray[16],
                imageSize: imageSize,
                textureData: byteArray.subarray((imageSizeOffset + 1) * 4, imageSize + 100)
            };
        }

        return ktxHeader;

    },

    /**
    * Extract PKM header from loaded binary
    *
    * @method Phaser.LoaderParser.pkm
    * @param {ArrayBuffer} arrayBuffer
    * @return {object} The parsed PKM file including texture data.
    */
    pkm: function (arrayBuffer) {

        // PKM header structure
        // ---------------------------------------
        // address: 0, size 4 bytes: for 'PKM '
        // address: 4, size 2 bytes: for version
        // address: 6, size 2 bytes: for type
        // address: 8, size 2 bytes: for extended width
        // address: 10, size 2 bytes: for extended height
        // address: 12, size 2 bytes: for original width
        // address: 14, size 2 bytes: for original height
        // address: 16, texture data
        // ---------------------------------------
        var byteArray = new Uint8Array(arrayBuffer),
            pkmHeader = null;

        if (byteArray[0] === 0x50 &&
            byteArray[1] === 0x4B &&
            byteArray[2] === 0x4D &&
            byteArray[3] === 0x20) {

            pkmHeader = {
                complete: true,
                fileFormat: 'PKM',
                compressionAlgorithm: 'ETC1',
                format: ((byteArray[6] << 8 | byteArray[7])) & 0xFFFF,
                width: ((byteArray[8] << 8 | byteArray[9])) & 0xFFFF,
                height: ((byteArray[10] << 8 | byteArray[11])) & 0xFFFF,
                originalWidth: ((byteArray[12] << 8 | byteArray[13])) & 0xFFFF,
                originalHeight: ((byteArray[14] << 8 | byteArray[15])) & 0xFFFF,
                textureData: byteArray.subarray(16, byteArray.length)
            };
        }

        return pkmHeader;

    }

};


var TileToWorldY = function (tileY, camera, layer)

keep keep keep replace keep keep keep keep keep

var Class = require('../../utils/Class');
var Extend = require('../../utils/object/Extend');
var LayerData = require('./mapdata/LayerData');
var StaticTilemapLayer = require('./staticlayer/StaticTilemapLayer.js');
var DynamicTilemapLayer = require('./dynamiclayer/DynamicTilemapLayer.js');
var Tileset = require('./Tileset');
var Formats = require('./Formats');
var TilemapComponents = require('./components');
var Tile = require('./Tile');
var Rotate = require('../../math/Rotate');
var DegToRad = require('../../math/DegToRad');

var Tilemap = new Class({

    initialize:

    /**
     * A Tilemap is a container for Tilemap data. This isn't a display object, rather, it holds data
     * about the map and allows you to add tilesets and tilemap layers to it. A map can have one or
     * more tilemap layers (StaticTilemapLayer or DynamicTilemapLayer), which are the display
     * objects that actually render tiles.
     *
     * The Tilemap data be parsed from a Tiled JSON file, a CSV file or a 2D array. Tiled is a free
     * software package specifically for creating tile maps, and is available from:
     * http://www.mapeditor.org
     *
     * A Tilemap has handy methods for getting & manipulating the tiles within a layer. You can only
     * use the methods that change tiles (e.g. removeTileAt) on a DynamicTilemapLayer.
     *
     * Note that all Tilemaps use a base tile size to calculate dimensions from, but that a
     * StaticTilemapLayer or DynamicTilemapLayer may have its own unique tile size that overrides
     * it.
     *
     * @class Tilemap
     * @constructor
     *
     * @param {Scene} scene - [description]
     * @param {MapData} mapData - A MapData instance containing Tilemap data.
     */
    function Tilemap (scene, mapData)
    {
        /**
         * @property {Scene} Scene
         */
        this.scene = scene;

        /**
         * The base width of a tile in pixels. Note that individual layers may have a different tile
         * width.
         * @property {integer} tileWidth
         */
        this.tileWidth = mapData.tileWidth;

        /**
         * The base height of a tile in pixels. Note that individual layers may have a different
         * tile height.
         * @property {integer} tileHeight
         */
        this.tileHeight = mapData.tileHeight;

        /**
         * The width of the map (in tiles).
         * @property {number} width
         */
        this.width = mapData.width;

        /**
         * The height of the map (in tiles).
         * @property {number} width
         */
        this.height = mapData.height;

        /**
         * The orientation of the map data (as specified in Tiled), usually 'orthogonal'.
         * @property {string} orientation
         */
        this.orientation = mapData.orientation;

        /**
         * @property {number} format - The format of the map data.
         */
        this.format = mapData.format;

        /**
         * The version of the map data (as specified in Tiled, usually 1).
         * @property {number} version
         */
        this.version = mapData.version;

        /**
         * Map specific properties as specified in Tiled.
         * @property {object} properties
         */
        this.properties = mapData.properties;

        /**
         * The width of the map in pixels based on width * tileWidth.
         * @property {number} widthInPixels
         */
        this.widthInPixels = mapData.widthInPixels;

        /**
         * The height of the map in pixels based on height * tileHeight.
         * @property {number} heightInPixels
         */
        this.heightInPixels = mapData.heightInPixels;

        /**
         * @property {ImageCollection[]} imagecollections
         */
        this.imageCollections = mapData.imageCollections;

        /**
         * An array of Tiled Image Layers.
         * @property {array} images
         */
        this.images = mapData.images;

        /**
         * An array of collision data. Specifically, any polyline objects defined in object layers.
         * @property {array} collision
         */
        this.collision = mapData.collision; // Note: this probably isn't useful anymore

        /**
         * @property {LayerData[]} layers - An array of Tilemap layer data.
         */
        this.layers = mapData.layers;

        /**
         * An array of Tilesets used in the map.
         * @property {Tileset[]} tilesets
         */
        this.tilesets = mapData.tilesets;

        /**
         * An array of Tiled Object Layers.
         * @property {array} objects
         */
        this.objects = mapData.objects;

        /**
         * The index of the currently selected LayerData object.
         * @property {integer} currentLayerIndex
         */
        this.currentLayerIndex = 0;
    },

    /**
     * Adds an image to the map to be used as a tileset. A single map may use multiple tilesets.
     * Note that the tileset name can be found in the JSON file exported from Tiled, or in the Tiled
     * editor.
     *
     * @param {string} tilesetName - The name of the tileset as specified in the map data.
     * @param {string} [key] - The key of the Phaser.Cache image used for this tileset. If
     * `undefined` or `null` it will look for an image with a key matching the tileset parameter.
     * @param {integer} [tileWidth] - The width of the tile (in pixels) in the Tileset Image. If not
     * given it will default to the map's tileWidth value, or the tileWidth specified in the Tiled
     * JSON file.
     * @param {integer} [tileHeight] - The height of the tiles (in pixels) in the Tileset Image. If
     * not given it will default to the map's tileHeight value, or the tileHeight specified in the
     * Tiled JSON file.
     * @param {integer} [tileMargin] - The margin around the tiles in the sheet (in pixels). If not
     * specified, it will default to 0 or the value specified in the Tiled JSON file.
     * @param {integer} [tileSpacing] - The spacing between each the tile in the sheet (in pixels).
     * If not specified, it will default to 0 or the value specified in the Tiled JSON file.
     * @param {integer} [gid=0] - If adding multiple tilesets to a blank map, specify the starting
     * GID this set will use here.
     * @return {Tileset|null} Returns the Tileset object that was created or updated, or null if it
     * failed.
     */
    addTilesetImage: function (tilesetName, key, tileWidth, tileHeight, tileMargin, tileSpacing, gid)
    {
        if (tilesetName === undefined) { return null; }
        if (key === undefined || key === null) { key = tilesetName; }

        if (!this.scene.sys.textures.exists(key))
        {
            console.warn('Invalid image key given for tileset: "' + key + '"');
            return null;
        }

        var texture = this.scene.sys.textures.get(key);

        var index = this.getTilesetIndex(tilesetName);

        if (index === null && this.format === Formats.TILEMAP_TILED_JSON)
        {
            console.warn('No data found in the JSON tilemap from Tiled matching the tileset name: "' + tilesetName + '"');
            return null;
        }

        if (this.tilesets[index])
        {
            this.tilesets[index].setTileSize(tileWidth, tileHeight);
            this.tilesets[index].setSpacing(tileMargin, tileSpacing);
            this.tilesets[index].setImage(texture);
            return this.tilesets[index];
        }

        if (tileWidth === undefined) { tileWidth = this.tileWidth; }
        if (tileHeight === undefined) { tileHeight = this.tileHeight; }
        if (tileMargin === undefined) { tileMargin = 0; }
        if (tileSpacing === undefined) { tileSpacing = 0; }
        if (gid === undefined) { gid = 0; }

        var tileset = new Tileset(tilesetName, gid, tileWidth, tileHeight, tileMargin, tileSpacing);
        tileset.setImage(texture);
        this.tilesets.push(tileset);
        return tileset;
    },

    /**
     * Turns the StaticTilemapLayer associated with the given layer into a DynamicTilemapLayer. If
     * no layer specified, the map's current layer is used. This is useful if you want to manipulate
     * a map at the start of a scene, but then make it non-manipulable and optimize it for speed.
     *
     * @param {string|integer|DynamicTilemapLayer} [layer] - The name of the layer from Tiled, the
     * index of the layer in the map, or a StaticTilemapLayer.
     * @return {StaticTilemapLayer|null} Returns the new layer that was created, or null if it
     * failed.
     */
    convertLayerToStatic: function (layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }

        var dynamicLayer = layer.tilemapLayer;

        if (!dynamicLayer || !(dynamicLayer instanceof DynamicTilemapLayer))
        {
            return null;
        }

        var staticLayer = new StaticTilemapLayer(dynamicLayer.scene, dynamicLayer.map,
            dynamicLayer.layerIndex, dynamicLayer.tileset, dynamicLayer.x, dynamicLayer.y);
        this.scene.sys.displayList.add(staticLayer);

        dynamicLayer.destroy();

        return staticLayer;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used. This
     * cannot be applied to StaticTilemapLayers.
     *
     * @return {this|null} Returns this, or null if the layer given was invalid.
     */
    copy: function (srcTileX, srcTileY, width, height, destTileX, destTileY, recalculateFaces, layer)
    {
        layer = this.getLayer(layer);
        if (this._isStaticCall(layer, 'copy')) { return this; }
        if (layer !== null)
        {
            TilemapComponents.Copy(srcTileX, srcTileY, width, height, destTileX, destTileY,
                recalculateFaces, layer);
        }
        return this;
    },

    /**
     * Creates a new and empty DynamicTilemapLayer. The currently selected layer in the map is set
     * to this new layer.
     *
     * @param {string} name - The name of this layer. Must be unique within the map.
     * @param {Tileset} tileset - The tileset the new layer will use.
     * @param {integer} width - The width of the layer in tiles. If not specified, it will default
     * to the map's width.
     * @param {integer} height - The height of the layer in tiles. If not specified, it will default
     * to the map's height.
     * @param {integer} tileWidth - The width of the tiles the layer uses for calculations. If not
     * specified, it will default to the map's tileWidth.
     * @param {integer} tileHeight - The height of the tiles the layer uses for calculations. If not
     * specified, it will default to the map's tileHeight.
     * @return {DynamicTilemapLayer|null} Returns the new layer was created, or null if it failed.
     */
    createBlankDynamicLayer: function (name, tileset, x, y, width, height, tileWidth, tileHeight)
    {
        if (tileWidth === undefined) { tileWidth = tileset.tileWidth; }
        if (tileHeight === undefined) { tileHeight = tileset.tileHeight; }
        if (width === undefined) { width = this.width; }
        if (height === undefined) { height = this.height; }
        if (x === undefined) { x = 0; }
        if (y === undefined) { y = 0; }

        var index = this.getLayerIndex(name);

        if (index !== null)
        {
            console.warn('Cannot create blank layer: layer with matching name already exists ' + name);
            return null;
        }

        var layerData = new LayerData({
            name: name,
            tileWidth: tileWidth,
            tileHeight: tileHeight,
            width: width,
            height: height
        });

        var row;
        for (var tileY = 0; tileY < height; tileY++)
        {
            row = [];
            for (var tileX = 0; tileX < width; tileX++)
            {
                row.push(new Tile(layerData, -1, tileX, tileY, tileWidth, tileHeight,
                    this.tileWidth, this.tileHeight));
            }
            layerData.data.push(row);
        }

        this.layers.push(layerData);
        this.currentLayerIndex = this.layers.length - 1;

        var dynamicLayer = new DynamicTilemapLayer(this.scene, this, this.currentLayerIndex, tileset, x, y);
        this.scene.sys.displayList.add(dynamicLayer);

        return dynamicLayer;
    },

    /**
     * Creates a new DynamicTilemapLayer that renders the LayerData associated with the given
     * `layerID`. The currently selected layer in the map is set to this new layer.
     *
     * The `layerID` is important. If you've created your map in Tiled then you can get this by
     * looking in Tiled and looking at the layer name. Or you can open the JSON file it exports and
     * look at the layers[].name value. Either way it must match.
     *
     * Unlike a static layer, a dynamic layer can be modified. See DynamicTilemapLayer for more
     * information.
     *
     * @param {integer|string} layerID - The layer array index value, or if a string is given, the
     * layer name from Tiled.
     * @param {Tileset} tileset - The tileset the new layer will use.
     * @param {number} x - The x position to place the layer in the world. If not specified, it will
     * default to the layer offset from Tiled or 0.
     * @param {number} y - The y position to place the layer in the world. If not specified, it will
     * default to the layer offset from Tiled or 0.
     * @return {DynamicTilemapLayer|null} Returns the new layer was created, or null if it failed.
     */
    createDynamicLayer: function (layerID, tileset, x, y)
    {
        var index = this.getLayerIndex(layerID);

        if (index === null)
        {
            console.warn('Cannot create tilemap layer, invalid layer ID given: ' + layerID);
            return null;
        }

        var layerData = this.layers[index];

        // Check for an associated static or dynamic tilemap layer
        if (layerData.tilemapLayer)
        {
            console.warn('Cannot create dynamic tilemap layer since a static or dynamic tilemap layer exists for layer ID:' + layerID);
            return null;
        }

        this.currentLayerIndex = index;

        // Make sure that all the LayerData & the tiles have the correct tile size. They usually
        // are, but wouldn't match if you try to load a 2x or 4x res tileset when the map was made
        // with a 1x res tileset.
        if (layerData.tileWidth !== tileset.tileWidth || layerData.tileHeight !== tileset.tileHeight)
        {
            this.setLayerTileSize(tileset.tileWidth, tileset.tileHeight, index);
        }

        // Default the x/y position to match Tiled layer offset, if it exists.
        if (x === undefined && this.layers[index].x) { x = this.layers[index].x; }
        if (y === undefined && this.layers[index].y) { y = this.layers[index].y; }

        var layer = new DynamicTilemapLayer(this.scene, this, index, tileset, x, y);
        this.scene.sys.displayList.add(layer);

        return layer;
    },

    /**
     * Creates a Sprite for every object matching the given gid in the map data. All properties from
     * the map data objectgroup are copied into the `spriteConfig`, so you can use this as an easy
     * way to configure Sprite properties from within the map editor. For example giving an object a
     * property of alpha: 0.5 in the map editor will duplicate that when the Sprite is created.
     *
     * @param {string} name - The name of the object layer (from Tiled) to create Sprites from.
     * @param {integer|string} id - Either the id (object), gid (tile object) or name (object or
     * tile object) from Tiled. Ids are unique in Tiled, but a gid is shared by all tile objects
     * with the same graphic. The same name can be used on multiple objects.
     * @param {object} spriteConfig - The config object to pass into the Sprite creator (i.e.
     * scene.make.sprite).
     * @param {Scene} [scene=the scene the map is within] - The Scene to create the Sprites within.
     * @return {Sprite[]} An array of the Sprites that were created.
     */
    createFromObjects: function (name, id, spriteConfig, scene)
    {
        if (spriteConfig === undefined) { spriteConfig = {}; }
        if (scene === undefined) { scene = this.scene; }

        if (!this.objects[name])
        {
            console.warn('Cannot create from object. Invalid objectgroup name given: ' + name);
            return;
        }

        var sprites = [];

        for (var i = 0; i < this.objects[name].length; i++)
        {
            var found = false;
            var obj = this.objects[name][i];

            if (obj.gid !== undefined && typeof id === 'number' && obj.gid === id ||
                obj.id !== undefined && typeof id === 'number' && obj.id === id ||
                obj.name !== undefined && typeof id === 'string' && obj.name === id)
            {
                found = true;
            }

            if (found)
            {
                var config = Extend({}, spriteConfig, obj.properties);

                config.x = obj.x;
                config.y = obj.y;

                var sprite = this.scene.make.sprite(config);

                sprite.name = obj.name;

                if (obj.width) { sprite.displayWidth = obj.width; }
                if (obj.height) { sprite.displayHeight = obj.height; }

                // Origin is (0, 1) in Tiled, so find the offset that matches the Sprite's origin.
                var offset = {
                    x: sprite.originX * sprite.displayWidth,
                    y: (sprite.originY - 1) * sprite.displayHeight
                };

                // If the object is rotated, then the origin offset also needs to be rotated.
                if (obj.rotation)
                {
                    var angle = DegToRad(obj.rotation);
                    Rotate(offset, angle);
                    sprite.rotation = angle;
                }

                sprite.x += offset.x;
                sprite.y += offset.y;

                if (obj.flippedHorizontal !== undefined || obj.flippedVertical !== undefined)
                {
                    sprite.setFlip(obj.flippedHorizontal, obj.flippedVertical);
                }

                if (!obj.visible) { sprite.visible = false; }

                sprites.push(sprite);
            }
        }

        return sprites;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {Sprite[]|null} Returns an array of Tiles, or null if the layer given was invalid.
     */
    createFromTiles: function (indexes, replacements, spriteConfig, scene, camera, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.CreateFromTiles(indexes, replacements, spriteConfig, scene, camera, layer);
    },

    /**
     * Creates a new StaticTilemapLayer that renders the LayerData associated with the given
     * `layerID`. The currently selected layer in the map is set to this new layer.
     *
     * The `layerID` is important. If you've created your map in Tiled then you can get this by
     * looking in Tiled and looking at the layer name. Or you can open the JSON file it exports and
     * look at the layers[].name value. Either way it must match.
     *
     * It's important to remember that a static layer cannot be modified. See StaticTilemapLayer for
     * more information.
     *
     * @param {integer|string} layerID - The layer array index value, or if a string is given, the
     * layer name from Tiled.
     * @param {Tileset} tileset - The tileset the new layer will use.
     * @param {number} x - The x position to place the layer in the world. If not specified, it will
     * default to the layer offset from Tiled or 0.
     * @param {number} y - The y position to place the layer in the world. If not specified, it will
     * default to the layer offset from Tiled or 0.
     * @return {StaticTilemapLayer|null} Returns the new layer was created, or null if it failed.
     */
    createStaticLayer: function (layerID, tileset, x, y)
    {
        var index = this.getLayerIndex(layerID);

        if (index === null)
        {
            console.warn('Cannot create tilemap layer, invalid layer ID given: ' + layerID);
            return null;
        }

        var layerData = this.layers[index];

        // Check for an associated static or dynamic tilemap layer
        if (layerData.tilemapLayer)
        {
            console.warn('Cannot create static tilemap layer since a static or dynamic tilemap layer exists for layer ID:' + layerID);
            return null;
        }

        this.currentLayerIndex = index;

        // Make sure that all the LayerData & the tiles have the correct tile size. They usually
        // are, but wouldn't match if you try to load a 2x or 4x res tileset when the map was made
        // with a 1x res tileset.
        if (layerData.tileWidth !== tileset.tileWidth || layerData.tileHeight !== tileset.tileHeight)
        {
            this.setLayerTileSize(tileset.tileWidth, tileset.tileHeight, index);
        }

        // Default the x/y position to match Tiled layer offset, if it exists.
        if (x === undefined && this.layers[index].x) { x = this.layers[index].x; }
        if (y === undefined && this.layers[index].y) { y = this.layers[index].y; }

        var layer = new StaticTilemapLayer(this.scene, this, index, tileset, x, y);
        this.scene.sys.displayList.add(layer);

        return layer;
    },

    /**
     * Removes all layer data from this Tilemap and nulls the scene reference. This will destroy any
     * StaticTilemapLayers or DynamicTilemapLayers that have been linked to LayerData.
     */
    destroy: function ()
    {
        this.removeAllLayers();
        this.tilesets.length = 0;
        this.objects.length = 0;
        this.scene = undefined;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used. This
     * cannot be applied to StaticTilemapLayers.
     *
     * @return {this|null} Returns this, or null if the layer given was invalid.
     */
    fill: function (index, tileX, tileY, width, height, recalculateFaces, layer)
    {
        layer = this.getLayer(layer);
        if (this._isStaticCall(layer, 'fill')) { return this; }
        if (layer !== null)
        {
            TilemapComponents.Fill(index, tileX, tileY, width, height, recalculateFaces, layer);
        }
        return this;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {Tile[]|null} Returns an array of Tiles, or null if the layer given was invalid.
     */
    filterTiles: function (callback, context, tileX, tileY, width, height, filteringOptions, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.FilterTiles(callback, context, tileX, tileY, width, height, filteringOptions, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {Tile|null} Returns a Tiles, or null if the layer given was invalid.
     */
    findByIndex: function (findIndex, skip, reverse, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.FindByIndex(findIndex, skip, reverse, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {this|null} Returns this, or null if the layer given was invalid.
     */
    forEachTile: function (callback, context, tileX, tileY, width, height, filteringOptions, layer)
    {
        layer = this.getLayer(layer);
        if (layer !== null)
        {
            TilemapComponents.ForEachTile(callback, context, tileX, tileY, width, height, filteringOptions, layer);
        }
        return this;
    },

    /**
     * Gets the image layer index based on its name.
     *
     * @method Phaser.Tilemap#getImageIndex
     * @param {string} name - The name of the image to get.
     * @return {integer} The index of the image in this tilemap, or null if not found.
     */
    getImageIndex: function (name)
    {
        return this.getIndex(this.images, name);
    },

    /**
     * Internally used. Returns the index of the object in one of the Tilemap's arrays whose name
     * property matches the given `name`.
     *
     * @param {array} location - The Tilemap array to search.
     * @param {string} name - The name of the array element to get.
     * @return {number} The index of the element in the array, or null if not found.
     */
    getIndex: function (location, name)
    {
        for (var i = 0; i < location.length; i++)
        {
            if (location[i].name === name)
            {
                return i;
            }
        }
        return null;
    },

    /**
     * Gets the LayerData from this.layers that is associated with `layer`, or null if an invalid
     * `layer` is given.
     *
     * @param {string|integer|DynamicTilemapLayer|StaticTilemapLayer} [layer] - The name of the
     * layer from Tiled, the index of the layer in the map, a DynamicTilemapLayer or a
     * StaticTilemapLayer. If not given will default to the map's current layer index.
     * @return {LayerData} The corresponding LayerData within this.layers.
     */
    getLayer: function (layer)
    {
        var index = this.getLayerIndex(layer);
        return index !== null ? this.layers[index] : null;
    },

    /**
     * Gets the LayerData index of the given `layer` within this.layers, or null if an invalid
     * `layer` is given.
     *
     * @param {string|integer|DynamicTilemapLayer|StaticTilemapLayer} [layer] - The name of the
     * layer from Tiled, the index of the layer in the map, a DynamicTilemapLayer or a
     * StaticTilemapLayer. If not given will default to the map's current layer index.
     * @return {integer} The LayerData index within this.layers.
     */
    getLayerIndex: function (layer)
    {
        if (layer === undefined)
        {
            return this.currentLayerIndex;
        }
        else if (typeof layer === 'string')
        {
            return this.getLayerIndexByName(layer);
        }
        else if (typeof layer === 'number' && layer < this.layers.length)
        {
            return layer;
        }
        else if (layer instanceof StaticTilemapLayer || layer instanceof DynamicTilemapLayer)
        {
            return layer.layerIndex;
        }
        else
        {
            return null;
        }
    },

    /**
     * Gets the index of the LayerData within this.layers that has the given `name`, or null if an
     * invalid `name` is given.
     *
     * @param {string} name - The name of the layer to get.
     * @return {integer} The LayerData index within this.layers.
     */
    getLayerIndexByName: function (name)
    {
        return this.getIndex(this.layers, name);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {Tile|null} Returns a Tile, or null if the layer given was invalid.
     */
    getTileAt: function (tileX, tileY, nonNull, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.GetTileAt(tileX, tileY, nonNull, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {Tile|null} Returns a Tile, or null if the layer given was invalid.
     */
    getTileAtWorldXY: function (worldX, worldY, nonNull, camera, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.GetTileAtWorldXY(worldX, worldY, nonNull, camera, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {Tile[]|null} Returns an array of Tiles, or null if the layer given was invalid.
     */
    getTilesWithin: function (tileX, tileY, width, height, filteringOptions, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.GetTilesWithin(tileX, tileY, width, height, filteringOptions, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {Tile[]|null} Returns an array of Tiles, or null if the layer given was invalid.
     */
    getTilesWithinShape: function (shape, filteringOptions, camera, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.GetTilesWithinShape(shape, filteringOptions, camera, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {Tile[]|null} Returns an array of Tiles, or null if the layer given was invalid.
     */
    getTilesWithinWorldXY: function (worldX, worldY, width, height, filteringOptions, camera, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.GetTilesWithinWorldXY(worldX, worldY, width, height, filteringOptions, camera, layer);
    },

    /**
     * Gets the index of the Tileset within this.tilesets that has the given `name`, or null if an
     * invalid `name` is given.
     *
     * @param {string} name - The name of the Tileset to get.
     * @return {integer} The Tileset index within this.tilesets.
     */
    getTilesetIndex: function (name)
    {
        return this.getIndex(this.tilesets, name);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {boolean|null} Returns a boolean, or null if the layer given was invalid.
     */
    hasTileAt: function (tileX, tileY, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.HasTileAt(tileX, tileY, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {boolean|null} Returns a boolean, or null if the layer given was invalid.
     */
    hasTileAtWorldXY: function (worldX, worldY, camera, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.HasTileAtWorldXY(worldX, worldY, camera, layer);
    },

    /**
     * The LayerData object that is currently selected in the map. You can set this property using
     * any type supported by setLayer.
     * @property {LayerData} layer
     */
    layer: {
        get: function ()
        {
            return this.layers[this.currentLayerIndex];
        },

        set: function (layer)
        {
            this.setLayer(layer);
        }
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used. This
     * cannot be applied to StaticTilemapLayers.
     *
     * @return {Tile|null} Returns a Tile, or null if the layer given was invalid.
     */
    putTileAt: function (tile, tileX, tileY, recalculateFaces, layer)
    {
        layer = this.getLayer(layer);
        if (this._isStaticCall(layer, 'putTileAt')) { return null; }
        if (layer === null) { return null; }
        return TilemapComponents.PutTileAt(tile, tileX, tileY, recalculateFaces, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used. This
     * cannot be applied to StaticTilemapLayers.
     *
     * @return {Tile|null} Returns a Tile, or null if the layer given was invalid.
     */
    putTileAtWorldXY: function (tile, worldX, worldY, recalculateFaces, camera, layer)
    {
        layer = this.getLayer(layer);
        if (this._isStaticCall(layer, 'putTileAtWorldXY')) { return null; }
        if (layer === null) { return null; }
        return TilemapComponents.PutTileAtWorldXY(tile, worldX, worldY, recalculateFaces, camera, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used. This
     * cannot be applied to StaticTilemapLayers.
     *
     * @return {this|null} Returns this, or null if the layer given was invalid.
     */
    putTilesAt: function (tilesArray, tileX, tileY, recalculateFaces, layer)
    {
        layer = this.getLayer(layer);
        if (this._isStaticCall(layer, 'putTilesAt')) { return this; }
        if (layer !== null)
        {
            TilemapComponents.PutTilesAt(tilesArray, tileX, tileY, recalculateFaces, layer);
        }
        return this;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used. This
     * cannot be applied to StaticTilemapLayers.
     *
     * @return {this|null} Returns this, or null if the layer given was invalid.
     */
    randomize: function (tileX, tileY, width, height, indexes, layer)
    {
        layer = this.getLayer(layer);
        if (this._isStaticCall(layer, 'randomize')) { return this; }
        if (layer !== null)
        {
            TilemapComponents.Randomize(tileX, tileY, width, height, indexes, layer);
        }
        return this;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {this|null} Returns this, or null if the layer given was invalid.
     */
    calculateFacesWithin: function (tileX, tileY, width, height, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return this; }
        TilemapComponents.CalculateFacesWithin(tileX, tileY, width, height, layer);
        return this;
    },

    /**
     * Removes all layers from this Tilemap and destroys any associated StaticTilemapLayers or
     * DynamicTilemapLayers.
     *
     * @return {this}
     */
    removeAllLayers: function ()
    {
        // Destroy any StaticTilemapLayers or DynamicTilemapLayers that are stored in LayerData
        for (var i = 0; i < this.layers.length; i++)
        {
            if (this.layers[i].tilemapLayer)
            {
                this.layers[i].tilemapLayer.destroy();
            }
        }
        this.layers.length = 0;
        this.currentLayerIndex = 0;
        return this;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used. This
     * cannot be applied to StaticTilemapLayers.
     *
     * @return {Tile|null} Returns a Tile, or null if the layer given was invalid.
     */
    removeTileAt: function (tileX, tileY, replaceWithNull, recalculateFaces, layer)
    {
        layer = this.getLayer(layer);
        if (this._isStaticCall(layer, 'removeTileAt')) { return null; }
        if (layer === null) { return null; }
        return TilemapComponents.RemoveTileAt(tileX, tileY, replaceWithNull, recalculateFaces, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used. This
     * cannot be applied to StaticTilemapLayers.
     *
     * @return {Tile|null} Returns a Tile, or null if the layer given was invalid.
     */
    removeTileAtWorldXY: function (worldX, worldY, replaceWithNull, recalculateFaces, camera, layer)
    {
        layer = this.getLayer(layer);
        if (this._isStaticCall(layer, 'removeTileAtWorldXY')) { return null; }
        if (layer === null) { return null; }
        return TilemapComponents.RemoveTileAtWorldXY(worldX, worldY, replaceWithNull, recalculateFaces, camera, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {this|null} Returns this, or null if the layer given was invalid.
     */
    renderDebug: function (graphics, styleConfig, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return this; }
        TilemapComponents.RenderDebug(graphics, styleConfig, layer);
        return this;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used. This
     * cannot be applied to StaticTilemapLayers.
     *
     * @return {this|null} Returns this, or null if the layer given was invalid.
     */
    replaceByIndex: function (findIndex, newIndex, tileX, tileY, width, height, layer)
    {
        layer = this.getLayer(layer);
        if (this._isStaticCall(layer, 'replaceByIndex')) { return this; }
        if (layer !== null)
        {
            TilemapComponents.ReplaceByIndex(findIndex, newIndex, tileX, tileY, width, height, layer);
        }
        return this;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {this|null} Returns this, or null if the layer given was invalid.
     */
    setCollision: function (indexes, collides, recalculateFaces, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return this; }
        TilemapComponents.SetCollision(indexes, collides, recalculateFaces, layer);
        return this;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {this|null} Returns this, or null if the layer given was invalid.
     */
    setCollisionBetween: function (start, stop, collides, recalculateFaces, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return this; }
        TilemapComponents.SetCollisionBetween(start, stop, collides, recalculateFaces, layer);
        return this;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {this|null} Returns this, or null if the layer given was invalid.
     */
    setCollisionByExclusion: function (indexes, collides, recalculateFaces, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return this; }
        TilemapComponents.SetCollisionByExclusion(indexes, collides, recalculateFaces, layer);
        return this;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {this|null} Returns this, or null if the layer given was invalid.
     */
    setTileIndexCallback: function (indexes, callback, callbackContext, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return this; }
        TilemapComponents.SetTileIndexCallback(indexes, callback, callbackContext, layer);
        return this;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {this|null} Returns this, or null if the layer given was invalid.
     */
    setTileLocationCallback: function (tileX, tileY, width, height, callback, callbackContext, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return this; }
        TilemapComponents.SetTileLocationCallback(tileX, tileY, width, height, callback, callbackContext, layer);
        return this;
    },

    /**
     * Sets the current layer to the LayerData associated with `layer`.
     *
     * @param {string|integer|DynamicTilemapLayer|StaticTilemapLayer} [layer] - The name of the
     * layer from Tiled, the index of the layer in the map, a DynamicTilemapLayer or a
     * StaticTilemapLayer. If not given will default to the map's current layer index.
     * @return {this}
     */
    setLayer: function (layer)
    {
        var index = this.getLayerIndex(layer);
        if (index !== null)
        {
            this.currentLayerIndex = index;
        }
        return this;
    },

    /**
     * Sets the base tile size for the map. Note: this does not necessarily match the tileWidth and
     * tileHeight for all layers. This also updates the base size on all tiles across all layers.
     *
     * @param {integer} tileWidth - The width of the tiles the map uses for calculations.
     * @param {integer} tileHeight - The height of the tiles the map uses for calculations.
     * @return {this}
     */
    setBaseTileSize: function (tileWidth, tileHeight)
    {
        this.tileWidth = tileWidth;
        this.tileHeight = tileHeight;
        this.widthInPixels = this.width * tileWidth;
        this.heightInPixels = this.height * tileHeight;

        // Update the base tile size on all tiles
        for (var i = 0; i < this.layers.length; i++)
        {
            var mapData = this.layers[i].data;
            var mapWidth = this.layers[i].width;
            var mapHeight = this.layers[i].height;

            for (var row = 0; row < mapHeight; ++row)
            {
                for (var col = 0; col < mapWidth; ++col)
                {
                    var tile = mapData[row][col];
                    if (tile !== null)
                    {
                        tile.setSize(undefined, undefined, tileWidth, tileHeight);
                    }
                }
            }
        }

        return this;
    },

    /**
     * Sets the tile size for a specific `layer`. Note: this does not necessarily match the map's
     * tileWidth and tileHeight for all layers. This will set the tile size for the layer and any
     * tiles the layer has.
     *
     * @param {integer} tileWidth - The width of the tiles (in pixels) in the layer.
     * @param {integer} tileHeight - The height of the tiles (in pixels) in the layer.
     * @param {string|integer|DynamicTilemapLayer|StaticTilemapLayer} [layer] - The name of the
     * layer from Tiled, the index of the layer in the map, a DynamicTilemapLayer or a
     * StaticTilemapLayer. If not given will default to the map's current layer index.
     * @return {this}
     */
    setLayerTileSize: function (tileWidth, tileHeight, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return this; }

        layer.tileWidth = tileWidth;
        layer.tileHeight = tileHeight;

        var mapData = layer.data;
        var mapWidth = layer.width;
        var mapHeight = layer.height;

        for (var row = 0; row < mapHeight; ++row)
        {
            for (var col = 0; col < mapWidth; ++col)
            {
                var tile = mapData[row][col];
                if (tile !== null) { tile.setSize(tileWidth, tileHeight); }
            }
        }

        return this;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used. This
     * cannot be applied to StaticTilemapLayers.
     *
     * @return {this|null} Returns this, or null if the layer given was invalid.
     */
    shuffle: function (tileX, tileY, width, height, layer)
    {
        layer = this.getLayer(layer);
        if (this._isStaticCall(layer, 'shuffle')) { return this; }
        if (layer !== null)
        {
            TilemapComponents.Shuffle(tileX, tileY, width, height, layer);
        }
        return this;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used. This
     * cannot be applied to StaticTilemapLayers.
     *
     * @return {this|null} Returns this, or null if the layer given was invalid.
     */
    swapByIndex: function (indexA, indexB, tileX, tileY, width, height, layer)
    {
        layer = this.getLayer(layer);
        if (this._isStaticCall(layer, 'swapByIndex')) { return this; }
        if (layer !== null)
        {
            TilemapComponents.SwapByIndex(indexA, indexB, tileX, tileY, width, height, layer);
        }
        return this;
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {number|null} Returns a number, or null if the layer given was invalid.
     */
    tileToWorldX: function (tileX, camera, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.TileToWorldX(tileX, camera, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {number|null} Returns a number, or null if the layer given was invalid.
     */
    tileToWorldY: function (tileX, camera, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.TileToWorldY(tileX, camera, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {Vector2|null} Returns a point, or null if the layer given was invalid.
     */
    tileToWorldXY: function (tileX, tileY, point, camera, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.TileToWorldXY(tileX, tileY, point, camera, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {number|null} Returns a number, or null if the layer given was invalid.
     */
    worldToTileX: function (worldX, snapToFloor, camera, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.WorldToTileX(worldX, snapToFloor, camera, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {number|null} Returns a number, or null if the layer given was invalid.
     */
    worldToTileY: function (worldY, snapToFloor, camera, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.WorldToTileY(worldY, snapToFloor, camera, layer);
    },

    /**
     * See component documentation. If no layer specified, the map's current layer is used.
     *
     * @return {Vector2|null} Returns a point, or null if the layer given was invalid.
     */
    worldToTileXY: function (worldX, worldY, snapToFloor, point, camera, layer)
    {
        layer = this.getLayer(layer);
        if (layer === null) { return null; }
        return TilemapComponents.WorldToTileXY(worldX, worldY, snapToFloor, point, camera, layer);
    },

    /**
     * Used internally to check if a layer is static and prints out a warning.
     *
     * @private
     * @return {boolean}
     */
    _isStaticCall: function (layer, functionName)
    {
        if (layer.tilemapLayer instanceof StaticTilemapLayer)
        {
            console.warn(functionName + ': You cannot change the tiles in a static tilemap layer');
            return true;
        }
        else
        {
            return false;
        }
    }

});

module.exports = Tilemap;


var Class = require('../../../utils/Class');
var Event = require('../../../events/Event');

var DragEnterEvent = new Class({

    Extends: Event,

    initialize:

    function DragEnterEvent (pointer, gameObject, dropZone)
    {
        Event.call(this, 'DRAG_ENTER_EVENT');

        //  The Pointer that triggered the event
        this.pointer = pointer;

        //  The native DOM event (MouseEvent, TouchEvent, etc)
        this.event = pointer.event;

        //  The Game Object the event occurred on
        this.gameObject = gameObject;

        //  The camera on which the input event occurred
        this.camera = pointer.camera;

        //  The drop zone the game object was dropped on
        this.dropZone = dropZone;
    }

});

module.exports = DragEnterEvent;


var Class = require('../../utils/Class');
var CONST = require('../const');
var File = require('../File');
var GetFastValue = require('../../utils/object/GetFastValue');

//  Phaser.Loader.FileTypes.BinaryFile

var BinaryFile = new Class({

    Extends: File,

    initialize:

    function BinaryFile (key, url, path, xhrSettings)
    {
        var fileKey = (typeof key === 'string') ? key : GetFastValue(key, 'key', '');

        var fileConfig = {
            type: 'binary',
            extension: GetFastValue(key, 'extension', 'bin'),
            responseType: 'arraybuffer',
            key: fileKey,
            url: GetFastValue(key, 'file', url),
            path: path,
            xhrSettings: GetFastValue(key, 'xhr', xhrSettings)
        };

        File.call(this, fileConfig);
    },

    onProcess: function (callback)
    {
        this.state = CONST.FILE_PROCESSING;

        this.data = this.xhrLoader.response;

        this.onComplete();

        callback(this);
    }

});

BinaryFile.create = function (loader, key, url, xhrSettings)
{
    if (Array.isArray(key))
    {
        for (var i = 0; i < key.length; i++)
        {
            //  If it's an array it has to be an array of Objects, so we get everything out of the 'key' object
            loader.addFile(new BinaryFile(key[i], url, loader.path, xhrSettings));
        }
    }
    else
    {
        loader.addFile(new BinaryFile(key, url, loader.path, xhrSettings));
    }

    //  For method chaining
    return loader;
};

module.exports = BinaryFile;


var Update = function (timestamp, delta)
{
    if (!this.isPlaying || this.currentAnim.paused)
    {
        return;
    }

    this.accumulator += delta * this._timeScale;

    if (this.accumulator >= this.nextTick)
    {
        this.currentAnim.setFrame(this);
    }
};

module.exports = Update;


var WorldToTileX = function (worldX, snapToFloor, camera, layer)

if (snapToFloor === undefined) { snapToFloor = true; }

if (camera === undefined) { camera = tilemapLayer.scene.cameras.main; }

keep keep keep keep replace keep keep keep keep keep


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

var MATH_CONST = require('../math/const');
var WrapAngle = require('../math/angle/Wrap');

/**
* 2D Transformation Component.
*
* @class
*/
var Transform = function (gameObject, x, y, scaleX, scaleY)
{
    if (x === undefined) { x = 0; }
    if (y === undefined) { y = 0; }
    if (scaleX === undefined) { scaleX = 1; }
    if (scaleY === undefined) { scaleY = 1; }

    this.gameObject = gameObject;

    this.state = (gameObject.state) ? gameObject.state : gameObject.parent.state;

    var w = 1;
    var h = 1;

    if (gameObject.frame)
    {
        w = gameObject.frame.cutWidth;
        h = gameObject.frame.cutHeight;
    }

    //  a = scale X
    //  b = shear Y
    //  c = shear X
    //  d = scale Y
    //  tx / ty = translation

    //  World Transform
    this.world = { a: scaleX, b: 0, c: 0, d: scaleY, tx: x, ty: y };

    //  Previous Transform (used for interpolation)
    this.old = { a: scaleX, b: 0, c: 0, d: scaleY, tx: x, ty: y };

    //  Cached Transform Calculations
    this.cache = { a: 1, b: 0, c: 0, d: 1, sr: 0, cr: 0 };

    //  GL Vertex Data
    this.glVertextData = { x0: 0, y0: 0, x1: 0, y1: 0, x2: 0, y2: 0, x3: 0, y3: 0 };

    //  Canvas SetTransform data
    this.canvasData = { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0, dx: 0, dy: 0 };

    this.bounds = { x: x, y: y, width: w, height: h };

    this.immediate = false;

    this.interpolate = false;

    this.hasLocalRotation = false;

    //  Z-depth, for display list sorting
    this.z = 0;

    //  Private value holders, accessed via the getters and setters
    this._posX = x;
    this._posY = y;
    this._scaleX = scaleX;
    this._scaleY = scaleY;
    this._rotation = 0;
    this._pivotX = 0;
    this._pivotY = 0;
    this._anchorX = 0;
    this._anchorY = 0;

    this._worldRotation = 0;
    this._worldScaleX = scaleX;
    this._worldScaleY = scaleY;

    this._dirty = true;
    this._dirtyVertex = true;

    //  This Transforms entry in the State RTree.
    this._bbox = { minX: x, minY: y, maxX: x + w, maxY: y + h, gameObject: gameObject };

    //  The parent Transform (NOT the parent GameObject, although very often they are related)
    this.parent = null;

    //  Any child Transforms of this one - note that they don't have to belong to Game Objects
    //  that are children of the owner of this Transform
    this.children = [];

    this.state.sys.updates.add(this);
};

Transform.prototype.constructor = Transform;

Transform.prototype = {

    add: function (child)
    {
        return this.addAt(child, this.children.length);
    },

    addAt: function (child, index)
    {
        //  Invalid child?
        if (child === this || child.parent === this || index < 0 || index > this.children.length)
        {
            console.log('Invalid child');
            return child;
        }

        //  Child already parented? Remove it
        if (child.parent)
        {
            child.parent.remove(child);
        }

        child.parent = this;

        this.children.splice(index, 0, child);

        this.dirty = true;

        this.updateAncestors();

        return child;
    },

    remove: function (child)
    {
        //  Invalid child?
        if (child === this || child.parent !== this)
        {
            return child;
        }

        var index = this.children.indexOf(child);

        if (index !== -1)
        {
            return this.removeAt(index);
        }
    },

    removeAt: function (index)
    {
        //  Valid index?
        if (index >= 0 && index < this.children.length)
        {
            var child = this.children.splice(index, 1);

            if (child[0])
            {
                child[0].parent = null;

                return child[0];
            }
        }
    },

    enableInterpolation: function ()
    {
        this.interpolate = true;

        this.syncInterpolation();
    },

    syncInterpolation: function ()
    {
        this._dirty = true;

        this.update();

        var old = this.old;
        var world = this.world;

        old.a = world.a;
        old.b = world.b;
        old.c = world.c;
        old.d = world.d;
        old.tx = world.tx;
        old.ty = world.ty;
    },

    disableInterpolation: function ()
    {
        this.interpolate = false;
    },

    setPosition: function (x, y)
    {
        if (y === undefined) { y = x; }

        this._posX = x;
        this._posY = y;

        return this.update();
    },

    setScale: function (x, y)
    {
        if (y === undefined) { y = x; }

        this._scaleX = x;
        this._scaleY = y;
        this.updateCache();

        return this.update();
    },

    setPivot: function (x, y)
    {
        if (y === undefined) { y = x; }

        this._pivotX = x;
        this._pivotY = y;

        return this.update();
    },

    setAnchor: function (x, y)
    {
        if (y === undefined) { y = x; }

        this._anchorX = x;
        this._anchorY = y;

        this.dirty = true;
    },

    setRotation: function (rotation)
    {
        this.rotation = rotation;

        return this.update();
    },

    //  Updates the Transform.world object, ready for rendering
    //  Assuming this Transform is a root node (i.e. no transform parent)
    updateFromRoot: function ()
    {
        var old = this.old;
        var world = this.world;

        old.a = world.a;
        old.b = world.b;
        old.c = world.c;
        old.d = world.d;
        old.tx = world.tx;
        old.ty = world.ty;

        if (this.hasLocalRotation)
        {
            // console.log(this.name, 'Transform.updateFromRoot');

            world.a = this.cache.a;
            world.b = this.cache.b;
            world.c = this.cache.c;
            world.d = this.cache.d;
            world.tx = this._posX - ((this._pivotX * this.cache.a) + (this._pivotY * this.cache.c));
            world.ty = this._posY - ((this._pivotX * this.cache.b) + (this._pivotY * this.cache.d));

            this._worldRotation = Math.atan2(-this.cache.c, this.cache.d);
        }
        else
        {
            // console.log(this.name, 'Transform.updateFromRoot FAST');

            world.a = this._scaleX;
            world.b = 0;
            world.c = 0;
            world.d = this._scaleY;
            world.tx = this._posX - (this._pivotX * this._scaleX);
            world.ty = this._posY - (this._pivotY * this._scaleY);

            this._worldRotation = 0;
        }

        this._worldScaleX = this._scaleX;
        this._worldScaleY = this._scaleY;

        return this;
    },

    updateFromParent: function ()
    {
        var old = this.old;
        var world = this.world;

        old.a = world.a;
        old.b = world.b;
        old.c = world.c;
        old.d = world.d;
        old.tx = world.tx;
        old.ty = world.ty;

        var parent = this.parent.world;
        var tx = 0;
        var ty = 0;
        var a;
        var b;
        var c;
        var d;

        if (this.hasLocalRotation)
        {
            // console.log(this.name, 'Transform.updateFromParent', this.parent.name);

            a = this.cache.a;
            b = this.cache.b;
            c = this.cache.c;
            d = this.cache.d;

            tx = this._posX - ((this._pivotX * a) + (this._pivotY * c));
            ty = this._posY - ((this._pivotX * b) + (this._pivotY * d));

            world.a = (a * parent.a) + (b * parent.c);
            world.b = (a * parent.b) + (b * parent.d);
            world.c = (c * parent.a) + (d * parent.c);
            world.d = (c * parent.b) + (d * parent.d);
        }
        else
        {
            // console.log(this.name, 'Transform.updateFromParent FAST', this.parent.name);

            tx = this._posX - (this._pivotX * this._scaleX);
            ty = this._posY - (this._pivotY * this._scaleY);

            world.a = this._scaleX * parent.a;
            world.b = this._scaleX * parent.b;
            world.c = this._scaleY * parent.c;
            world.d = this._scaleY * parent.d;
        }

        world.tx = (tx * parent.a) + (ty * parent.c) + parent.tx;
        world.ty = (tx * parent.b) + (ty * parent.d) + parent.ty;

        a = world.a;
        b = world.b;
        c = world.c;
        d = world.d;

        var determ = (a * d) - (b * c);

        if (a || b)
        {
            var r = Math.sqrt((a * a) + (b * b));

            this._worldRotation = (b > 0) ? Math.acos(a / r) : -Math.acos(a / r);
            this._worldScaleX = r;
            this._worldScaleY = determ / r;
        }
        else if (c || d)
        {
            var s = Math.sqrt((c * c) + (d * d));

            this._worldRotation = MATH_CONST.TAU - ((d > 0) ? Math.acos(-c / s) : -Math.acos(c / s));
            this._worldScaleX = determ / s;
            this._worldScaleY = s;
        }
        else
        {
            this._worldScaleX = 0;
            this._worldScaleY = 0;
        }

        return this;
    },

    updateAncestors: function ()
    {
        // console.log(this.name, 'Transform.updateAncestors');

        //  No parent? Then just update the children and leave, our job is done
        if (!this.parent)
        {
            // console.log(this.name, 'updateAncestors has no parent Transform');

            this.updateFromRoot();

            this.updateChildren();

            this.dirty = false;

            return this;
        }

        // console.log(this.name, 'start updateAncestors while');

        //  Gets all parent nodes, starting from this Transform.
        //  Then updates from the top, down, but only on the ancestors,
        //  not any other children - will give us accurate worldX etc properties

        var node = this.parent;
        var nodes = [];

        do
        {
            nodes.push(node);
            node = node.parent;
        }
        while (node);

        //  We've got all the ancestors in the 'nodes' array, let's loop it

        while (nodes.length)
        {
            node = nodes.pop();

            if (node.parent)
            {
                node.updateFromParent();
            }
            else
            {
                node.updateFromRoot();
            }
        }

        //  By this point all of this Transforms ancestors have been
        //  updated, in the correct order, so we can now do this one
        //  and any of its children too

        this.update();
    },

    updateChildren: function ()
    {
        // console.log(this.name, 'Transform.updateChildren');

        for (var i = 0; i < this.children.length; i++)
        {
            this.children[i].update();
        }
    },

    updateFromDirtyParent: function ()
    {
        // console.log(this.name, 'is updateFromDirtyParent', this.parent.name);

        this.updateFromParent();

        if (this.children.length)
        {
            for (var i = 0; i < this.children.length; i++)
            {
                this.children[i].updateFromDirtyParent();
            }
        }

        this._dirty = false;
        this._dirtyVertex = true;
    },

    update: function ()
    {
        if (!this._dirty)
        {
            return;
        }

        //  If we got this far then this Transform is dirty
        //  so we need to update it from its parent
        //  and then force the update to all children

        if (this.parent)
        {
            this.updateFromParent();
        }
        else
        {
            this.updateFromRoot();
        }

        //  Calculate local bounds

        this.calculateLocalBounds();

        //  Update the RTree bbox entry

        if (this._bbox)
        {
            var x = this.world.tx;
            var y = this.world.ty;

            this.state.sys.tree.remove(this._bbox);

            this._bbox.minX = x;
            this._bbox.minY = y;
            this._bbox.maxX = x + this.bounds.width;
            this._bbox.maxY = y + this.bounds.height;

            this.state.sys.tree.insert(this._bbox);
        }

        //  Update the children

        var len = this.children.length;

        if (len)
        {
            for (var i = 0; i < len; i++)
            {
                this.children[i].updateFromDirtyParent();
            }
        }

        this._dirty = false;
        this._dirtyVertex = true;
    },

    deleteTreeNode: function ()
    {
        this.state.sys.tree.remove(this._bbox);

        this._bbox = undefined;
    },

    removeTreeNode: function ()
    {
        this.state.sys.tree.remove(this._bbox);
    },

    updateCache: function ()
    {
        this.cache.a = this.cache.cr * this._scaleX;
        this.cache.b = this.cache.sr * this._scaleX;
        this.cache.c = -this.cache.sr * this._scaleY;
        this.cache.d = this.cache.cr * this._scaleY;
    },

    updateVertexData: function (interpolationPercentage, renderer)
    {
        if (!this.gameObject.frame || (!this._dirtyVertex && !this.interpolate))
        {
            return;
        }

        var frame = this.gameObject.frame;

        var w0;
        var h0;
        var w1;
        var h1;

        if (frame.data.trim)
        {
            //  If the sprite is trimmed, add the extra space before transforming
            w1 = frame.x - (this._anchorX * frame.width);
            w0 = w1 + frame.cutWidth;

            h1 = frame.y - (this._anchorY * frame.height);
            h0 = h1 + frame.cutHeight;
        }
        else
        {
            w0 = frame.cutWidth * (1 - this._anchorX);
            w1 = frame.cutWidth * -this._anchorX;

            h0 = frame.cutHeight * (1 - this._anchorY);
            h1 = frame.cutHeight * -this._anchorY;
        }

        var resolution = frame.source.resolution;

        var wt = this.world;

        var a = wt.a / resolution;
        var b = wt.b / resolution;
        var c = wt.c / resolution;
        var d = wt.d / resolution;
        var tx = wt.tx;
        var ty = wt.ty;

        if (this.interpolate)
        {
            var old = this.old;

            // Interpolate with the last position to reduce stuttering.
            a = old.a + ((a - old.a) * interpolationPercentage);
            b = old.b + ((b - old.b) * interpolationPercentage);
            c = old.c + ((c - old.c) * interpolationPercentage);
            d = old.d + ((d - old.d) * interpolationPercentage);
            tx = old.tx + ((tx - old.tx) * interpolationPercentage);
            ty = old.ty + ((ty - old.ty) * interpolationPercentage);
        }

        if (frame.rotated)
        {
            // var cw = frame.cutWidth;
            var ch = frame.height;
            var a0 = a;
            var b0 = b;
            var c0 = c;
            var d0 = d;
            var _w1 = w1;
            var _w0 = w0;

            //  Offset before rotating
            tx = (wt.c * ch) + tx;
            ty = (wt.d * ch) + ty;

            //  Rotate matrix by 90 degrees with precalc values for sine and cosine of rad(90)
            a = (a0 * 6.123233995736766e-17) + -c0;
            b = (b0 * 6.123233995736766e-17) + -d0;
            c = a0 + (c0 * 6.123233995736766e-17);
            d = b0 + (d0 * 6.123233995736766e-17);

            // Update UV coordinates
            frame.updateUVsInverted();

            // Rotate dimensions
            w0 = h0;
            w1 = h1;
            h0 = _w0;
            h1 = _w1;
        }

        if (frame.autoRound === 1 || (frame.autoRound === -1 && renderer.roundPixels))
        {
            tx |= 0;
            ty |= 0;
        }

        var vert = this.glVertextData;

        //  Top Left Vert
        // vert.x0 = (a * w1) + (c * h1) + tx;
        // vert.y0 = (d * h1) + (b * w1) + ty;

        //  Top Right Vert
        // vert.x1 = (a * w0) + (c * h1) + tx;
        // vert.y1 = (d * h1) + (b * w0) + ty;

        //  Bottom Right Vert
        // vert.x2 = (a * w0) + (c * h0) + tx;
        // vert.y2 = (d * h0) + (b * w0) + ty;

        //  Bottom Left Vert
        // vert.x3 = (a * w1) + (c * h0) + tx;
        // vert.y3 = (d * h0) + (b * w1) + ty;

        return vert;
    },

    getVertexData: function (interpolationPercentage, renderer)
    {
        if (this.interpolate || this._dirtyVertex)
        {
            this.updateVertexData(interpolationPercentage, renderer);

            this._dirtyVertex = false;
        }

        return this.glVertextData;
    },

    cloneVertexData: function ()
    {
        var src = this.glVertextData;

        return {
            x0: src.x0,
            y0: src.y0,
            x1: src.x1,
            y1: src.y1,
            x2: src.x2,
            y2: src.y2,
            x3: src.x3,
            y3: src.y3
        };
    },

    getCanvasTransformData: function (interpolationPercentage, renderer)
    {
        var frame = this.gameObject.frame;

        var world = this.world;
        var data = this.canvasData;

        if (this.interpolate)
        {
            var old = this.old;

            // Interpolate with the last position to reduce stuttering.
            data.a = old.a + ((world.a - old.a) * interpolationPercentage);
            data.b = old.b + ((world.b - old.b) * interpolationPercentage);
            data.c = old.c + ((world.c - old.c) * interpolationPercentage);
            data.d = old.d + ((world.d - old.d) * interpolationPercentage);
            data.tx = old.tx + ((world.tx - old.tx) * interpolationPercentage);
            data.ty = old.ty + ((world.ty - old.ty) * interpolationPercentage);
            data.dx = old.dx + ((frame.x - (this.anchorX * frame.width)) * interpolationPercentage);
            data.dy = old.dy + ((frame.y - (this.anchorY * frame.height)) * interpolationPercentage);
        }
        else
        {
            //  Copy over the values to the canvasData object, in case the renderer needs to adjust them
            data.a = world.a;
            data.b = world.b;
            data.c = world.c;
            data.d = world.d;
            data.tx = world.tx;
            data.ty = world.ty;
            data.dx = frame.x - (this.anchorX * frame.width);
            data.dy = frame.y - (this.anchorY * frame.height);
        }

        if (frame.autoRound === 1 || (frame.autoRound === -1 && renderer.roundPixels))
        {
            data.tx |= 0;
            data.ty |= 0;
            data.dx |= 0;
            data.dy |= 0;
        }

        return data;
    },

    calculateLocalBounds: function ()
    {
        //  TODO
    }

};

Object.defineProperties(Transform.prototype, {

    //  Transform getters / setters

    x: {

        enumerable: true,

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

        set: function (value)
        {
            this._posX = value;
            this.dirty = true;
        }

    },

    y: {

        enumerable: true,

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

        set: function (value)
        {
            this._posY = value;
            this.dirty = true;
        }

    },

    scale: {

        enumerable: true,

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

        set: function (value)
        {
            this._scaleX = value;
            this._scaleY = value;

            this.dirty = true;
            this.updateCache();
        }

    },

    scaleX: {

        enumerable: true,

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

        set: function (value)
        {
            this._scaleX = value;

            this.dirty = true;
            this.updateCache();
        }

    },

    scaleY: {

        enumerable: true,

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

        set: function (value)
        {
            this._scaleY = value;

            this.dirty = true;
            this.updateCache();
        }

    },

    anchor: {

        enumerable: true,

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

        set: function (value)
        {
            this.setAnchor(value);
        }

    },

    anchorX: {

        enumerable: true,

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

        set: function (value)
        {
            this._anchorX = value;
            this.dirty = true;
        }

    },

    anchorY: {

        enumerable: true,

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

        set: function (value)
        {
            this._anchorY = value;
            this.dirty = true;
        }

    },

    pivotX: {

        enumerable: true,

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

        set: function (value)
        {
            this._pivotX = value;
            this.dirty = true;
            this.updateCache();
        }

    },

    pivotY: {

        enumerable: true,

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

        set: function (value)
        {
            this._pivotY = value;
            this.dirty = true;
            this.updateCache();
        }

    },

    angle: {

        enumerable: true,

        get: function ()
        {
            return WrapAngle(this.rotation * MATH_CONST.RAD_TO_DEG);
        },

        set: function (value)
        {
            this.rotation = WrapAngle(value) * MATH_CONST.DEG_TO_RAD;
        }

    },

    rotation: {

        enumerable: true,

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

        set: function (value)
        {
            if (this._rotation === value)
            {
                return;
            }

            this._rotation = value;
            this.dirty = true;

            if (this._rotation % MATH_CONST.PI2)
            {
                this.cache.sr = Math.sin(this._rotation);
                this.cache.cr = Math.cos(this._rotation);
                this.updateCache();
                this.hasLocalRotation = true;
            }
            else
            {
                this.hasLocalRotation = false;
            }
        }

    },

    //  Sets this *component* as being dirty
    dirty: {

        enumerable: true,

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

        set: function (value)
        {
            if (value)
            {
                if (!this._dirty)
                {
                    this._dirty = true;

                    if (this.immediate)
                    {
                        this.update();
                    }
                    else
                    {
                        this._dirtyVertex = true;
                        this.state.sys.updates.add(this);
                    }
                }
            }
            else
            {
                this._dirty = false;
            }
        }

    },

    //  GLOBAL read-only properties from here on
    //  Need *all* parents taken into account to get the correct values

    name: {

        enumerable: true,

        get: function ()
        {
            return (this.gameObject) ? this.gameObject.name : '';
        }

    },

    worldRotation: {

        enumerable: true,

        get: function ()
        {
            this.updateAncestors();

            return this._worldRotation;
        }

    },

    worldScaleX: {

        enumerable: true,

        get: function ()
        {
            this.updateAncestors();

            return this._worldScaleX;
        }

    },

    worldScaleY: {

        enumerable: true,

        get: function ()
        {
            this.updateAncestors();

            return this._worldScaleY;
        }

    },

    worldX: {

        enumerable: true,

        get: function ()
        {
            this.updateAncestors();

            return this.world.tx;
        }

    },

    worldY: {

        enumerable: true,

        get: function ()
        {
            this.updateAncestors();

            return this.world.ty;
        }

    }

});

module.exports = Transform;


/**
 * The MIT License (MIT)
 *
 * Copyright (c) Liam Brummitt and contributors.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */


// Get total curve arc length

/**
 * [description]
 *
 * @method Phaser.Curves.Curve#getLength
 * @since 3.0.0
 *
 * @return {number} [description]
 */
var GetLength = function ()
{
    var lengths = this.getLengths();

    return lengths[lengths.length - 1];
};

module.exports = GetLength;


keep keep keep replace keep keep keep keep

/**
 * Converts from world X coordinates (pixels) to tile X coordinates (tile units), factoring in the
 * layer's position, scale and scroll.
 *
 * @param {number} worldX - [description]
 * @param {boolean} [snapToFloor=true] - Whether or not to round the tile coordinate down to the
 * nearest integer.
 * @param {Camera} [camera=main camera] - [description]
 * @param {LayerData} layer - [description]
 * @returns {number} The X location in tile units.
 */
var WorldToTileX = function (worldX, snapToFloor, camera, layer)
{
    if (snapToFloor === undefined) { snapToFloor = true; }

    var tileWidth = layer.tileWidth;
    var tilemapLayer = layer.tilemapLayer;

    if (tilemapLayer)
    {
        if (camera === undefined) { camera = tilemapLayer.scene.cameras.main; }

        // Find the world position relative to the static or dynamic layer's top left origin,
        // factoring in the camera's horizontal scroll
        worldX = worldX - (tilemapLayer.x + camera.scrollX * (1 - tilemapLayer.scrollFactorX));

        tileWidth *= tilemapLayer.scaleX;
    }

    return snapToFloor
        ? Math.floor(worldX / tileWidth)
        : worldX / tileWidth;
};

module.exports = WorldToTileX;


var RESERVED = require('../tween/ReservedProps');

var GetProps = function (config)
{
    var key;
    var keys = [];

    //  First see if we have a props object

    if (config.hasOwnProperty('props'))
    {
        for (key in config.props)
        {
            //  Skip any property that starts with an underscore
            if (key.substr(0, 1) !== '_')
            {
                keys.push({ key: key, value: config.props[key] });
            }
        }
    }
    else
    {
        for (key in config)
        {
            //  Skip any property that is in the ReservedProps list or that starts with an underscore
            if (RESERVED.indexOf(key) === -1 && key.substr(0, 1) !== '_')
            {
                keys.push({ key: key, value: config[key] });
            }
        }
    }

    return keys;
};

module.exports = GetProps;


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

/**
* The Delta component provides access to delta values between the Game Objects current and previous position.
*
* @class
*/
Phaser.Component.Delta = function () {};

Phaser.Component.Delta.prototype = {

    /**
    * Returns the delta x value. The difference between world.x now and in the previous frame.
    *
    * The value will be positive if the Game Object has moved to the right or negative if to the left.
    *
    * @property {number} deltaX
    * @readonly
    */
    deltaX: {

        get: function() {

            return this.world.x - this.previousPosition.x;

        }

    },

    /**
    * Returns the delta y value. The difference between world.y now and in the previous frame.
    *
    * The value will be positive if the Game Object has moved down or negative if up.
    *
    * @property {number} deltaY
    * @readonly
    */
    deltaY: {

        get: function() {

            return this.world.y - this.previousPosition.y;

        }

    },

    /**
    * Returns the delta z value. The difference between rotation now and in the previous frame.
    *
    * @property {number} deltaZ - The delta value.
    * @readonly
    */
    deltaZ: {

        get: function() {

            return this.rotation - this.previousRotation;

        }

    }

};


var BuildGameObject = require('../BuildGameObject');
var GameObjectCreator = require('../../scene/plugins/GameObjectCreator');
var GetAdvancedValue = require('../../utils/object/GetAdvancedValue');
var Quad = require('./Quad');

//  When registering a factory function 'this' refers to the GameObjectCreator context.

GameObjectCreator.register('quad', function (config)
{
    var x = GetAdvancedValue(config, 'x', 0);
    var y = GetAdvancedValue(config, 'y', 0);
    var key = GetAdvancedValue(config, 'key', null);
    var frame = GetAdvancedValue(config, 'frame', null);

    var quad = new Quad(this.scene, x, y, key, frame);

    BuildGameObject(this.scene, quad, config);

    return quad;
});