import FacingDirectionSystem from "../../../../../../../model/game/ecs/system/FacingDirectionSystem.js";
import { makeEngineOptionsModel } from "../../../../../../../model/game/options/makeEngineOptionsModel.js";
import { enableEditor } from "../../../../../../editor/enableEditor.js";
import Vector3 from "../../../../../core/geom/Vector3.js";
import { clamp01 } from "../../../../../core/math/clamp01.js";
import { computeCatmullRomSpline } from "../../../../../core/math/spline/computeCatmullRomSpline.js";
import { CanvasView } from "../../../../../view/elements/CanvasView.js";
import { GameAssetType } from "../../../../asset/GameAssetType.js";
import { GLTFAssetLoader } from "../../../../asset/loaders/GLTFAssetLoader.js";
import { ImageRGBADataLoader } from "../../../../asset/loaders/image/ImageRGBADataLoader.js";
import { JsonAssetLoader } from "../../../../asset/loaders/JsonAssetLoader.js";
import { TextureAssetLoader } from "../../../../asset/loaders/texture/TextureAssetLoader.js";
import { Animation } from "../../../../ecs/animation/Animation.js";
import { AttachmentSystem } from "../../../../ecs/attachment/AttachmentSystem.js";
import Entity from "../../../../ecs/Entity.js";
import { EntityBlueprint } from "../../../../ecs/EntityBlueprint.js";
import GUIElementSystem from "../../../../ecs/gui/GUIElementSystem.js";
import HeadsUpDisplaySystem from "../../../../ecs/gui/hud/HeadsUpDisplaySystem.js";
import ViewportPositionSystem from "../../../../ecs/gui/position/ViewportPositionSystem.js";
import { InverseKinematicsSystem } from "../../../../ecs/ik/InverseKinematicsSystem.js";
import AnimationSystem from "../../../../ecs/systems/AnimationSystem.js";
import ScriptSystem from "../../../../ecs/systems/ScriptSystem.js";
import TimerSystem from "../../../../ecs/systems/TimerSystem.js";
import ClingToTerrainSystem from "../../../../ecs/terrain/ecs/cling/ClingToTerrainSystem.js";
import TerrainSystem from "../../../../ecs/terrain/ecs/TerrainSystem.js";
import { Transform } from "../../../../ecs/transform/Transform.js";
import { EngineConfiguration } from "../../../../EngineConfiguration.js";
import { EngineHarness } from "../../../../EngineHarness.js";
import { GridPosition2TransformSystem } from "../../../../grid/grid2transform/GridPosition2TransformSystem.js";
import GridPositionSystem from "../../../../grid/position/GridPositionSystem.js";
import { Transform2GridPositionSystem } from "../../../../grid/transform2grid/Transform2GridPositionSystem.js";
import InputControllerSystem from "../../../../input/ecs/systems/InputControllerSystem.js";
import { InputSystem } from "../../../../input/ecs/systems/InputSystem.js";
import { BehaviorComponent } from "../../../../intelligence/behavior/ecs/BehaviorComponent.js";
import { BehaviorSystem } from "../../../../intelligence/behavior/ecs/BehaviorSystem.js";
import { InterpolationType } from "../../../../navigation/ecs/components/InterpolationType.js";
import Path from "../../../../navigation/ecs/components/Path.js";
import { PathEvents } from "../../../../navigation/ecs/components/PathEvents.js";
import PathFollowingSystem from "../../../../navigation/ecs/path_following/PathFollowingSystem.js";
import { SoundEmitterSystem } from "../../../../sound/ecs/emitter/SoundEmitterSystem.js";
import SoundControllerSystem from "../../../../sound/ecs/SoundControllerSystem.js";
import SoundListenerSystem from "../../../../sound/ecs/SoundListenerSystem.js";
import {
    AmbientOcclusionPostProcessEffect
} from "../../../render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js";
import AnimationControllerSystem from "../../animation/AnimationControllerSystem.js";
import { AnimationGraphSystem } from "../../animation/animator/AnimationGraphSystem.js";
import { CameraSystem } from "../../camera/CameraSystem.js";
import TopDownCameraControllerSystem from "../../camera/topdown/TopDownCameraControllerSystem.js";
import { TopDownCameraLanderSystem } from "../../camera/topdown/TopDownCameraLanderSystem.js";
import Highlight from "../../highlight/Highlight.js";
import MeshHighlightSystem from "../../highlight/system/MeshHighlightSystem.js";
import LightSystem from "../../light/LightSystem.js";
import { ShadedGeometrySystem } from "../../mesh-v2/ShadedGeometrySystem.js";
import Mesh from "../../mesh/Mesh.js";
import { MeshSystem } from "../../mesh/MeshSystem.js";
import Trail2DSystem from "../../trail2d/Trail2DSystem.js";
import WaterSystem from "../../water/WaterSystem.js";
import { EntityPathMarkerDefinition } from "../entity/EntityPathMarkerDefinition.js";
import { EntityPathStyle } from "../entity/EntityPathStyle.js";
import { PathDisplay } from "../PathDisplay.js";
import { PathDisplaySpec } from "../PathDisplaySpec.js";
import { PathDisplaySystem } from "../PathDisplaySystem.js";
import { PathDisplayType } from "../PathDisplayType.js";
import { RibbonPathStyle } from "../ribbon/RibbonPathStyle.js";
import { CapType } from "./CapType.js";
import { MatcapMaterialDefinition } from "./MatcapMaterialDefinition.js";
import { TubeMaterialType } from "./TubeMaterialType.js";
import { TubePathStyle } from "./TubePathStyle.js";

const engineHarness = new EngineHarness();

function makeConfig(engine) {

    const config = new EngineConfiguration();

    /**
     *
     * @type {SoundEngine}
     */
    const sound = engine.sound;
    const graphics = engine.graphics;
    const assetManager = engine.assetManager;
    const devices = engine.devices;

    const guiSystem = new GUIElementSystem(engine.gui.view, engine);
    const headsUpDisplaySystem = new HeadsUpDisplaySystem(graphics);

    config.systems.push(
        new ScriptSystem(),
        new FacingDirectionSystem(),
        new PathFollowingSystem(),
        new AttachmentSystem(),
        new SoundEmitterSystem(assetManager, sound.destination, sound.context),
        new SoundControllerSystem(),
        new SoundListenerSystem(sound.context),
        new TimerSystem(),
        guiSystem,
        new AnimationSystem(graphics.viewport.size),
        new TopDownCameraControllerSystem(graphics),
        new TopDownCameraLanderSystem(),
        new CameraSystem(engine.graphics),
        new MeshSystem(engine),
        new ClingToTerrainSystem(),
        new TerrainSystem(graphics, assetManager),
        new WaterSystem(graphics),
        new Trail2DSystem(engine),
        new ViewportPositionSystem(graphics.viewport.size),
        new GridPosition2TransformSystem(),
        new Transform2GridPositionSystem(),
        new GridPositionSystem(),
        new InputControllerSystem(devices),
        new InputSystem(devices),
        new MeshHighlightSystem(engine),
        new LightSystem(engine, {
            shadowResolution: 1024
        }),
        new AnimationControllerSystem(),
        new AnimationGraphSystem(graphics.viewport.size),
        headsUpDisplaySystem,
        new BehaviorSystem(engine),
        new InverseKinematicsSystem(),
        new PathDisplaySystem(engine),
    );

    // Plugins

    config.addPlugin(AmbientOcclusionPostProcessEffect)

    // Knowledge

    // Assets

    config.addLoader(GameAssetType.ModelGLTF, new GLTFAssetLoader());
    config.addLoader(GameAssetType.ModelGLTF_JSON, new GLTFAssetLoader());
    config.addLoader(GameAssetType.JSON, new JsonAssetLoader());
    config.addLoader(GameAssetType.Texture, new TextureAssetLoader());
    config.addLoader(GameAssetType.Image, new ImageRGBADataLoader());

    return config;
}

/**
 *
 * @param {EngineHarness} harness
 * @return {Promise<void>}
 */
async function init(harness) {
    const engine = harness.engine;

    // engine.entityManager.addSystem(new RenderSystem(engine.graphics));
    engine.entityManager.addSystem(new ShadedGeometrySystem(engine));

    makeEngineOptionsModel(engine.options, engine);

    await makeConfig(engine).apply(engine);

    enableEditor(engine);

    await harness.initialize();

    main(engine);
}

function catmullRom(points) {
    const cv = new CanvasView();

    cv.size.set(500, 500);

    cv.link();

    cv.css({
        position: "absolute",
        left: 0,
        right: 0,
        zIndex: 99999
    });

    const sample = [];

    const ctx = cv.context2d;
    ctx.strokeStyle = "rgba(255,255,255,1)";
    ctx.lineWidth = 2;
    ctx.beginPath();

    const draw_scale = 50;

    const result = [];
    const sampleCount = 50;

    computeCatmullRomSpline(result, points, points.length / 3, 3, sampleCount, 0.5);

    for (let i = 0; i < sampleCount; i++) {
        const x = result[i * 3];
        const y = result[i * 3 + 1];
        const z = result[i * 3 + 2];

        ctx.lineTo(x * draw_scale, z * draw_scale);

    }
    ctx.stroke();

    document.body.appendChild(cv.el);
}

function sample_style_entity() {
    const style = new EntityPathStyle();
    style.spacing = 0.6;
    style.marker_start = EntityPathMarkerDefinition.from({
        blueprint: EntityBlueprint.from([
            Transform.fromJSON({}),
            Mesh.fromJSON({
                url: "data/models/arrow-symbols/arrow4/model-green.gltf",
                castShadow: true,
                receiveShadow: true
            })
        ]),
        transform: Transform.fromJSON({
            scale: {
                x: 0.3,
                y: 0.3,
                z: 0.3
            },
            rotation: {
                x: 0.5,
                y: -0.5,
                z: 0.5,
                w: 0.5
            },
            position: {
                x: 0,
                y: 1,
                z: 0
            }
        })
    });
    style.marker_end = EntityPathMarkerDefinition.from({
        blueprint: EntityBlueprint.from([
            Transform.fromJSON({}),
            Mesh.fromJSON({
                url: "data/models/arrow-symbols/arrow4/model-red.gltf",
                castShadow: true,
                receiveShadow: true
            })
        ]),
        transform: Transform.fromJSON({
            scale: {
                x: 0.3,
                y: 0.3,
                z: 0.3
            },
            rotation: {
                x: 0.5,
                y: -0.5,
                z: 0.5,
                w: 0.5
            },
            position: {
                x: 0,
                y: 1,
                z: 0
            }
        })
    });
    style.marker_main = EntityPathMarkerDefinition.from({
        blueprint: EntityBlueprint.from([
            Transform.fromJSON({}),
            Mesh.fromJSON({
                url: "data/models/Barel-Roll Droid/handpainted.gltf",
                castShadow: true,
                receiveShadow: true
            }),
            Animation.fromJSON({
                clips: [
                    {
                        name: "fly",
                        repeatCount: Infinity
                    }
                ]
            }),
            Highlight.fromOne(1, 0, 0, 1)
        ]),
        transform: Transform.fromJSON({
            scale: {
                x: 0.2,
                y: 0.2,
                z: 0.2
            },
            position: {
                x: 0,
                y: 1,
                z: 0
            }
        })
    });

    return style;
}

function sample_style_tube() {
    const style = new TubePathStyle();

    style.color.setRGB(1, 1, 1);
    style.material_type = TubeMaterialType.Matcap;
    style.material = new MatcapMaterialDefinition();
    // style.material.texture = 'data/textures/matcap/matcap-porcelain-white.jpg';
    style.material.texture = "data/textures/matcap/nidorx/3E2335_D36A1B_8E4A2E_2842A5.jpg";

    style.width = 0.1;
    style.resolution = 50;
    style.cap_type = CapType.None;

    style.path_mask = [
        0, 1,
    ];

    return style;
}


function main(engine) {
    EngineHarness.buildBasics({ engine });

    const _p = new Path();
    _p.interpolation = InterpolationType.CatmullRom;
    // _p.interpolation = InterpolationType.Linear;

    // _p.setPositionsFromVectorArray(Path.smoothPath([
    //     new Vector3(3, 1, 1),
    //     new Vector3(3, 1, 3),
    //     new Vector3(7, 1, 3),
    //     new Vector3(7, 1, 7),
    //     new Vector3(3, 1, 7),
    //     new Vector3(3, 1, 8),
    // ], 50));

    _p.setPositionsFromVectorArray([
        new Vector3(3, 1, 1),
        new Vector3(3, 1, 3),
        new Vector3(7, 1, 3),
        new Vector3(7, 1, 7),
        new Vector3(3, 1, 7),
        new Vector3(3, 1, 8),
    ]);


    const trailPathStyle = new RibbonPathStyle();
    trailPathStyle.material.diffuse = "data/textures/trail/Circle_04.png";
    trailPathStyle.resolution = 10;
    trailPathStyle.thickness = 0.1;

    const pathDisplay = new PathDisplay();
    const tube_style = sample_style_tube();
    pathDisplay.specs.push(
        PathDisplaySpec.from(
            PathDisplayType.Tube,
            tube_style
        )
    );


    const path_entity = new Entity();

    const ecd = engine.entityManager.dataset;
    path_entity
        .add(_p)
        .add(pathDisplay)
        .build(ecd);

    let time = 0;
    new Entity()
        .add(BehaviorComponent.loop((delta) => {
            time += delta * 0.01;

            const segment_count = 23;

            tube_style.path_mask = [];
            for (let i = 0; i < segment_count; i++) {
                const offset_start = (i / (segment_count - 1) + time) % 1;
                const offset_end = offset_start + 1 / (segment_count * 1.5);

                tube_style.path_mask.push(
                    clamp01(offset_start), clamp01(offset_end)
                );
            }

            path_entity.sendEvent(PathEvents.Changed);
        }))
        .build(ecd)
}

init(engineHarness);
