import {
    AmbientLight,
    Clock,
    DirectionalLight,
    Matrix4,
    Mesh,
    PerspectiveCamera,
    PlaneGeometry as PlaneBufferGeometry,
    Scene,
    WebGLRenderer
} from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import "../../../../../../../../css/main.scss";
import Signal from "../../../../core/events/signal/Signal.js";
import { computePathDirectory } from "../../../../core/path/computePathDirectory.js";
import LabelView from "../../../../view/common/LabelView.js";
import { CSS_ABSOLUTE_POSITIONING } from "../../../../view/CSS_ABSOLUTE_POSITIONING.js";
import EmptyView from "../../../../view/elements/EmptyView.js";
import SmoothProgressBar from "../../../../view/elements/progress/SmoothProgressBar.js";
import { AssetManager } from "../../../asset/AssetManager.js";
import { GameAssetType } from "../../../asset/GameAssetType.js";
import { ImageRGBADataLoader } from "../../../asset/loaders/image/ImageRGBADataLoader.js";
import { buildCanvasViewFromTexture } from "../../render/visibility/hiz/buildCanvasViewFromTexture.js";
import { VirtualTextureSystem } from "./VirtualTextureSystem.js";

let camera,
    /**
     * @type {WebGLRenderer}
     */
    renderer,
    clock,
    /**
     * @type {Scene}
     */
    scene;

// let mesh;

const signal_render = new Signal();

// const TEXTURE_URL = "data/textures/utility/uv_map_reference.png";
// const TEXTURE_URL = "data/textures/utility/maurus_uv_checker_8k/UV-CheckerMap_Maurus_01_8K.png";
// const TEXTURE_URL = "data/textures/utility/4096x4096TexelDensityTexture1.png";
// const TEXTURE_URL = "data/textures/utility/Lenna.png";
// const TEXTURE_URL = "data/textures/utility/TESTIMAGES/SAMPLING/8BIT/RGB/2448x2448/SRC/img_2448x2448_3x8bit_SRC_RGB_cards_a.png";
// const TEXTURE_URL = "data/models/LowPolyTownshipSet/Town_Hall//diffuse_2048.png";


// const usageDebugView = new UsageDebugView();
// usageDebugView.position.set(0,256);
//
// window.page = virtualTexturePage;
// window.mapping = memoryMapping;
// window.usage = virtualTextureManager.usage_metadata;

const container_view = new EmptyView();

const options = {
    spin: false,
    get jitter_samples() {
        return virtualTextureManager.frame_jitter_enabled
    },
    set jitter_samples(v) {
        virtualTextureManager.frame_jitter_enabled = v;
    }
};

init();

function makeMesh(mat) {
    const size = 10;


    // const geo = new TetrahedronBufferGeometry(size,30);
    // const geo = new TorusGeometry(size, 0.3, 30, 30);
    const geo = new PlaneBufferGeometry(size, size);

    const mesh = new Mesh(geo, mat);
    const m4 = new Matrix4();

    // m4.makeRotationZ(-Math.PI/2);
    m4.makeScale(1, -1, -1);

    geo.applyMatrix4(m4);

    mesh.rotation.y = Math.PI*0.5;

    scene.add(mesh);

    return mesh;
}

/**
 *
 * @type {VirtualTextureSystem[]}
 */
const virtual_texture_managers = [];

async function loadGLTF({
                            path
                        }) {

    const loader = new GLTFLoader();

    const base_path = computePathDirectory(path);

    const progress = new SmoothProgressBar();

    function layout() {
        progress.size.set(100, 20);
        progress.position.set((container_view.size.x - progress.size.x) * 0.5, (container_view.size.y - progress.size.y) * 0.5);
    }

    layout();
    container_view.size.onChanged.add(layout);

    container_view.addChild(progress);

    const gltf = await new Promise((resolve, reject) => {

        loader.load(path, resolve, event => {
            console.log(event);

            progress.max = event.total;
            progress.value = event.loaded;
        }, reject);
    })

    container_view.removeChild(progress);


    console.warn(gltf);

    const materials = new Set();

    /**
     *
     * @type {Map<Material, Mesh[]>}
     */
    const material_users = new Map();

    /**
     *
     * @type {Map<Material, Material>}
     */
    const replacement_materials = new Map();

    gltf.scene.traverse(n => {
        if (!n.isMesh) {
            return;
        }

        const mat = n.material;
        if (mat === undefined) {
            return;
        }

        if (materials.has(mat)) {
            return;
        }

        const extensionVT = mat.userData?.gltfExtensions?.meepVirtualTexture;
        if (extensionVT === undefined) {
            return;
        }

        materials.add(mat);

        const vts = {};

        for (const textureKey in extensionVT) {
            const textureMeta = extensionVT[textureKey];

            const vt = new VirtualTextureSystem();

            vt.setTexture({
                path: base_path + '/' + textureMeta.path,
                texture_resolution: textureMeta.texture_resolution,
                tile_resolution: textureMeta.tile_size,
                tile_margin: textureMeta.tile_margin
            });

            virtual_texture_managers.push(vt);

            vts[textureKey] = vt;
        }

        if (vts.baseColorTexture !== undefined) {
            replacement_materials.set(mat, vts.baseColorTexture.makeMaterial())
        }
    })

    // replace materials
    gltf.scene.traverse(n => {
        if (n.isMesh) {

            const replacement = replacement_materials.get(n.material);

            if (replacement !== undefined) {
                n.material = replacement;
            }

        }
    });

    return gltf.scene;
}

function flatSample({
                        am
                    }) {

    // virtualTexturePage.path = "data/textures/utility/maurus_uv_checker_8k/vt-01-32";
    // virtualTexturePage.path = "data/textures/utility/maurus_uv_checker_8k/vt-01-128";
    // virtualTexturePage.path = "data/textures/utility/azeroth/vt-01-128";
    // virtualTexturePage.path = "data/textures/utility/aco-greece/vt-01-128";
    // sparseTexture.path = "data/textures/utility/vt/uv_map_reference";
    // virtualTexturePage.path = "data/textures/utility/vt/Lenna";
    // virtualTexturePage.path = "data/textures/utility/Lenna/vt-01-32";
    // sparseTexture.path = "data/textures/utility/vt/TexelDensity1";

    const vts = new VirtualTextureSystem();

    vts.setTexture({
        path: "data/textures/utility/Lenna/vt-01-32",
        texture_resolution: 512,
        tile_resolution: 32,
    });
    vts.initialize({
        asset_manager: am,
        // page_resolution: 1024
    })
    // console.log(virtualTexturePage);

    const material = vts.makeMaterial();

    console.log(material.uniforms);

    const virt = makeMesh(material);


    virtual_texture_managers.push(vts);

}

async function init() {

    const am = new AssetManager();
    await am.registerLoader(GameAssetType.Image, new ImageRGBADataLoader());
    am.startup();

    const container = document.body;

    container.style.margin = "0";

    container.appendChild(container_view.el);
    container_view.link();

    scene = new Scene();

    // const gltf = await loadGLTF({ path: "data/models/Scans/green_car_wreck/converted/model.gltf" })
    // scene.add(gltf);

    flatSample({am});

    virtual_texture_managers.forEach(vt => {
        vt.initialize({
            asset_manager: am,
            page_resolution: 1024
        })
    });

    console.log(virtual_texture_managers);

    const manager = virtual_texture_managers[0];


    //
    // usageDebugView.mip_levels = virtualTextureManager.max_mip_level;
    // container_view.addChild(usageDebugView);


    // const usagePyramidDebugView = new UsagePyramidDebugView();
    // usagePyramidDebugView.setImageURL("data/models/Scans/green_car_wreck/converted/merged_1024.png");
    // container_view.addChild(usagePyramidDebugView);
    //
    // signal_render.add(()=>{
    //
    //     usagePyramidDebugView.setTextureParameters(manager.updater.texture_resolution, manager.updater.tile_resolution);
    //     usagePyramidDebugView.usage = manager.updater.usage_metadata;
    // })

    // const residencyDebugView = new ResidencyDebugView();
    // residencyDebugView.texture = manager.page;
    // residencyDebugView.css({
    //     bottom: 0,
    //     right: 0
    // });
    // container_view.addChild(residencyDebugView);
    //
    // signal_render.add(()=> residencyDebugView.update());

    // ===================


    camera = new PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100);
    camera.position.set(
        12.022084899129627,
        9.60952583639588,
        -0.04984304693423923
    );
    camera.rotation.set(
        -1.5687013871667483,
        0.9201500864954011,
        1.5681634783563936);

    window.camera = camera;

    // scene.background = new Color("#002D62");

    clock = new Clock();

    //
    // const map = new TextureLoader().load(TEXTURE_URL);
    // map.flipY = false;
    //
    // const material = new MeshBasicMaterial({
    //     map,
    //     side: DoubleSide
    // });


    // makeTorus();
    // virt.position.x = -0.4

    // const original_texture = new TextureLoader().load("data/textures/utility/maurus_uv_checker_8k/UV-CheckerMap_Maurus_01_8K.png");
    // original_texture.flipY = false;
    // original_texture.generateMipmaps = true;
    // original_texture.anisotropy = 4;

    // const ref = makeMesh(new MeshBasicMaterial({
    //     // map: original_texture,
    //     color: 0xFFFFFF,
    //     side: DoubleSide
    // }));
    //
    // ref.position.x = 0.4;

    // makeGLTF("data/models/LowPolyTownshipSet/Town_Hall/model.gltf");

    scene.add(new DirectionalLight(0xFFFFFF, 0.7));
    scene.add(new AmbientLight(0xFFFFFF, 0.2));


    //

    renderer = new WebGLRenderer({ antialias: true });
    renderer.setClearColor("#002D62", 1);

    renderer.setPixelRatio(window.devicePixelRatio);
    container.appendChild(renderer.domElement);
    renderer.autoClear = false;

    window.renderer = renderer;

    //


    //

    onWindowResize();

    window.addEventListener("resize", onWindowResize);

    new OrbitControls(camera, renderer.domElement);


    // const gui = new GUI();
    //
    // Object.keys(options).forEach(key => {
    //     gui.add(options, key);
    // });


    animate();


    const mappingPreview = buildCanvasViewFromTexture({
        texture: manager.page.texture,
        renderer,
        swizzle: ['r', 'g', 'b', 1],
        width: 256,
        height: 256,
        flipY: false
    });

    mappingPreview.view.css(CSS_ABSOLUTE_POSITIONING);
    mappingPreview.view.css({
        // border: "1px solid red"
        boxShadow: "0 0 8px rgba(0,0,0,0.4)",
    });
    mappingPreview.view.position.set(4, 4);


    signal_render.add(() => {
        mappingPreview.render();
    });

    container_view.addChild(mappingPreview.view);

    // credits
    container_view.addChild(new LabelView(`MODEL: Green car wreck, by matousekfoto, CC-BY-4.0`, {
        css: {
            position: "absolute",
            bottom: "4px",
            width: "100%",
            textAlign: "center",
            color: "#88e1d2",
            textShadow: "0 0 3px black",
            fontFamily: "monospace"
        }
    }));
}

function onWindowResize() {

    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);

}

//

function animate() {

    requestAnimationFrame(animate);

    render();

}

function render() {

    const delta = 5 * clock.getDelta();

    // if (options.spin && mesh !== undefined) {
    //     mesh.rotation.y += 0.0125 * delta;
    //     mesh.rotation.x += 0.05 * delta;
    // }

    virtual_texture_managers.forEach(vts => {
        vts.update(renderer, scene, camera);
    });

    // renderer.setViewport(0, 0, view_port_size.x, view_port_size.y);
    renderer.clear();
    renderer.render(scene, camera)

    // usageDebugView.usage = virtualTextureManager.usage_metadata;

    // residencyDebugView.update();

    signal_render.send0();
}
