import { EngineHarness } from "../../src/engine/EngineHarness.js";
import RenderSystem from "../../src/engine/ecs/systems/RenderSystem.js";
import { Camera } from "../../src/engine/graphics/ecs/camera/Camera.js";
import TopDownCameraController from "../../src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js";
import Vector3 from "../../src/core/geom/Vector3.js";
import { TopDownCameraLander } from "../../src/engine/graphics/ecs/camera/topdown/TopDownCameraLander.js";
import Terrain from "../../src/engine/ecs/terrain/ecs/Terrain.js";
import { TerrainFlags } from "../../src/engine/ecs/terrain/ecs/TerrainFlags.js";
import Water from "../../src/engine/graphics/ecs/water/Water.js";
import GridObstacle from "../../src/engine/grid/obstacle/GridObstacle.js";
import Entity from "../../src/engine/ecs/Entity.js";
import GridPosition from "../../src/engine/grid/position/GridPosition.js";
import { Transform } from "../../src/engine/ecs/transform/Transform.js";
import { GridData } from "../../src/generation/grid/GridData.js";
import { computeStringHash } from "../../src/core/primitives/strings/computeStringHash.js";
import { ThemeEngine } from "../../src/generation/theme/ThemeEngine.js";
import { AreaTheme } from "../../src/generation/theme/AreaTheme.js";
import TaskGroup from "../../src/core/process/task/TaskGroup.js";
import { generateTerrainPreview } from "../../../../generator/generator/generateTerrainPreview.js";
import { SampleTheme0 } from "./themes/SampleTheme0.js";
import { SampleGenerator0 } from "./SampleGenerator0.js";
import { configureMirGrid } from "./grid/configureMirGrid.js";
import { EngineConfiguration } from "../../src/engine/EngineConfiguration.js";
import TerrainSystem from "../../src/engine/ecs/terrain/ecs/TerrainSystem.js";
import WaterSystem from "../../src/engine/graphics/ecs/water/WaterSystem.js";
import { MeshSystem } from "../../src/engine/graphics/ecs/mesh/MeshSystem.js";
import TopDownCameraControllerSystem
    from "../../src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js";
import { TopDownCameraLanderSystem } from "../../src/engine/graphics/ecs/camera/topdown/TopDownCameraLanderSystem.js";
import LightSystem from "../../src/engine/graphics/ecs/light/LightSystem.js";
import { TaskLoadingScreen } from "../../src/view/task/TaskLoadingScreen.js";
import { actionTask } from "../../src/core/process/task/util/actionTask.js";

function makeEngineConfig(engine) {
    const config = new EngineConfiguration();

    const graphics = engine.graphics;

    config.addSystem(new RenderSystem(graphics));
    config.addSystem(new TerrainSystem(graphics, engine.assetManager));
    config.addSystem(new WaterSystem(graphics));
    config.addSystem(new MeshSystem(engine));
    config.addSystem(new TopDownCameraControllerSystem());
    config.addSystem(new TopDownCameraLanderSystem());
    config.addSystem(new LightSystem(engine))

    return config;
}


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

    await makeEngineConfig(engine).apply(engine);

    await harness.initialize();

    main(engine);
}

function prepare_gen_task({
                              SIZE_X = 64,
                              SIZE_Y = 64,
                              ecd
                          }) {

    const camera = EngineHarness.buildCamera({
        engine,
        target: new Vector3(15, 0, 16),
        // pitch: -PI_HALF,
        pitch: 1.1,
        yaw: 3.14,
        distance: 13,
        ecd,
        distanceMin: 5,
        distanceMax: 30
    });

    camera.add(new TopDownCameraLander());

    // build lights
    EngineHarness.buildLights({ engine })


    // build terrain
    const terrain = new Terrain();

    terrain.resolution = 4;

    terrain.size.set(SIZE_X, SIZE_Y);
    terrain.samplerHeight.resize(SIZE_X, SIZE_Y);
    terrain.splat.resize(SIZE_X * 4, SIZE_Y * 4, 0);

    terrain.layers.resolution.set(512, 512);

    terrain.clearFlag(TerrainFlags.Built);
    terrain.build(engine.assetManager);

    const water = new Water();

    water.color.setRGB(0.2, 0.0, 0.5);
    water.shoreColor.setRGB(0.3, 0.3, 1);
    water.shoreDepthTransition.set(0.16, 0.5);
    water.waveFrequency.set(1.3);
    water.waveAmplitude.set(0.15);
    water.waveSpeed.set(1.2);
    water.scattering.set(1.2);
    water.level.set(-0.3);

    const obstacle = new GridObstacle();
    obstacle.resize(terrain.size.x, terrain.size.y);

    const terrainBuilder = new Entity();

    terrainBuilder
        .add(terrain)
        .add(water)
        .add(obstacle)
        .add(new GridPosition())
        .add(new Transform())
        .build(ecd);


    const gridData = new GridData();

    configureMirGrid(gridData);

    gridData.resize(terrain.size.x, terrain.size.y);
    gridData.computeScale(1);

    const generator = SampleGenerator0;

    const tGenerationTask = generator.generate(ecd, gridData, computeStringHash('alexx855'));


    const themeEngine = new ThemeEngine();
    themeEngine.assetManager = engine.assetManager;

    const theme0 = new AreaTheme();

    const width = gridData.width;
    const height = gridData.height;

    theme0.mask.resize(width, height);
    theme0.mask.mask.fill(0, 0, width, height, [1]);
    theme0.theme = SampleTheme0;

    themeEngine.add(theme0);


    themeEngine.random.setCurrentSeed(1);
    const tTheme = themeEngine.apply(gridData, ecd);

    tTheme.addDependency(tGenerationTask);


    const tBuildLevel = new TaskGroup([tGenerationTask, tTheme]);


    const tAddGameplayElements = actionTask(() => {
        // prepare gameplay-specific things on top of the generated level

        //build minimap
        const terrainPreview = generateTerrainPreview(terrainBuilder.id, ecd);
        terrain.preview.copy(terrainPreview);
    });

    tAddGameplayElements.addDependency(tBuildLevel);

    return new TaskGroup([tBuildLevel, tAddGameplayElements]);
}

/**
 *
 * @param {Engine} engine
 */
function main(engine) {
    const ecd = engine.entityManager.dataset;

    const tBuildLevel = prepare_gen_task({ ecd });

    TaskLoadingScreen.load(engine, tBuildLevel);

    engine.executor.runGroup(tBuildLevel);

    tBuildLevel.promise().then(() => {
        console.log('Generation took ', tBuildLevel.getExecutedCpuTime(), 'ms');

        const camera = ecd.getAnyComponent(Camera);

        EngineHarness.buildOrbitalCameraController({
            cameraEntity: camera.entity,
            engine
        });

        // tweak camera for prototyping
        const ctrl = ecd.getComponent(camera.entity, TopDownCameraController);
        if (ctrl !== undefined) {
            ctrl.distanceMax = 200;
        }

    });

    // Resume audio context
    engine.devices.pointer.on.down.addOne(engine.sound.resumeContext, engine.sound);
    engine.devices.pointer.on.wheel.addOne(engine.sound.resumeContext, engine.sound);

}

const engineHarness = new EngineHarness();

init(engineHarness);
