import {TypedSopNode} from "./_Base";
import {
  CoreTransform,
  ROTATION_ORDERS,
  RotationOrder,
  TransformTargetType,
  TRANSFORM_TARGET_TYPES
} from "../../../core/Transform";
import {InputCloneMode as InputCloneMode2} from "../../poly/InputCloneMode";
import {Vector3 as Vector32} from "three/src/math/Vector3";
const max_transform_count = 6;
const ROT_ORDER_DEFAULT = ROTATION_ORDERS.indexOf(RotationOrder.XYZ);
const ROT_ORDER_MENU_ENTRIES = {
  menu: {
    entries: ROTATION_ORDERS.map((order, v) => {
      return {name: order, value: v};
    })
  }
};
function visible_for_count(count) {
  const list = [];
  for (let i = count + 1; i <= max_transform_count; i++) {
    list.push({
      count: i
    });
  }
  return {visibleIf: list};
}
import {NodeParamsConfig, ParamConfig} from "../utils/params/ParamsConfig";
import {TypeAssert} from "../../poly/Assert";
import {CoreAttribute, Attribute as Attribute2} from "../../../core/geometry/Attribute";
class TransformMultiSopParamConfig extends NodeParamsConfig {
  constructor() {
    super(...arguments);
    this.applyOn = ParamConfig.INTEGER(TRANSFORM_TARGET_TYPES.indexOf(TransformTargetType.GEOMETRIES), {
      menu: {
        entries: TRANSFORM_TARGET_TYPES.map((target_type, i) => {
          return {name: target_type, value: i};
        })
      }
    });
    this.count = ParamConfig.INTEGER(2, {
      range: [0, max_transform_count],
      rangeLocked: [true, true]
    });
    this.sep0 = ParamConfig.SEPARATOR(null, {...visible_for_count(0)});
    this.rotationOrder0 = ParamConfig.INTEGER(ROT_ORDER_DEFAULT, {
      ...ROT_ORDER_MENU_ENTRIES,
      ...visible_for_count(0)
    });
    this.r0 = ParamConfig.VECTOR3([0, 0, 0], {...visible_for_count(0)});
    this.sep1 = ParamConfig.SEPARATOR(null, {...visible_for_count(1)});
    this.rotationOrder1 = ParamConfig.INTEGER(ROT_ORDER_DEFAULT, {
      ...ROT_ORDER_MENU_ENTRIES,
      ...visible_for_count(1)
    });
    this.r1 = ParamConfig.VECTOR3([0, 0, 0], {...visible_for_count(1)});
    this.sep2 = ParamConfig.SEPARATOR(null, {...visible_for_count(2)});
    this.rotationOrder2 = ParamConfig.INTEGER(ROT_ORDER_DEFAULT, {
      ...ROT_ORDER_MENU_ENTRIES,
      ...visible_for_count(2)
    });
    this.r2 = ParamConfig.VECTOR3([0, 0, 0], {...visible_for_count(2)});
    this.sep3 = ParamConfig.SEPARATOR(null, {...visible_for_count(3)});
    this.rotationOrder3 = ParamConfig.INTEGER(ROT_ORDER_DEFAULT, {
      ...ROT_ORDER_MENU_ENTRIES,
      ...visible_for_count(3)
    });
    this.r3 = ParamConfig.VECTOR3([0, 0, 0], {...visible_for_count(3)});
    this.sep4 = ParamConfig.SEPARATOR(null, {...visible_for_count(4)});
    this.rotationOrder4 = ParamConfig.INTEGER(ROT_ORDER_DEFAULT, {
      ...ROT_ORDER_MENU_ENTRIES,
      ...visible_for_count(4)
    });
    this.r4 = ParamConfig.VECTOR3([0, 0, 0], {...visible_for_count(4)});
    this.sep5 = ParamConfig.SEPARATOR(null, {...visible_for_count(5)});
    this.rotationOrder5 = ParamConfig.INTEGER(ROT_ORDER_DEFAULT, {
      ...ROT_ORDER_MENU_ENTRIES,
      ...visible_for_count(5)
    });
    this.r5 = ParamConfig.VECTOR3([0, 0, 0], {...visible_for_count(5)});
  }
}
const ParamsConfig2 = new TransformMultiSopParamConfig();
export class TransformMultiSopNode extends TypedSopNode {
  constructor() {
    super(...arguments);
    this.params_config = ParamsConfig2;
    this._core_transform = new CoreTransform();
    this._t = new Vector32(0, 0, 0);
    this._s = new Vector32(1, 1, 1);
    this._scale = 1;
  }
  static type() {
    return "transformMulti";
  }
  static displayedInputNames() {
    return ["objects to transform", "objects to copy initial transform from"];
  }
  initializeNode() {
    this.io.inputs.setCount(1, 2);
    this.io.inputs.initInputsClonedState([InputCloneMode2.FROM_NODE, InputCloneMode2.NEVER]);
    this.scene().dispatchController.onAddListener(() => {
      this.params.onParamsCreated("params_label", () => {
        this.params.label.init([this.p.applyOn], () => {
          return TRANSFORM_TARGET_TYPES[this.pv.applyOn];
        });
      });
    });
    this.params.onParamsCreated("cache param pairs", () => {
      this._rot_and_index_pairs = [
        [this.p.r0, this.p.rotationOrder0],
        [this.p.r1, this.p.rotationOrder1],
        [this.p.r2, this.p.rotationOrder2],
        [this.p.r3, this.p.rotationOrder3],
        [this.p.r4, this.p.rotationOrder4],
        [this.p.r5, this.p.rotationOrder5]
      ];
    });
  }
  cook(input_contents) {
    const objects = input_contents[0].objectsWithGeo();
    const src_object = input_contents[1] ? input_contents[1].objectsWithGeo()[0] : void 0;
    this._apply_transforms(objects, src_object);
    this.setObjects(objects);
  }
  _apply_transforms(objects, src_object) {
    const mode = TRANSFORM_TARGET_TYPES[this.pv.applyOn];
    switch (mode) {
      case TransformTargetType.GEOMETRIES: {
        return this._apply_matrix_to_geometries(objects, src_object);
      }
      case TransformTargetType.OBJECTS: {
        return this._apply_matrix_to_objects(objects, src_object);
      }
    }
    TypeAssert.unreachable(mode);
  }
  _apply_matrix_to_geometries(objects, src_object) {
    if (!this._rot_and_index_pairs) {
      return;
    }
    if (src_object) {
      const src_geometry = src_object.geometry;
      if (src_geometry) {
        const attributes_to_copy = [Attribute2.POSITION, Attribute2.NORMAL, Attribute2.TANGENT];
        for (let attrib_name of attributes_to_copy) {
          const src = src_geometry.attributes[attrib_name];
          for (let object of objects) {
            const geometry = object.geometry;
            const dest = geometry.attributes[attrib_name];
            if (src && dest) {
              CoreAttribute.copy(src, dest);
            }
          }
        }
      }
    }
    let pair;
    for (let i = 0; i < this.pv.count; i++) {
      pair = this._rot_and_index_pairs[i];
      const matrix = this._matrix(pair[0].value, pair[1].value);
      for (let object of objects) {
        object.geometry.applyMatrix4(matrix);
      }
    }
  }
  _apply_matrix_to_objects(objects, src_object) {
    if (!this._rot_and_index_pairs) {
      return;
    }
    if (src_object) {
      for (let object of objects) {
        object.matrix.copy(src_object.matrix);
        object.matrix.decompose(object.position, object.quaternion, object.scale);
      }
    }
    let pair;
    for (let i = 0; i < this.pv.count; i++) {
      pair = this._rot_and_index_pairs[i];
      const matrix = this._matrix(pair[0].value, pair[1].value);
      for (let object of objects) {
        object.applyMatrix4(matrix);
      }
    }
  }
  _matrix(r, rot_order_index) {
    return this._core_transform.matrix(this._t, r, this._s, this._scale, ROTATION_ORDERS[rot_order_index]);
  }
}
