import {TypedSopNode} from "./_Base";
import {CoreInterpolate} from "../../../core/math/Interpolate";
import {CoreOctree} from "../../../core/math/octree/Octree";
import {CoreIterator} from "../../../core/Iterator";
import {NodeParamsConfig, ParamConfig} from "../utils/params/ParamsConfig";
import {InputCloneMode as InputCloneMode2} from "../../poly/InputCloneMode";
class AttribTransferSopParamsConfig extends NodeParamsConfig {
  constructor() {
    super(...arguments);
    this.srcGroup = ParamConfig.STRING();
    this.destGroup = ParamConfig.STRING();
    this.name = ParamConfig.STRING();
    this.maxSamplesCount = ParamConfig.INTEGER(1, {
      range: [1, 10],
      rangeLocked: [true, false]
    });
    this.distanceThreshold = ParamConfig.FLOAT(1);
    this.blendWidth = ParamConfig.FLOAT(0);
  }
}
const ParamsConfig2 = new AttribTransferSopParamsConfig();
export class AttribTransferSopNode extends TypedSopNode {
  constructor() {
    super(...arguments);
    this.params_config = ParamsConfig2;
  }
  static type() {
    return "attribTransfer";
  }
  static displayedInputNames() {
    return ["geometry to transfer attributes to", "geometry to transfer attributes from"];
  }
  initializeNode() {
    this.io.inputs.setCount(2);
    this.io.inputs.initInputsClonedState([InputCloneMode2.FROM_NODE, InputCloneMode2.NEVER]);
  }
  async cook(input_contents) {
    this._core_group_dest = input_contents[0];
    const dest_points = this._core_group_dest.pointsFromGroup(this.pv.destGroup);
    this._core_group_src = input_contents[1];
    this._attrib_names = this._core_group_src.attribNamesMatchingMask(this.pv.name);
    this._error_if_attribute_not_found_on_second_input();
    this._build_octree_if_required(this._core_group_src);
    this._add_attribute_if_required();
    await this._transfer_attributes(dest_points);
    this.setCoreGroup(this._core_group_dest);
  }
  _error_if_attribute_not_found_on_second_input() {
    for (let attrib_name of this._attrib_names) {
      if (!this._core_group_src.hasAttrib(attrib_name)) {
        this.states.error.set(`attribute '${attrib_name}' not found on second input`);
      }
    }
  }
  _build_octree_if_required(core_group) {
    const second_input_changed = this._octree_timestamp == null || this._octree_timestamp !== core_group.timestamp();
    const srcGroup_changed = this._prev_param_srcGroup !== this.pv.srcGroup;
    if (srcGroup_changed || second_input_changed) {
      this._octree_timestamp = core_group.timestamp();
      this._prev_param_srcGroup = this.pv.srcGroup;
      const points_src = this._core_group_src.pointsFromGroup(this.pv.srcGroup);
      this._octree = new CoreOctree(this._core_group_src.boundingBox());
      this._octree.set_points(points_src);
    }
  }
  _add_attribute_if_required() {
    this._attrib_names.forEach((attrib_name) => {
      if (!this._core_group_dest.hasAttrib(attrib_name)) {
        const attrib_size = this._core_group_src.attribSize(attrib_name);
        this._core_group_dest.addNumericVertexAttrib(attrib_name, attrib_size, 0);
      }
    });
  }
  async _transfer_attributes(dest_points) {
    const iterator = new CoreIterator();
    await iterator.start_with_array(dest_points, this._transfer_attributes_for_point.bind(this));
  }
  _transfer_attributes_for_point(dest_point) {
    const total_dist = this.pv.distanceThreshold + this.pv.blendWidth;
    const nearest_points = this._octree?.find_points(dest_point.position(), total_dist, this.pv.maxSamplesCount) || [];
    for (let attrib_name of this._attrib_names) {
      this._interpolate_points(dest_point, nearest_points, attrib_name);
    }
  }
  _interpolate_points(point_dest, src_points, attrib_name) {
    let new_value;
    new_value = CoreInterpolate.perform(point_dest, src_points, attrib_name, this.pv.distanceThreshold, this.pv.blendWidth);
    if (new_value != null) {
      point_dest.setAttribValue(attrib_name, new_value);
    }
  }
}
