import {Vector3 as Vector32} from "three/src/math/Vector3";
import {BufferAttribute as BufferAttribute2} from "three/src/core/BufferAttribute";
import {TypedSopNode} from "./_Base";
import {InputCloneMode as InputCloneMode2} from "../../poly/InputCloneMode";
import {NodeParamsConfig, ParamConfig} from "../utils/params/ParamsConfig";
import {ArrayUtils as ArrayUtils2} from "../../../core/ArrayUtils";
class FaceSopParamsConfig extends NodeParamsConfig {
  constructor() {
    super(...arguments);
    this.makeFacesUnique = ParamConfig.BOOLEAN(0);
    this.addFaceCenterAttribute = ParamConfig.BOOLEAN(0, {
      visibleIf: {makeFacesUnique: 1}
    });
    this.addFaceId = ParamConfig.BOOLEAN(0, {
      visibleIf: {makeFacesUnique: 1}
    });
    this.transform = ParamConfig.BOOLEAN(0, {
      visibleIf: {makeFacesUnique: 1}
    });
    this.scale = ParamConfig.FLOAT(1, {
      visibleIf: {makeFacesUnique: 1, transform: 1}
    });
  }
}
const ParamsConfig2 = new FaceSopParamsConfig();
export class FaceSopNode extends TypedSopNode {
  constructor() {
    super(...arguments);
    this.params_config = ParamsConfig2;
  }
  static type() {
    return "face";
  }
  initializeNode() {
    this.io.inputs.setCount(1);
    this.io.inputs.initInputsClonedState(InputCloneMode2.FROM_NODE);
  }
  cook(input_contents) {
    const core_group = input_contents[0];
    if (this.pv.makeFacesUnique) {
      this._makeFacesUnique(core_group);
      if (this.pv.addFaceCenterAttribute) {
        this._addFaceCenterAttribute(core_group);
      }
      if (this.pv.addFaceId) {
        this._addFaceId(core_group);
      }
      if (this.pv.transform) {
        this._transform_faces(core_group);
      }
    }
    this.setCoreGroup(core_group);
  }
  _makeFacesUnique(core_group) {
    for (let object of core_group.objects()) {
      if (object.isMesh) {
        const geometry = object.geometry;
        const faces = ArrayUtils2.chunk(geometry.index?.array || [], 3);
        const points_count = faces.length * 3;
        for (let attrib_name of Object.keys(geometry.attributes)) {
          const attrib = geometry.attributes[attrib_name];
          const attrib_size = attrib.itemSize;
          const new_values = new Float32Array(points_count * attrib_size);
          let new_value_index = 0;
          faces.forEach((face) => {
            face.forEach((index) => {
              for (let i = 0; i < attrib_size; i++) {
                const current_value = attrib.array[index * attrib_size + i];
                new_values[new_value_index] = current_value;
                new_value_index += 1;
              }
            });
          });
          geometry.setAttribute(attrib_name, new BufferAttribute2(new_values, attrib_size));
        }
        const new_indices = ArrayUtils2.range(points_count);
        geometry.setIndex(new_indices);
      }
    }
  }
  _addFaceCenterAttribute(core_group) {
    const attrib_name = "face_center";
    const face_center = new Vector32();
    let faces, face, points, point;
    core_group.coreObjects().forEach((core_object) => {
      const object = core_object.object();
      const core_geometry = core_object.coreGeometry();
      if (object.isMesh && core_geometry) {
        faces = core_geometry.faces();
        if (!core_geometry.hasAttrib(attrib_name)) {
          core_geometry.addNumericAttrib(attrib_name, 3, -1);
        }
        for (let fi = 0; fi < faces.length; fi++) {
          face = faces[fi];
          face.center(face_center);
          points = face.points();
          for (let pi = 0; pi < points.length; pi++) {
            point = points[pi];
            point.setAttribValue(attrib_name, face_center);
          }
        }
      }
    });
  }
  _addFaceId(core_group) {
    const attrib_name = "face_id";
    core_group.coreObjects().forEach((core_object) => {
      const object = core_object.object();
      const core_geometry = core_object.coreGeometry();
      if (object.isMesh && core_geometry) {
        const faces = core_geometry.faces();
        if (!core_geometry.hasAttrib(attrib_name)) {
          core_geometry.addNumericAttrib(attrib_name, 1, -1);
        }
        for (let i = 0; i < faces.length; i++) {
          const face = faces[i];
          const points = face.points();
          for (let j = 0; j < points.length; j++) {
            const point = points[j];
            point.setAttribValue(attrib_name, i);
          }
        }
      }
    });
  }
  _transform_faces(core_group) {
    const attrib_name = "position";
    const face_center = new Vector32();
    const new_position = new Vector32();
    const scale = this.pv.scale;
    let faces, face, points, point;
    core_group.coreObjects().forEach((core_object) => {
      const object = core_object.object();
      const core_geometry = core_object.coreGeometry();
      if (object.isMesh && core_geometry) {
        faces = core_geometry.faces();
        if (!core_geometry.hasAttrib(attrib_name)) {
          core_geometry.addNumericAttrib(attrib_name, 3, -1);
        }
        for (let fi = 0; fi < faces.length; fi++) {
          face = faces[fi];
          face.center(face_center);
          points = face.points();
          for (let pi = 0; pi < points.length; pi++) {
            point = points[pi];
            const position = point.position();
            new_position.x = position.x * scale + face_center.x * (1 - scale);
            new_position.y = position.y * scale + face_center.y * (1 - scale);
            new_position.z = position.z * scale + face_center.z * (1 - scale);
            point.setAttribValue(attrib_name, new_position);
          }
        }
      }
    });
  }
}
