import { EventDispatcher } from '../events/EventDispatcher';
import { isPow2, BaseTextureCache, TextureCache, uid } from '../utils';
import { SCALE_MODES, WRAP_MODES } from "../const"
import { GlobalLoader } from '../loader';

/**
 * 每个texture都有一个BaseTexture，多数用于图集，texture可自身设置属性
 * 实际绘制用的都是BaseTexture
 * @class
 * @extends EventDispatcher
 */
export default class BaseTexture extends EventDispatcher {
    /**
     * 贴图回收时用到，
     * 标记是否被使用过
     */
    _touchedId: number;
    /**
     * 批处理时用到的标志位
     * 被使用着的id
     */
    _enabledId: number;
    /**
     * 宽度
     */
    width: number;
    /**
     * 高度
     */
    height: number;
    /**
     * SCALE_MODES，一般是线性
     * 用于glTexture
     */
    scaleMode: SCALE_MODES;
    /**
     * 加载完成会设置为true
     * 加载失败或没有贴图数据则为false，
     * 通常用于标记基础纹理是否可用
     */
    hasLoaded: boolean;
    /**
     * 正在加载
     */
    private _isLoading: boolean;
    /**
     * image类型 eg. `png`
     * 暂时不考虑svg
     * @readonly
     */
    imageType: string;
    /**
     * rgb预乘alpha，webgl用到，png图片设置必为true，否则色值会出问题
     * @default true
     */
    premultipliedAlpha: boolean;
    /**
     * 图片路径
     * @member {string}
     */
    imageUrl: string;
    /**
     * 是否尺寸为2的次方，尽可能图集尺寸都为2的次方，gpu处理方便，并且能做mipmap缓存，性能更好
     */
    isPowerOfTwo: boolean;
    /**
     * 尺寸是2的次方下才能设置true，用于生成mipmap缓存
     * @default true
     */
    mipmap: boolean;
    /**
     * 非2的次方时要设置CLAMP
     * WebGL Texture wrap mode
     * @default WRAP_MODES.CLAMP
     */
    wrapMode: WRAP_MODES;
    /**
     * A map of renderer IDs to webgl textures
     * 不同渲染器对应的记录，暂时应该只需要一个
     * @member {object<number, WebGLTexture>}
     */
    _glTextures: {};
    /**
     * The ids under which this BaseTexture has been added to the base texture cache. This is
     * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
     * BaseTexture is added directly to the BaseTextureCache array.
     * @member {string[]}
     */
    textureCacheIds: string[];

    /**
     * image对象，canvas对象，或者imageData（canvas渲染模式用不了，是否考虑去掉），以后类型增多后再说
     * {
     * data:Uint8Array,
     * width:number,
     * height:number,
     * type:string,
     * path:string
     * }
     */
    source;
    /**
     * @param {} [source] - 源数据
     * @param {SCALE_MODES}
     */
    constructor(source = null, scaleMode: SCALE_MODES = SCALE_MODES.LINEAR) {
        super();
        this._touchedId = 0;
        this.width = 100;
        this.height = 100;
        this.scaleMode = scaleMode;
        this.hasLoaded = false;
        this._isLoading = false;
        this.source = null;
        this.imageType = null;
        this.premultipliedAlpha = true;
        this.imageUrl = null;
        this.isPowerOfTwo = false;
        this.mipmap = true;
        this.wrapMode = WRAP_MODES.CLAMP;
        this._glTextures = {};
        this._enabledId = 0;
        this.textureCacheIds = [];
        //有source要加载，构造函数不缓存baseTexture，要缓存用fromUrl
        if (source) {
            //是imageData
            if (source.data) {
                this._sourceChange(source);
            }
            //是canvas
            else if (source.getContext) {
                this._sourceChange(source);
            }
            else {
                if (source.complete) {
                    this._sourceChange(source);
                    this.dispatchEvent("loaded");
                } else {
                    var self = this;
                    //会覆盖onload方法
                    source.onload = function () {
                        self._sourceChange(source);
                        self.dispatchEvent("loaded");
                    };
                    //失败的
                    source.onerror = function () {
                        self.dispatchEvent("loaded");
                    };
                }
            }
        }
    }

    /**
     * 会触发更新事件
     * @fires BaseTexture#update
     */
    update() {
        //尺寸重置
        this.width = this.source.width;
        this.height = this.source.height;
        //再来判断一次
        this.hasLoaded = this.width && this.height ? true : false;
        //判断是否是2的n次方
        this.isPowerOfTwo = isPow2(this.width) && isPow2(this.height);

        //触发绑定过的更新事件，比如webgl纹理更新
        this.dispatchEvent("update")
    }

    /**
     * 
     */
    _sourceChange(source) {
        //赋值
        this.source = source;
        //路径，暂时没用，需要时再处理，先找src 再path（基本没有）
        this.imageUrl = source.src || source.path || null;
        //更新类型,暂时没用，也没做，需要是处理
        this.imageType = this.source.type || null;
        //加载完成
        this.hasLoaded = true;
        //更新
        this.update();
        //触发事件
        this.dispatchEvent("loaded");
    }

    /**
     * 销毁 base texture
     * 基本不会销毁纹理
     */
    destroy() {
        if (this.imageUrl) {
            delete TextureCache[this.imageUrl];
            this.imageUrl = null;
        }
        this.source = null;
        this.dispose();
        BaseTexture.removeFromCache(this);
        this.textureCacheIds = null;
    }

    /**
     * 用于释放gpu缓存，并不销毁纹理，需要时可再上传到GPU
     * @fires BaseTexture#dispose
     */
    dispose() {
        //用于触发TextureManager中监听的
        this.dispatchEvent("dispose")
    }

    //辅助静态方法
    /**
     * 根据路径，会缓存baseTexture
     * @param {string} url  路径
     */
    static fromUrl(url: string) {
        if (BaseTextureCache[url]) return BaseTextureCache[url];
        let baseTexture = new BaseTexture();
        GlobalLoader.loadImage((s, res) => {
            if (s) {
                baseTexture._sourceChange(res);
                //添加进缓存
                if (url.indexOf('data:') !== 0) BaseTexture.addToCache(baseTexture, url);
            } else {
                //失败的时候也需要派发加载事件
                baseTexture.dispatchEvent("loaded");
            }
        }, url)
        return baseTexture
    }

    /**
     * 随便啥形式的，比如data，
     * @param data 
     */
    static fromData(data) {
        return new BaseTexture(data);
    }

    /**
     * 从离屏canvas创建的，会给canvas加唯一标识_canvasId，并缓存
     */
    static fromCanvas(canvas: HTMLCanvasElement, origin: string = 'canvas') {
        //标记canvasId
        if (!canvas["_canvasId"]) {
            canvas["_canvasId"] = `${origin}_${uid()}`;
        }
        let baseTexture = BaseTextureCache[canvas["_canvasId"]];
        if (!baseTexture) {
            baseTexture = new BaseTexture(canvas);
            BaseTexture.addToCache(baseTexture, canvas["_canvasId"]);
        }
        return baseTexture;
    }
    /**
     * 根据图片
     * @param image 
     */
    static fromImage(image: HTMLImageElement) {
        //图片标签
        const imageUrl = image.src;
        let baseTexture = BaseTextureCache[imageUrl];
        if (!baseTexture) {
            baseTexture = new BaseTexture(image);
            //不缓存base64，如需要，设名字，手动缓存
            if (imageUrl && imageUrl.indexOf('data:') !== 0) BaseTexture.addToCache(baseTexture, imageUrl);
        }
        return baseTexture;
    }
    /**
     * 所有形式，图片路径，canvas标签，图片标签，或者数据
     * @param anything 
     * @returns 
     */
    static from(anything: string | HTMLCanvasElement | HTMLImageElement) {
        //路径
        if (typeof anything === 'string') {
            return BaseTexture.fromUrl(anything);
        }
        //@ts-ignore
        else if (anything.data) {
            return BaseTexture.fromData(anything);
        }
        //@ts-ignore canvas
        else if (anything.getContext) {
            //@ts-ignore
            return BaseTexture.fromCanvas(anything);
        } else {
            //@ts-ignore
            return BaseTexture.fromImage(anything);
        }
    }

    /**
     * 加入全局基础纹理缓存
     * @static
     * @param {BaseTexture} baseTexture
     * @param {string} id
     */
    static addToCache(baseTexture: BaseTexture, id: string) {
        if (id) {
            if (baseTexture.textureCacheIds.indexOf(id) === -1) {
                baseTexture.textureCacheIds.push(id);
            }

            if (BaseTextureCache[id]) {
                //覆盖
                console.warn(`id为${id}的基础纹理已存在，即将覆盖`);
            }
            BaseTextureCache[id] = baseTexture;
        }
    }

    /**
     * 移除缓存
     * @static
     * @param {string|BaseTexture} baseTexture id或者BaseTexture
     * @return {BaseTexture|null} 移除的BaseTexture或null
     */
    static removeFromCache(baseTexture: string | BaseTexture): BaseTexture {
        if (typeof baseTexture === 'string') {
            const baseTextureFromCache = BaseTextureCache[baseTexture];
            if (baseTextureFromCache) {
                const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
                if (index > -1) {
                    baseTextureFromCache.textureCacheIds.splice(index, 1);
                }
                delete BaseTextureCache[baseTexture];
                return baseTextureFromCache;
            }
        }
        else if (baseTexture && baseTexture.textureCacheIds) {
            for (let i = 0; i < baseTexture.textureCacheIds.length; ++i) {
                delete BaseTextureCache[baseTexture.textureCacheIds[i]];
            }
            baseTexture.textureCacheIds.length = 0;
            return baseTexture;
        }
        return null;
    }
}
