import {BaseSopOperation} from "./_Base";
import {BufferGeometry as BufferGeometry2} from "three/src/core/BufferGeometry";
import {BufferAttribute as BufferAttribute2} from "three/src/core/BufferAttribute";
import {ObjectType} from "../../../core/geometry/Constant";
import {InputCloneMode as InputCloneMode2} from "../../../engine/poly/InputCloneMode";
import {CoreIterator} from "../../Iterator";
import {CoreMath} from "../../math/_Module";
import {CoreType} from "../../Type";
import {ArrayUtils as ArrayUtils2} from "../../ArrayUtils";
export class ScatterSopOperation extends BaseSopOperation {
  static type() {
    return "scatter";
  }
  async cook(input_contents, params) {
    const core_group = input_contents[0];
    let faces = core_group.faces();
    const areas_thresholds = [];
    let area_sum = 0;
    const area_by_face_index = new Map();
    for (let face of faces) {
      const area = face.area();
      area_by_face_index.set(face.index(), area);
    }
    const sorted_faces = ArrayUtils2.sortBy(faces, (f) => {
      return area_by_face_index.get(f.index()) || -1;
    });
    let i = 0;
    for (let face of sorted_faces) {
      area_sum += area_by_face_index.get(face.index());
      areas_thresholds[i] = area_sum;
      i++;
    }
    const positions = [];
    let attrib_names = [];
    if (params.transferAttributes) {
      attrib_names = core_group.attribNamesMatchingMask(params.attributesToTransfer);
    }
    const attrib_values_by_name = new Map();
    const attrib_sizes_by_name = new Map();
    for (let attrib_name of attrib_names) {
      attrib_values_by_name.set(attrib_name, []);
      attrib_sizes_by_name.set(attrib_name, core_group.attribSize(attrib_name));
    }
    const iterator = new CoreIterator();
    await iterator.start_with_count(params.pointsCount, (point_index) => {
      const rand = CoreMath.rand_float(params.seed + point_index) * area_sum;
      for (let face_index = 0; face_index < areas_thresholds.length; face_index++) {
        const areas_threshold = areas_thresholds[face_index];
        if (rand <= areas_threshold) {
          const face = sorted_faces[face_index];
          const position = face.random_position(rand);
          position.toArray(positions, positions.length);
          for (let attrib_name of attrib_names) {
            const attrib_value = face.attrib_value_at_position(attrib_name, position);
            if (attrib_value) {
              if (CoreType.isNumber(attrib_value)) {
                attrib_values_by_name.get(attrib_name).push(attrib_value);
              } else {
                attrib_value.toArray(attrib_values_by_name.get(attrib_name), attrib_values_by_name.get(attrib_name).length);
              }
            }
          }
          break;
        }
      }
    });
    const geometry = new BufferGeometry2();
    geometry.setAttribute("position", new BufferAttribute2(new Float32Array(positions), 3));
    for (let attrib_name of attrib_names) {
      geometry.setAttribute(attrib_name, new BufferAttribute2(new Float32Array(attrib_values_by_name.get(attrib_name)), attrib_sizes_by_name.get(attrib_name)));
    }
    if (params.addIdAttribute || params.addIdnAttribute) {
      const pointsCount = params.pointsCount;
      const ids = ArrayUtils2.range(pointsCount);
      if (params.addIdAttribute) {
        geometry.setAttribute("id", new BufferAttribute2(new Float32Array(ids), 1));
      }
      const idns = ids.map((id) => id / (pointsCount - 1));
      if (params.addIdnAttribute) {
        geometry.setAttribute("idn", new BufferAttribute2(new Float32Array(idns), 1));
      }
    }
    const object = this.create_object(geometry, ObjectType.POINTS);
    return this.create_core_group_from_objects([object]);
  }
}
ScatterSopOperation.DEFAULT_PARAMS = {
  pointsCount: 100,
  seed: 0,
  transferAttributes: true,
  attributesToTransfer: "normal",
  addIdAttribute: true,
  addIdnAttribute: true
};
ScatterSopOperation.INPUT_CLONED_STATE = InputCloneMode2.FROM_NODE;
