import { computeFileExtension } from "../../../../core/path/computeFileExtension.js";
import { convertTexture2Sampler2D } from "../../../graphics/texture/sampler/convertTexture2Sampler2D.js";
import { GameAssetType } from "../../GameAssetType.js";
import { ArrayBufferLoader } from "../ArrayBufferLoader.js";
import { AssetLoader } from "../AssetLoader.js";
import { CodecWithFallback } from "./codec/CodecWithFallback.js";
import { NativeImageDecoder } from "./codec/NativeImageDecoder.js";
import { ThreadedImageDecoder } from "./codec/ThreadedImageDecoder.js";
import { ImageRGBADataAsset } from "./ImageRGBADataAsset.js";

const ASSET_TYPE_ARRAY_BUFFER = "arraybuffer";

export class ImageRGBADataLoader extends AssetLoader {
    /**
     *
     * @param {string} [worker_path] Path to decoder worker, allows flexibility in deployment
     */
    constructor({
                    worker_path
                } = {}) {
        super();

        /**
         *
         * @type {CodecWithFallback<{data, width:number, height:number, itemSize:number, bitDepth:number}>}
         */
        this.decoder = new CodecWithFallback(
            new ThreadedImageDecoder({ worker_path }),
            new NativeImageDecoder()
        );
    }

    async link(assetManager, engine) {

        if (assetManager.getLoaderByType(ASSET_TYPE_ARRAY_BUFFER) === undefined) {
            await assetManager.registerLoader(ASSET_TYPE_ARRAY_BUFFER, new ArrayBufferLoader());
        }

        await super.link(assetManager, engine);
    }

    /**
     *
     * @param {string} path
     * @param {AssetRequestScope} scope
     * @returns {Promise<{data:ArrayBuffer, width: number, height:number, itemSize: number, bitDepth:number}>}
     * @private
     */
    async __decode_via_worker(path, scope) {
        const asset = await this.assetManager.promise(path, ASSET_TYPE_ARRAY_BUFFER, { scope });

        /**
         * @type {ArrayBuffer}
         */
        const array_buffer = asset.create();

        return await this.decoder.decode(new Uint8Array(array_buffer));
    }

    /**
     *
     * @param {String} path
     * @param {AssetRequestScope} scope
     * @return {Promise<ImageRGBADataAsset>}
     * @private
     */
    async __standard(path, scope) {

        const bitmap = await this.__decode_via_worker(path, scope);

        let data;

        const bitDepth = bitmap.bitDepth;

        switch (bitDepth) {
            case 1:
            case 2:
            case 4:
            case 8:
                data = new Uint8Array(bitmap.data);
                break;
            case 16:
                // PNGReader normalizes 16-bit samples to platform-endian, so the
                // buffer can be viewed as Uint16Array directly.
                data = new Uint16Array(bitmap.data);
                break;
            default:
                throw new Error(`Unsupported bit depth '${bitDepth}'. path='${path}'`);
        }

        return new ImageRGBADataAsset(data, bitmap.width, bitmap.height, bitmap.itemSize);
    }

    load(scope, path, success, failure, progress) {
        const extension = computeFileExtension(path);

        if (extension === 'dds') {
            //compressed texture
            this.assetManager.get({
                path: path,
                scope,
                type: GameAssetType.Texture,
                callback: asset => {

                    /**
                     *
                     * @type {Texture}
                     */
                    const texture = asset.create();

                    const sampler2D = convertTexture2Sampler2D(texture);

                    texture.dispose();

                    const f = new ImageRGBADataAsset(sampler2D.data, sampler2D.width, sampler2D.height, sampler2D.itemSize);

                    success(f);

                }, failure: failure, progress: progress
            });

        } else {

            this.__standard(path, scope).then(success, failure);

        }
    }
}
