import { Vector2 } from "three";
import { assert } from "../../../../core/assert.js";
import { VirtualTextureMaterial } from "./VirtualTextureMaterial.js";
import { VirtualTextureMemoryMapping } from "./VirtualTextureMemoryMapping.js";
import { VirtualTexturePage } from "./VirtualTexturePage.js";
import { VirtualTextureUsageUpdater } from "./VirtualTextureUsageUpdater.js";
import { VT_DEFAULT_PAGE_RESOLUTION } from "./VT_DEFAULT_PAGE_RESOLUTION.js";

export class VirtualTextureSystem {
    updater = new VirtualTextureUsageUpdater()
    page = new VirtualTexturePage()
    mapping = new VirtualTextureMemoryMapping()

    #managed_materials_uniforms = {

        u_page: { value: null },
        u_page_resolution: {
            value: new Vector2(0, 0)
        },
        u_tile_resolution: { value: 1 },
        u_tile_padding: { value: 0 },
        u_mapping: { value: null },
        u_texture_resolution: { value: 0 },
        u_max_mip_level: { value: 0 },
        u_mapping_texture_width: { value: 0 }
    };

    setTexture({
                   path,
                   texture_resolution,
                   tile_resolution,
                   tile_margin,
               }) {
        this.updater.setTextureParameters(
            texture_resolution,
            tile_resolution,
            3
        );
        this.page.tile_resolution = this.updater.tile_resolution;
        this.page.path = path;

        this.mapping.resolution = this.updater.texture_resolution / this.updater.tile_resolution;
    }

    #updateUniforms() {
        const uniforms = this.#managed_materials_uniforms;


        const page = this.page;
        const mapping = this.mapping;

        uniforms.u_page.value = page.texture;
        uniforms.u_page_resolution.value.set(
            page.page_texture_resolution_in_tiles[0],
            page.page_texture_resolution_in_tiles[1]
        );
        uniforms.u_tile_resolution.value = page.tile_resolution;
        uniforms.u_tile_padding.value = page.tile_margin;
        uniforms.u_mapping.value = mapping.texture;
        uniforms.u_texture_resolution.value = mapping.resolution * page.tile_resolution;
        uniforms.u_max_mip_level.value = mapping.max_mip_level;
        uniforms.u_mapping_texture_width.value = mapping.texture.image.width;
    }

    initialize({
                   page_resolution = VT_DEFAULT_PAGE_RESOLUTION,
                   asset_manager
               }) {

        assert.defined(asset_manager, 'asset_manager');
        assert.isObject(asset_manager, 'asset_manager');
        assert.equal(asset_manager.isAssetManager, true, 'asset_manager.isAssetManager !== true');

        assert.isNonNegativeInteger(page_resolution, 'page_resolution');

        this.page.page_texture_size = [page_resolution, page_resolution];
        this.page.asset_manager = asset_manager;

    }

    makeMaterial() {
        return new VirtualTextureMaterial({
            uniforms: this.#managed_materials_uniforms
        });
    }

    /**
     *
     * @param {WebGLRenderer} renderer
     * @param {Scene} scene
     * @param {Camera} camera
     */
    update(renderer, scene, camera) {
        this.page.renderer = renderer;

        const view_port_size = renderer.getSize(new Vector2());
        view_port_size.multiplyScalar(renderer.getPixelRatio());

        this.updater.setViewportResolution(view_port_size.x, view_port_size.y);
        this.updater.updateUsage(renderer, scene, camera);
        this.page.update_usage(this.updater.usage_metadata);
        this.mapping.residency = this.page;

        //
        this.#updateUniforms();
    }
}