import { EngineHarness } from "../../../../EngineHarness.js";
import RenderSystem from "../../../../ecs/systems/RenderSystem.js";

import '../../../../../../../../../css/main.scss';
import { GameAssetType } from "../../../../asset/GameAssetType.js";
import { TextureAssetLoader } from "../../../../asset/loaders/texture/TextureAssetLoader.js";
import Vector3 from "../../../../../core/geom/Vector3.js";
import Vector2 from "../../../../../core/geom/Vector2.js";
import { GLTFAssetLoader } from "../../../../asset/loaders/GLTFAssetLoader.js";
import Entity from "../../../../ecs/Entity.js";
import Renderable from "../../../../ecs/renderable/Renderable.js";
import { Transform } from "../../../../ecs/transform/Transform.js";
import { merge_geometry_hierarchy } from "./merge_geometry_hierarchy.js";
import {
    AmbientOcclusionPostProcessEffect
} from "../../../render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js";
import GUIElementSystem from "../../../../ecs/gui/GUIElementSystem.js";
import ViewportPositionSystem from "../../../../ecs/gui/position/ViewportPositionSystem.js";
import HeadsUpDisplaySystem from "../../../../ecs/gui/hud/HeadsUpDisplaySystem.js";
import HeadsUpDisplay from "../../../../ecs/gui/hud/HeadsUpDisplay.js";
import ViewportPosition from "../../../../ecs/gui/position/ViewportPosition.js";
import GUIElement from "../../../../ecs/gui/GUIElement.js";
import LabelView from "../../../../../view/common/LabelView.js";
import { halton_sequence } from "../../../../../core/math/statistics/halton_sequence.js";
import { Color } from "../../../../../core/color/Color.js";
import { MeshStandardMaterial } from 'three';
import { lerp } from "../../../../../core/math/lerp.js";
import { cloneObject3D } from "../../../three/cloneObject3D.js";

function apply_random_coloring(object) {
    const result = cloneObject3D(object);

    const material_set = new Map();

    result.traverse(o => {
        if (o.isMesh) {
            let objects = material_set.get(o.geometry);

            if (objects === undefined) {
                objects = [];
                material_set.set(o.geometry, objects);
            }

            objects.push(o);
        }
    });

    const material_array = Array.from(material_set.keys());

    const material_count = material_array.length;
    for (let i = 0; i < material_count; i++) {

        const key_0 = i % 32;
        const key_1 = Math.ceil(i / 64)

        const h = halton_sequence(32, key_0);
        const s = halton_sequence(Math.ceil(material_count / 64), key_1)

        const material = material_array[i];

        const objects = material_set.get(material);

        const m = new MeshStandardMaterial({ color: Color.fromHSV(h, lerp(0.6, 1, s), 1).toUint() });


        objects.forEach(o => {
            o.material = m;
        });
    }

    return result;
}

function compute_meshes(o) {
    let r = 0;
    o.traverse(obj => {
        if (obj.isMesh) {

            r++;

        }
    });

    return r;
}

/**
 *
 * @param {Engine} engine
 */
async function main(engine) {
    await EngineHarness.buildBasics({
        engine,
        yaw: 0,
        pitch: 1,
        focus: new Vector3(138, 0, 125),
        distance: 30,
        terrainResolution: 1,
        terrainSize: new Vector2(128, 128),
        enableWater: false
    });

    const gltf = await engine.assetManager.promise("moicon/gnutti_not_optimized/model.gltf", GameAssetType.ModelGLTF_JSON);

    const hierarchy = gltf.create();

    const optimized_hierarchy = merge_geometry_hierarchy(hierarchy);

    const css = {
        background: "rgba(0,0,0,0.5)",
        color: "white",
        border: "1px solid black",
        width: "min-content",
        padding: "4px",
        borderRadius: "4px",
        whiteSpace: "pre"
    };

    new Entity()
        .add(new Renderable(apply_random_coloring(optimized_hierarchy)))
        .add(Transform.fromJSON({
            position: new Vector3(128, 2, 128)
        }))
        .add(HeadsUpDisplay.fromJSON({
            worldOffset: new Vector3(0, 2, 0)
        }))
        .add(new ViewportPosition())
        .add(GUIElement.fromView(new LabelView(`optimized\ninstances: ${compute_meshes(optimized_hierarchy)}`, {
            css
        })))
        .build(engine.entityManager.dataset);

    new Entity()
        .add(new Renderable(apply_random_coloring(hierarchy)))
        .add(Transform.fromJSON({
            position: new Vector3(138, 2, 128)
        }))
        .add(HeadsUpDisplay.fromJSON({
            worldOffset: new Vector3(0, 2, 0)
        }))
        .add(new ViewportPosition())
        .add(GUIElement.fromView(new LabelView(`original\ninstances: ${compute_meshes(hierarchy)}`, {
            css
        })))
        .build(engine.entityManager.dataset);


    new Entity()
        .add(new Renderable(optimized_hierarchy))
        .add(Transform.fromJSON({
            position: new Vector3(148, 2, 128)
        }))
        .add(HeadsUpDisplay.fromJSON({
            worldOffset: new Vector3(0, 2, 0)
        }))
        .add(new ViewportPosition())
        .add(GUIElement.fromView(new LabelView(`original look`, {
            css
        })))
        .build(engine.entityManager.dataset);

}

new EngineHarness().initialize({
    configuration(config, engine) {
        config.addSystem(new RenderSystem(engine.graphics));
        config.addSystem(new GUIElementSystem(engine.gameView, engine));
        config.addSystem(new ViewportPositionSystem(engine.graphics.viewport.size));
        config.addSystem(new HeadsUpDisplaySystem(engine.graphics));

        config.addLoader(GameAssetType.ModelGLTF_JSON, new GLTFAssetLoader());
        config.addLoader(GameAssetType.Texture, new TextureAssetLoader());

        config.addPlugin(AmbientOcclusionPostProcessEffect);
    }
}).then(main)
