import { assert } from "../../../../core/assert.js";
import TaskGroup from "../../../../core/process/task/TaskGroup.js";
import { actionTask } from "../../../../core/process/task/util/actionTask.js";
import { countTask } from "../../../../core/process/task/util/countTask.js";
import { promiseTask } from "../../../../core/process/task/util/promiseTask.js";
import { find_max_depth_radius_for_point } from "./find_max_depth_radius_for_point.js";
import { PathTracerProbeRenderer } from "./PathTracerProbeRenderer.js";

export class LightProbeVolumeBaker {

    _ren = new PathTracerProbeRenderer();


    /**
     * Bake light probes
     * @returns {TaskGroup}
     * @param {LightProbeVolume} lpv
     * @param {EntityComponentDataset} ecd
     */
    bake(lpv, ecd) {
        const renderer = this._ren;

        const tPrepare = promiseTask(new Promise(async (resolve, reject) => {
            renderer.build_scene(ecd);
            await renderer.bake_start(ecd);

            resolve();
        }), 'Prepare');


        const probe_count = lpv.count;

        const tBakeIrradiance = countTask(0, probe_count, (probe_index) => {
            this.bake_probe(renderer, lpv, probe_index);

        });
        tBakeIrradiance.estimatedDuration = probe_count;

        tBakeIrradiance.promise().then(() => {

            const duration = tBakeIrradiance.__executedCpuTime;

            console.log(`Baked ${probe_count} probes in ${duration}ms, ~${(duration / probe_count).toFixed(2)}ms per probe`);

        });

        const tFinish = actionTask(() => {
            renderer.bake_end();

            lpv.incrementVersion();
        });

        tFinish.addDependency(tBakeIrradiance);
        tBakeIrradiance.addDependency(tPrepare);

        return new TaskGroup([
            tPrepare, tBakeIrradiance, tFinish
        ], "LightProbeVolume Bake");
    }

    /**
     *
     * @param {PathTracerProbeRenderer} renderer
     * @param {LightProbeVolume} lpv
     * @param {number} probe_index
     */
    bake_probe(renderer, lpv, probe_index) {
        assert.isNonNegativeInteger(probe_index, 'probe_index');

        const position_offset = probe_index * 3;

        // Bake color
        renderer.bake(
            lpv.points, position_offset,
            lpv.harmonics, probe_index * 9 * 3
        );


        // Bake depth
        const max_distance = find_max_depth_radius_for_point(probe_index, lpv.mesh, lpv.points);

        // bias a little to ensure we capture everything
        const max_depth = max_distance * 1.01 + 1e-6;

        const depth_resolution = lpv.depth_map_resolution;

        const probe_element_count = depth_resolution * depth_resolution;

        renderer.bake_depth_octahedral(
            depth_resolution,
            max_depth,
            lpv.points, position_offset,
            lpv.depth, probe_index * probe_element_count * 2
        );
    }
}