import { array_copy } from "../../../../core/collection/array/array_copy.js";
import { Ray3 } from "../../../../core/geom/3d/ray/Ray3.js";
import { sh3_basis_at } from "../../../../core/geom/3d/sphere/harmonics/sh3_basis_at.js";
import {
    sh3_dering_optimize_positive
} from "../../../../core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.js";
import { sh3_vector_accumulate } from "../../../../core/geom/3d/sphere/harmonics/sh3_vector_accumulate.js";
import { randomPointOnSphere } from "../../../../core/geom/random/randomPointOnSphere.js";
import { vector_scale_array } from "../../../../core/geom/vec/vector_scale_array.js";
import { seededRandom } from "../../../../core/math/random/seededRandom.js";
import { PathTracedScene } from "../path_tracer/PathTracedScene.js";
import { PathTracer } from "../path_tracer/PathTracer.js";
import { populate_path_traced_scene_from_ecd } from "../path_tracer/populate_path_traced_scene_from_ecd.js";
import { bake_octahedral_depth_map } from "./depth/octahedral/bake_octahedral_depth_map.js";
import { ProbeRenderer } from "./ProbeRenderer.js";

const __shared_buffer = new ArrayBuffer((27 + 9) * 8);
const sh_basis = new Float64Array(__shared_buffer, 0, 9);
const coefficients = new Float64Array(__shared_buffer, 9 * 8, 27);

const ray = new Ray3();
const sampled_irradiance = new Float32Array(3);

export class PathTracerProbeRenderer extends ProbeRenderer {
    tracer = new PathTracer();
    max_bounce_count = 5;
    // sample_count = 256;
    sample_count = 4096;
    // sample_count = 40960;
    random = seededRandom(0);

    scene = new PathTracedScene()

    /**
     *
     * @param {number} resolution
     * @param {number} max_depth
     * @param {number[]} position
     * @param {number} position_offset
     * @param {number[]} output
     * @param {number} output_offset
     */
    bake_depth_octahedral(
        resolution, max_depth,
        position, position_offset,
        output, output_offset
    ) {
        bake_octahedral_depth_map(
            output, output_offset,
            this.scene,
            position, position_offset,
            resolution, max_depth,
            16
        );
    }

    bake(position, position_offset, output, output_offset) {


        const tracer = this.tracer;


        array_copy(position, position_offset, ray, 0, 3);


        coefficients.fill(0);

        const max_bounce = this.max_bounce_count;

        for (let i = 0; i < this.sample_count; i++) {

            randomPointOnSphere(this.random, ray, 3);
            ray.tMax = Infinity;
            ray.tMin = 0;

            tracer.path_trace(sampled_irradiance, ray, 1, max_bounce, this.random, this.scene);

            sh3_basis_at(ray[3], ray[4], ray[5], sh_basis);

            // accumulate
            sh3_vector_accumulate(
                coefficients, 0,
                sh_basis, 0,
                sampled_irradiance, 0, 3
            );
        }

        // Area of a unit sphere is (4*PI)
        const normalization_weight = (4 * Math.PI) / this.sample_count;

        vector_scale_array(coefficients, 0, coefficients, 0, 27, normalization_weight);

        sh3_dering_optimize_positive(coefficients, 0, coefficients, 0, 3);

        array_copy(coefficients, 0, output, output_offset, 27);
    }

    bake_end() {
    }

    bake_start() {
    }

    build_scene(ecd) {

        const scene = this.scene;
        populate_path_traced_scene_from_ecd(ecd, scene);

    }
}