import { AbstractParticleRenderer } from "../AbstractParticleRenderer.js";
import { DataTexture, FloatType, RGBAFormat } from "three";
import { TextureAtlas } from "../../../../texture/atlas/TextureAtlas.js";
import IdPool from "../../../../../../core/IdPool.js";
import { AtlasPatch } from "../../../../texture/atlas/AtlasPatch.js";

const EMPTY_PATCH = new AtlasPatch();

export class BillboardParticleRenderer extends AbstractParticleRenderer {
    constructor() {
        super();


        /**
         *
         * @type {WebGL2RenderingContext}
         * @private
         */
        this.__gl = null;

        /**
         * Images for billboards are referenced by an unsigned integer. This integer is used for looking up actual UV coordinates for image patch inside the atlas
         * @type {DataTexture}
         * @private
         */
        this.__image_lookup_texture = new DataTexture(new Uint8Array(0), 0, 0, RGBAFormat, FloatType);
        this.__image_lookup_texture.generateMipmaps = false;

        /**
         *
         * @type {number}
         * @private
         */
        this.__image_atlas_padding = 4;

        /**
         *
         * @type {TextureAtlas}
         * @private
         */
        this.__image_atlas = new TextureAtlas();


        /**
         *
         * @type {Map<number, AtlasPatch>}
         * @private
         */
        this.__image_atlas_patches = new Map();

        /**
         *
         * @type {IdPool}
         * @private
         */
        this.__image_ids = new IdPool();

        /**
         *
         * @type {boolean}
         * @private
         */
        this.__image_lookup_texture_needs_update = false;
    }

    __updateImageLookupTexture() {

        // clear the flag
        this.__image_lookup_texture_needs_update = false;
    }

    /**
     * Create a new image slot
     * @returns {number} ID of newly created image
     */
    createImage() {
        const id = this.__image_ids.get();

        this.__image_atlas_patches.set(id, EMPTY_PATCH);

        return id;
    }

    /**
     * Remove image
     * @param {number} id
     * @returns {boolean}
     */
    deleteImage(id) {
        if (!this.__image_ids.isUsed(id)) {
            // ID is not used
            return false;
        }

        this.__image_ids.release(id);

        /**
         *
         * @type {AtlasPatch}
         */
        const patch = this.__image_atlas_patches.get(id);

        this.__image_atlas_patches.delete(id);

        this.__image_atlas.remove(patch);

        return true;
    }

    /**
     * Upload an image into a given slot
     * @param {number} id Image ID
     * @param {Sampler2D} data
     */
    uploadImage(id, data) {
        const existing_patch = this.__image_atlas_patches.get(id);

        if (existing_patch === undefined) {
            throw new Error(`No patch data for ID '${id}'`);
        }

        if (existing_patch !== EMPTY_PATCH) {
            this.__image_atlas.remove(existing_patch);
        }

        const patch = this.__image_atlas.add(data, this.__image_atlas_padding);

        this.__image_atlas_patches.set(id, patch);

    }

    startup() {
        const engine = this.engine;

    }

    finalize() {
        // release resources
    }

    initialize(engine) {
        super.initialize(engine);

        this.__gl = engine.graphics.renderer.getContext();

        //initialize the material
    }

    render(commands) {
        this.__image_atlas.update();

        if (this.__image_lookup_texture_needs_update) {
            this.__updateImageLookupTexture();
        }


        commands.forEach(this.__render, this);
    }

    /**
     *
     * @param {ParticleRenderSpecification} spec
     * @param {EmitterAttributeData} data
     * @private
     */
    __render(spec, data) {
        // TODO upload attribute data into relevant shader slots
        // TODO upload uniform data into relevant shader slots
    }
}
