import { MeshStandardMaterial, OctahedronBufferGeometry } from "three";
import '../../../../../../../../css/game.scss';
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 { 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 InputControllerSystem from "../../../input/ecs/systems/InputControllerSystem.js";
import { InputSystem } from "../../../input/ecs/systems/InputSystem.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 PathFollowingSystem from "../../../navigation/ecs/path_following/PathFollowingSystem.js";
import {
    AmbientOcclusionPostProcessEffect
} from "../../render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js";
import { GizmoRenderingPlugin } from "../../render/gizmo/GizmoRenderingPlugin.js";
import AnimationControllerSystem from "../animation/AnimationControllerSystem.js";
import { AnimationGraphSystem } from "../animation/animator/AnimationGraphSystem.js";
import { Camera } from "../camera/Camera.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 { ShadedGeometryHighlightSystem } from "../highlight/system/ShadedGeometryHighlightSystem.js";
import LightSystem from "../light/LightSystem.js";
import { ShadedGeometry } from "../mesh-v2/ShadedGeometry.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 { PathDisplayHighlightSystem } from "./highlight/PathDisplayHighlightSystem.js";
import { PathDisplay } from "./PathDisplay.js";
import { PathDisplaySpec } from "./PathDisplaySpec.js";
import { PathDisplaySystem } from "./PathDisplaySystem.js";
import { PathDisplayType } from "./PathDisplayType.js";
import { BasicMaterialDefinition } from "./tube/BasicMaterialDefinition.js";
import { CapType } from "./tube/CapType.js";
import { MatcapMaterialDefinition } from "./tube/MatcapMaterialDefinition.js";
import { PathNormalType } from "./tube/PathNormalType.js";
import { StandardMaterialDefinition } from "./tube/StandardMaterialDefinition.js";
import { TubeMaterialType } from "./tube/TubeMaterialType.js";
import { TubePathStyle } from "./tube/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 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 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 ShadedGeometryHighlightSystem(engine),
        new PathDisplaySystem(engine),
        new PathDisplayHighlightSystem()
    );

    // Plugins

    config.addPlugin(AmbientOcclusionPostProcessEffect);
    config.addPlugin(GizmoRenderingPlugin);

    // 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.path_normal_type = PathNormalType.FixedStart;

    style.color.setRGB(1, 1, 1);

    function make_matcap(style) {

        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";

    }

    function make_standard(style) {

        style.material_type = TubeMaterialType.Standard;
        style.material = new StandardMaterialDefinition();
        // style.material.texture = 'data/textures/matcap/matcap-porcelain-white.jpg';
        // style.material.texture = "data/textures/matcap/nidorx/3E2335_D36A1B_8E4A2E_2842A5.jpg";

        style.color.setHSV(0, 1, 0.9);
    }

    function make_basic(style) {
        style.material_type = TubeMaterialType.Basic;
        style.material = new BasicMaterialDefinition();

        style.color.setHSV(0, 1, 0.9);
    }

    make_standard(style);

    style.width = 0.5;
    style.cast_shadow = true;

    style.cap_type = CapType.Round;
    // style.cap_type = CapType.None;

    // style.path_mask = [
    //     0.1, 0.12,
    //     0.3,  1
    // ];

    //
    // style.shape = [
    //     0.1, -0.5,
    //     0.1, 0.5,
    //     -0.1, 0.5,
    //     -0.1, -0.5,
    // ];

    style.shape = [
        -0.5, -0.1,
        0.5, -0.1,
        0.5, 0.1,
        -0.5, 0.1,
    ];
    //
    // style.shape = [
    //     -0.5, -0.1,
    //     -0.45, -0.15,
    //     0.45, -0.15,
    //     0.5, -0.1,
    //     0.5, 0.1,
    //     0.45, 0.15,
    //     -0.45, 0.15,
    //     -0.5, 0.1
    // ];

    style.path_mask = [
        0, 1
    ];

    return style;
}

/**
 *
 * @param {number[]} points
 * @param {InterpolationType} [interp]
 * @param {Vector3} [offset]
 * @returns {Entity}
 */
function makePath({
                      points,
                      interp = InterpolationType.Linear,
                      offset = Vector3.zero
                  }) {
    const _p = new Path();

    _p.interpolation = interp;

    const pk = points.length / 3;

    _p.setPointCount(pk);
    for (let i = 0; i < pk; i++) {
        const i3 = i * 3;
        _p.setPosition(i,
            points[i3] + offset.x,
            points[i3 + 1] + offset.y,
            points[i3 + 2] + offset.z
        );
    }

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

    return new Entity()
        .add(_p)
        .add(pathDisplay)
    // .add(Highlight.fromOne(0, 1, 1, 1));

}

/**
 *
 * @param {Path} path
 * @param {EntityComponentDataset} ecd
 */
function drawControlPoints(path, ecd) {

    const point_count = path.getPointCount();

    const geo = new OctahedronBufferGeometry(1, 3);
    const mat = new MeshStandardMaterial({});

    for (let i = 0; i < point_count; i++) {
        const t = new Transform();

        t.scale.setScalar(0.4);

        path.getPosition(i, t.position);

        new Entity()
            .add(t)
            .add(ShadedGeometry.from(geo, mat))
            .build(ecd)
    }


}

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

    const ecd = engine.entityManager.dataset;

    const cam = ecd.getAnyComponent(Camera);
    // cam.component.projectionType.set(ProjectionType.Orthographic);

    const points = [
        84,
        20,
        100,
        68.36000061035156,
        5.840000152587891,
        31.1200008392334,
        69.01000213623047,
        8,
        24.020000457763672,
        88.19000244140625,
        35,
        22.729999542236328,
        114.80999755859375,
        35,
        10.970000267028809,
        71,
        35,
        -69.01000213623047,
        -31.809999465942383,
        25,
        -27.850000381469727,
        -11.130000114440918,
        25,
        30.770000457763672,
        20.75,
        25,
        6.539999961853027,
        13.0600004196167,
        25,
        -6.28000020980835,
        7.75,
        17.760000228881836,
        14.109999656677246,
        4.260000228881836,
        10.529999732971191,
        8.869999885559082,
        -5.96999979019165,
        8.079999923706055,
        -2.180000066757202,
        -5.369999885559082,
        6.059999942779541,
        -12.010000228881836,
        -9.359999656677246,
        6.059999942779541,
        -12.369999885559082,
        -10.579999923706055,
        4.789999961853027,
        -15.300000190734863,
        -9.130000114440918,
        4.639999866485596,
        -16.350000381469727,
        -9.119999885559082,
        4.639999866485596,
        -16.360000610351562,
        -9.109999656677246,
        4.639999866485596,
        -16.360000610351562,
        -9.100000381469727,
        4.630000114440918,
        -16.360000610351562,
        -4.429999828338623,
        5,
        -9.720000267028809,
        -7.309999942779541,
        5,
        6.059999942779541,
        -4.170000076293945,
        0.1599999964237213,
        10.720000267028809,
        -1.4600000381469727,
        0.1599999964237213,
        12.109999656677246,
        -0.4399999976158142,
        0.1599999964237213,
        19.25,
        0.5099999904632568,
        0.1599999964237213,
        21.65999984741211,
        2,
        0.1599999964237213,
        21.56999969482422,
        7.880000114440918,
        2.859999895095825,
        21.610000610351562,
        12.039999961853027,
        2.859999895095825,
        21.860000610351562,
        14.369999885559082,
        3.2100000381469727,
        21.760000228881836,
        14.1899995803833,
        4.440000057220459,
        25.639999389648438,
        14.130000114440918,
        5.53000020980835,
        29.149999618530273,
        15.15999984741211,
        7.159999847412109,
        29.899999618530273,
        19.540000915527344,
        700,
        51.060001373291016
    ];

    makePath({
        points: points,
        interp: InterpolationType.CatmullRom
    }).build(ecd);

    drawControlPoints(Path.fromJSON({
        points
    }), ecd);

    makePath({
        points: [
            17.600000381469727, 3.2100000381469727, -16.34000015258789,
            17.84000015258789, 4.559999942779541, -10.029999732971191,
            16.979999542236328, 3.130000114440918, -8.25,
            16.719999313354492, 3.130000114440918, -6.889999866485596
        ],
        interp: InterpolationType.Linear
    }).build(ecd);
}

init(engineHarness);
