import { vec3 } from "gl-matrix";
import { compute_binary_data_type_from_typed_array } from "../../../../../core/collection/array/typed/compute_binary_data_type_from_typed_array.js";
import { v3_array_matrix4_rotate } from "../../../../../core/geom/vec3/v3_array_matrix4_rotate.js";
import { v3_distance_sqr } from "../../../../../core/geom/vec3/v3_distance_sqr.js";
import { decode_attribute_value } from "../../../geometry/decode_attribute_value.js";
import { geometry_construct_triangle_normal } from "../../../geometry/geometry_construct_triangle_normal.js";
import { sample_triangle_attribute } from "../geometry/sample_triangle_attribute.js";

import { v3_orthonormal_matrix_from_normal } from "../sampling/v3_orthonormal_matrix_from_normal.js";

const texture_uv = [0, 0];
const color = new Float64Array(3);
const normal = new Float32Array(3);
const scratch_m3 = new Float32Array(9);

/**
 *
 * @param {number[]} color
 * @param {BufferAttribute} color_attribute
 * @param {number} index_0
 * @param {number} index_1
 * @param {number} index_2
 * @param {number} u
 * @param {number} v
 */
function sample_color_attribute(
    color,
    color_attribute,
    index_0, index_1, index_2,
    u, v
) {
    const color_array = color_attribute.array;
    sample_triangle_attribute(color, 0, index_0, index_1, index_2, color_array, color_attribute.itemSize, u, v);

    const type = compute_binary_data_type_from_typed_array(color_array);

    for (let i = 0; i < 3; i++) {
        color[i] = decode_attribute_value(
            color[i], type, color_attribute.normalized
        );
    }

}

/**
 *
 * @param {number[]} out
 * @param {PathTracedMesh} mesh
 * @param {number} primitive_id
 * @param {number} u
 * @param {number} v
 */
export function sample_material(
    out,
    mesh,
    primitive_id,
    u, v
) {

    const material = mesh.material;
    const geometry = mesh.geometry;
    const index_attribute = geometry.getIndex();
    const index_array = index_attribute.array;


    // sample surface normal
    const index_offset = primitive_id * 3;

    const index_0 = index_array[index_offset];
    const index_1 = index_array[index_offset + 1];
    const index_2 = index_array[index_offset + 2];

    const normal_attribute = geometry.getAttribute('normal');

    if (normal_attribute !== undefined) {
        // vertex normals are present, use those
        const normal_array = normal_attribute.array;

        sample_triangle_attribute(out, 3, index_0, index_1, index_2, normal_array, 3, u, v);

    } else {
        const position_attribute = geometry.getAttribute('position');

        // copy hit normal
        geometry_construct_triangle_normal(out, 3, index_0, index_1, index_2, position_attribute.array);
    }

    // apply transform
    v3_array_matrix4_rotate(out, 3, out, 3, mesh.transform);

    material.normal.sample(normal, texture_uv[0], texture_uv[1]);

    if (v3_distance_sqr(normal[0], normal[1], normal[2], 0.5, 0.5, 1) > 0.01) {
        //
        normal[0] = normal[0] * 2 - 1;
        normal[1] = normal[1] * 2 - 1;
        normal[2] = normal[2] * 2 - 1;

        vec3.normalize(normal, normal);

        // needs transform
        v3_orthonormal_matrix_from_normal(scratch_m3, 0, out[3], out[4], out[5]);

        out[3] = scratch_m3[0] * normal[0] + scratch_m3[3] * normal[1] + scratch_m3[6] * normal[2];
        out[4] = scratch_m3[1] * normal[0] + scratch_m3[4] * normal[1] + scratch_m3[7] * normal[2];
        out[5] = scratch_m3[2] * normal[0] + scratch_m3[5] * normal[1] + scratch_m3[8] * normal[2];
    }

    const uv_attribute = geometry.getAttribute('uv');

    if (uv_attribute !== undefined) {
        const uv_array = uv_attribute.array;
        sample_triangle_attribute(texture_uv, 0, index_0, index_1, index_2, uv_array, 2, u, v);
    } else {
        // default texture uv
        texture_uv[0] = 0;
        texture_uv[1] = 0;
    }

    const color_attribute = geometry.getAttribute('color');

    if (color_attribute !== undefined) {
        sample_color_attribute(color, color_attribute, index_0, index_1, index_2, u, v);

        vec3.multiply(out, out, color);
    }


    material.diffuse.sample(color, texture_uv[0], texture_uv[1]);

    vec3.multiply(out, out, color);

}