
import Texture from "./Texture"
import BaseTexture from './BaseTexture';
import { Rectangle } from "../math";
/**
 * 简单点来说，就是用来拆图集的，TexturePacker的json格式
 * Utility class for maintaining reference to a collection
 * of Textures on a single TextureSheet.
 * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite.
 */
export default class TextureSheet {
    /**
     * Reference to ths source texture
     */
    baseTexture: BaseTexture;
    /**
     * A map containing all textures of the sprite sheet.
     * Can be used to create a {@link Sprite|Sprite}:
     * ```js
     * new Sprite(sheet.textures["image.png"]);
     */
    textures: {};
    /**
     * A map containing the textures for each animation.
     * Can be used to create an {@link extras.AnimatedSprite|AnimatedSprite}:
     * ```js
     * new extras.AnimatedSprite(sheet.animations["anim_name"])
     * ```
     * @member {Object}
     */
    animations: {};
    /**
     * Reference to the original JSON data.
     * @type {Object}
     */
    data: any;
    /**
     * Map of TextureSheet frames.
     * @type {Object}
     * @private
     */
    _frames: any;
    /**
     * Collection of frame names.
     * @type {string[]}
     * @private
     */
    _frameKeys: string[];
    /**
     * Callback when parse is completed.
     * @private
     */
    _callback: Function;

    /**
     * @param {BaseTexture} baseTexture Reference to the source BaseTexture object.
     * @param {Object} data - TextureSheet image data.
     */
    constructor(baseTexture: BaseTexture, data: any) {
        this.baseTexture = baseTexture;
        this.textures = {};
        this.animations = {};
        this.data = data;
        this._frames = this.data.frames;
        this._frameKeys = Object.keys(this._frames);
        this._callback = null;
    }

    /**
     * Parser TextureSheet from loaded data. This is done asynchronously
     * to prevent creating too many Texture within a single process.
     *
     * @param {Function} callback - Callback when complete returns
     *        a map of the Textures for this TextureSheet.
     */
    parse(callback: Function) {
        this._callback = callback;
        this._processFrames(0);
        this._processAnimations();
        //原先里面可能又超过数量的贴图，需要多次处理，所以，额外放一个complete
        this._parseComplete();
    }

    /**
     * Process a batch of frames
     *
     * @private
     * @param {number} initialFrameIndex - The index of frame to start.
     */
    _processFrames(initialFrameIndex: number) {
        let frameIndex = initialFrameIndex;

        while (frameIndex < this._frameKeys.length) {
            const i = this._frameKeys[frameIndex];
            const data = this._frames[i];
            const rect = data.frame;
            if (rect) {
                let frame = null;
                let trim = null;
                //如果是被截掉过透明边界的，则取data.sourceSize（原尺寸）
                const sourceSize = data.trimmed !== false && data.sourceSize
                    ? data.sourceSize : data.frame;

                //贴图原始尺寸
                const orig = new Rectangle(
                    0,
                    0,
                    Math.floor(sourceSize.w),
                    Math.floor(sourceSize.h)
                );

                //图集上的位置
                if (data.rotated) {
                    frame = new Rectangle(
                        Math.floor(rect.x),
                        Math.floor(rect.y),
                        Math.floor(rect.h),
                        Math.floor(rect.w)
                    );
                } else {
                    frame = new Rectangle(
                        Math.floor(rect.x),
                        Math.floor(rect.y),
                        Math.floor(rect.w),
                        Math.floor(rect.h)
                    );
                }

                //  Check to see if the sprite is trimmed
                if (data.trimmed !== false && data.spriteSourceSize) {
                    //其实就是在orig上切图，偏移
                    trim = new Rectangle(
                        Math.floor(data.spriteSourceSize.x),
                        Math.floor(data.spriteSourceSize.y),
                        Math.floor(rect.w),
                        Math.floor(rect.h)
                    );
                }

                this.textures[i] = new Texture(
                    this.baseTexture,
                    frame,
                    orig,
                    trim,
                    data.rotated ? 2 : 0,
                    // data.anchor
                );

                // lets also add the frame to  global cache for fromFrame and fromFrame functions
                Texture.addToCache(this.textures[i], i);
            }

            frameIndex++;
        }
    }

    /**
     * Parse animations config
     *
     * @private
     */
    _processAnimations() {
        const animations = this.data.animations || {};
        for (const animName in animations) {
            this.animations[animName] = [];
            for (const frameName of animations[animName]) {
                this.animations[animName].push(this.textures[frameName]);
            }
        }
    }

    /**
     * The parse has completed.
     *
     * @private
     */
    _parseComplete() {
        const callback = this._callback;
        this._callback = null;
        callback.call(this, this.textures);
    }

    /**
     * Destroy TextureSheet and don't use after this.
     *
     * @param {boolean} [destroyBase=false] Whether to destroy the base texture as well
     */
    destroy(destroyBase: boolean = false) {
        for (const i in this.textures) {
            this.textures[i].destroy();
        }
        this._frames = null;
        this._frameKeys = null;
        this.data = null;
        this.textures = null;
        if (destroyBase) {
            this.baseTexture.destroy();
        }
        this.baseTexture = null;
    }
}
