import { Logger } from '../../Util/Log';
import { ImageFiltering } from '../Filtering';
import { ImageWrapping } from '../Wrapping';
/**
 * Manages loading image sources into webgl textures, a unique id is associated with all sources
 */
export class TextureLoader {
    constructor(gl) {
        this._textureMap = new Map();
        this._gl = gl;
        TextureLoader._MAX_TEXTURE_SIZE = gl.getParameter(gl.MAX_TEXTURE_SIZE);
    }
    dispose() {
        for (const [image] of this._textureMap) {
            this.delete(image);
        }
        this._textureMap.clear();
        this._gl = null;
    }
    /**
     * Get the WebGL Texture from a source image
     * @param image
     */
    get(image) {
        return this._textureMap.get(image);
    }
    /**
     * Returns whether a source image has been loaded as a texture
     * @param image
     */
    has(image) {
        return this._textureMap.has(image);
    }
    /**
     * Loads a graphic into webgl and returns it's texture info, a webgl context must be previously registered
     * @param image Source graphic
     * @param options {ImageSourceOptions} Optionally configure the ImageFiltering and ImageWrapping mode to apply to the loaded texture
     * @param forceUpdate Optionally force a texture to be reloaded, useful if the source graphic has changed
     */
    load(image, options, forceUpdate = false) {
        // Ignore loading if webgl is not registered
        const gl = this._gl;
        if (!gl) {
            return null;
        }
        const { filtering, wrapping } = { ...options };
        let tex = null;
        // If reuse the texture if it's from the same source
        if (this.has(image)) {
            tex = this.get(image);
        }
        // Update existing webgl texture and return early
        if (tex) {
            if (forceUpdate) {
                gl.bindTexture(gl.TEXTURE_2D, tex);
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
            }
            return tex;
        }
        // No texture exists create a new one
        tex = gl.createTexture();
        // TODO implement texture gc with weakmap and timer
        TextureLoader.checkImageSizeSupportedAndLog(image);
        gl.bindTexture(gl.TEXTURE_2D, tex);
        gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
        let wrappingConfig;
        if (wrapping) {
            if (typeof wrapping === 'string') {
                wrappingConfig = {
                    x: wrapping,
                    y: wrapping
                };
            }
            else {
                wrappingConfig = {
                    x: wrapping.x,
                    y: wrapping.y
                };
            }
        }
        const { x: xWrap, y: yWrap } = wrappingConfig !== null && wrappingConfig !== void 0 ? wrappingConfig : TextureLoader.wrapping;
        switch (xWrap) {
            case ImageWrapping.Clamp:
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
                break;
            case ImageWrapping.Repeat:
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
                break;
            case ImageWrapping.Mirror:
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT);
                break;
            default:
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        }
        switch (yWrap) {
            case ImageWrapping.Clamp:
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
                break;
            case ImageWrapping.Repeat:
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
                break;
            case ImageWrapping.Mirror:
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT);
                break;
            default:
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        }
        // NEAREST for pixel art, LINEAR for hi-res
        const filterMode = filtering !== null && filtering !== void 0 ? filtering : TextureLoader.filtering;
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filterMode === ImageFiltering.Pixel ? gl.NEAREST : gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filterMode === ImageFiltering.Pixel ? gl.NEAREST : gl.LINEAR);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
        this._textureMap.set(image, tex);
        return tex;
    }
    delete(image) {
        // Ignore loading if webgl is not registered
        const gl = this._gl;
        if (!gl) {
            return null;
        }
        let tex = null;
        if (this.has(image)) {
            tex = this.get(image);
            gl.deleteTexture(tex);
        }
    }
    /**
     * Takes an image and returns if it meets size criteria for hardware
     * @param image
     * @returns if the image will be supported at runtime
     */
    static checkImageSizeSupportedAndLog(image) {
        var _a;
        const originalSrc = (_a = image.dataset.originalSrc) !== null && _a !== void 0 ? _a : 'internal canvas bitmap';
        if (image.width > TextureLoader._MAX_TEXTURE_SIZE || image.height > TextureLoader._MAX_TEXTURE_SIZE) {
            TextureLoader._LOGGER.error(`The image [${originalSrc}] provided to Excalibur is too large for the device's maximum texture size of ` +
                `(${TextureLoader._MAX_TEXTURE_SIZE}x${TextureLoader._MAX_TEXTURE_SIZE}) please resize to an image ` +
                `for excalibur to render properly.\n\nImages will likely render as black rectangles.\n\n` +
                `Read more here: https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#understand_system_limits`);
            return false;
        }
        else if (image.width > 4096 || image.height > 4096) {
            // https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#understand_system_limits
            TextureLoader._LOGGER.warn(`The image [${originalSrc}] provided to excalibur is too large may not work on all mobile devices, ` +
                `it is recommended you resize images to a maximum (4096x4096).\n\n` +
                `Images will likely render as black rectangles on some mobile platforms.\n\n` +
                `Read more here: https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#understand_system_limits`);
        }
        return true;
    }
}
TextureLoader._LOGGER = Logger.getInstance();
/**
 * Sets the default filtering for the Excalibur texture loader, default [[ImageFiltering.Blended]]
 */
TextureLoader.filtering = ImageFiltering.Blended;
TextureLoader.wrapping = { x: ImageWrapping.Clamp, y: ImageWrapping.Clamp };
TextureLoader._MAX_TEXTURE_SIZE = 4096;
//# sourceMappingURL=texture-loader.js.map