import {TypedSopNode} from "./_Base";
import {
  AttribClass,
  AttribClassMenuEntries,
  ObjectType,
  ObjectTypeMenuEntries,
  ObjectTypes,
  objectTypeFromConstructor,
  AttribType,
  AttribTypeMenuEntries,
  ATTRIBUTE_TYPES,
  AttribSize,
  ATTRIBUTE_CLASSES,
  ATTRIBUTE_SIZE_RANGE
} from "../../../core/geometry/Constant";
import {CoreGeometry} from "../../../core/geometry/Geometry";
import {InputCloneMode as InputCloneMode2} from "../../poly/InputCloneMode";
import {NodeParamsConfig, ParamConfig} from "../utils/params/ParamsConfig";
import {EntitySelectionHelper as EntitySelectionHelper2} from "./utils/delete/EntitySelectionHelper";
import {
  ByAttributeHelper as ByAttributeHelper2,
  ComparisonOperatorMenuEntries,
  ComparisonOperator,
  COMPARISON_OPERATORS
} from "./utils/delete/ByAttributeHelper";
import {ByExpressionHelper as ByExpressionHelper2} from "./utils/delete/ByExpressionHelper";
import {ByBboxHelper as ByBboxHelper2} from "./utils/delete/ByBboxHelper";
import {ByObjectTypeHelper as ByObjectTypeHelper2} from "./utils/delete/ByObjectTypeHelper";
class DeleteSopParamsConfig extends NodeParamsConfig {
  constructor() {
    super(...arguments);
    this.class = ParamConfig.INTEGER(ATTRIBUTE_CLASSES.indexOf(AttribClass.VERTEX), {
      menu: {
        entries: AttribClassMenuEntries
      }
    });
    this.invert = ParamConfig.BOOLEAN(0);
    this.byObjectType = ParamConfig.BOOLEAN(0, {
      visibleIf: {class: ATTRIBUTE_CLASSES.indexOf(AttribClass.OBJECT)}
    });
    this.objectType = ParamConfig.INTEGER(ObjectTypes.indexOf(ObjectType.MESH), {
      menu: {
        entries: ObjectTypeMenuEntries
      },
      visibleIf: {
        class: ATTRIBUTE_CLASSES.indexOf(AttribClass.OBJECT),
        byObjectType: true
      }
    });
    this.separatorObjectType = ParamConfig.SEPARATOR(null, {
      visibleIf: {class: ATTRIBUTE_CLASSES.indexOf(AttribClass.OBJECT)}
    });
    this.byExpression = ParamConfig.BOOLEAN(0);
    this.expression = ParamConfig.BOOLEAN("@ptnum==0", {
      visibleIf: {byExpression: true},
      expression: {forEntities: true}
    });
    this.separatorExpression = ParamConfig.SEPARATOR();
    this.byAttrib = ParamConfig.BOOLEAN(0);
    this.attribType = ParamConfig.INTEGER(ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC), {
      menu: {
        entries: AttribTypeMenuEntries
      },
      visibleIf: {byAttrib: 1}
    });
    this.attribName = ParamConfig.STRING("", {
      visibleIf: {byAttrib: 1}
    });
    this.attribSize = ParamConfig.INTEGER(1, {
      range: ATTRIBUTE_SIZE_RANGE,
      rangeLocked: [true, true],
      visibleIf: {byAttrib: 1, attribType: ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC)}
    });
    this.attribComparisonOperator = ParamConfig.INTEGER(COMPARISON_OPERATORS.indexOf(ComparisonOperator.EQUAL), {
      menu: {
        entries: ComparisonOperatorMenuEntries
      },
      visibleIf: {
        byAttrib: true,
        attribType: ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC),
        attribSize: AttribSize.FLOAT
      }
    });
    this.attribValue1 = ParamConfig.FLOAT(0, {
      visibleIf: {byAttrib: 1, attribType: ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC), attribSize: 1}
    });
    this.attribValue2 = ParamConfig.VECTOR2([0, 0], {
      visibleIf: {byAttrib: 1, attribType: ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC), attribSize: 2}
    });
    this.attribValue3 = ParamConfig.VECTOR3([0, 0, 0], {
      visibleIf: {byAttrib: 1, attribType: ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC), attribSize: 3}
    });
    this.attribValue4 = ParamConfig.VECTOR4([0, 0, 0, 0], {
      visibleIf: {byAttrib: 1, attribType: ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC), attribSize: 4}
    });
    this.attribString = ParamConfig.STRING("", {
      visibleIf: {byAttrib: 1, attribType: ATTRIBUTE_TYPES.indexOf(AttribType.STRING)}
    });
    this.separatorAttrib = ParamConfig.SEPARATOR();
    this.byBbox = ParamConfig.BOOLEAN(0, {
      visibleIf: {
        class: ATTRIBUTE_CLASSES.indexOf(AttribClass.VERTEX)
      }
    });
    this.bboxSize = ParamConfig.VECTOR3([1, 1, 1], {
      visibleIf: {
        class: ATTRIBUTE_CLASSES.indexOf(AttribClass.VERTEX),
        byBbox: true
      }
    });
    this.bboxCenter = ParamConfig.VECTOR3([0, 0, 0], {
      visibleIf: {
        class: ATTRIBUTE_CLASSES.indexOf(AttribClass.VERTEX),
        byBbox: true
      }
    });
    this.separatorBbox = ParamConfig.SEPARATOR(null, {
      visibleIf: {
        class: ATTRIBUTE_CLASSES.indexOf(AttribClass.VERTEX)
      }
    });
    this.keepPoints = ParamConfig.BOOLEAN(0, {
      visibleIf: {class: ATTRIBUTE_CLASSES.indexOf(AttribClass.OBJECT)}
    });
  }
}
const ParamsConfig2 = new DeleteSopParamsConfig();
export class DeleteSopNode extends TypedSopNode {
  constructor() {
    super(...arguments);
    this.params_config = ParamsConfig2;
    this._marked_for_deletion_per_object_index = new Map();
    this.entity_selection_helper = new EntitySelectionHelper2(this);
    this.byBbox_helper = new ByBboxHelper2(this);
    this.byExpression_helper = new ByExpressionHelper2(this);
    this.byAttribute_helper = new ByAttributeHelper2(this);
    this.byObjectType_helper = new ByObjectTypeHelper2(this);
  }
  static type() {
    return "delete";
  }
  static displayedInputNames() {
    return ["geometry to delete from"];
  }
  initializeNode() {
    this.io.inputs.setCount(1);
    this.io.inputs.initInputsClonedState(InputCloneMode2.FROM_NODE);
  }
  async cook(input_contents) {
    const core_group = input_contents[0];
    switch (this.pv.class) {
      case AttribClass.VERTEX:
        await this._eval_for_points(core_group);
        break;
      case AttribClass.OBJECT:
        await this._eval_for_objects(core_group);
        break;
    }
  }
  set_class(attrib_class) {
    this.p.class.set(attrib_class);
  }
  async _eval_for_objects(core_group) {
    const core_objects = core_group.coreObjects();
    this.entity_selection_helper.init(core_objects);
    this._marked_for_deletion_per_object_index = new Map();
    for (let core_object of core_objects) {
      this._marked_for_deletion_per_object_index.set(core_object.index(), false);
    }
    if (this.pv.byExpression) {
      await this.byExpression_helper.eval_for_entities(core_objects);
    }
    if (this.pv.byObjectType) {
      this.byObjectType_helper.eval_for_objects(core_objects);
    }
    if (this.pv.byAttrib && this.pv.attribName != "") {
      this.byAttribute_helper.eval_for_entities(core_objects);
    }
    const core_objects_to_keep = this.entity_selection_helper.entities_to_keep();
    const objects_to_keep = core_objects_to_keep.map((co) => co.object());
    if (this.pv.keepPoints) {
      const core_objects_to_delete = this.entity_selection_helper.entities_to_delete();
      for (let core_object_to_delete of core_objects_to_delete) {
        const point_object = this._point_object(core_object_to_delete);
        if (point_object) {
          objects_to_keep.push(point_object);
        }
      }
    }
    this.setObjects(objects_to_keep);
  }
  async _eval_for_points(core_group) {
    const core_objects = core_group.coreObjects();
    let core_object;
    let objects = [];
    for (let i = 0; i < core_objects.length; i++) {
      core_object = core_objects[i];
      let core_geometry = core_object.coreGeometry();
      if (core_geometry) {
        const object = core_object.object();
        const points = core_geometry.pointsFromGeometry();
        this.entity_selection_helper.init(points);
        const init_points_count = points.length;
        if (this.pv.byExpression) {
          await this.byExpression_helper.eval_for_entities(points);
        }
        if (this.pv.byAttrib && this.pv.attribName != "") {
          this.byAttribute_helper.eval_for_entities(points);
        }
        if (this.pv.byBbox) {
          this.byBbox_helper.eval_for_points(points);
        }
        const kept_points = this.entity_selection_helper.entities_to_keep();
        if (kept_points.length == init_points_count) {
          objects.push(object);
        } else {
          core_geometry.geometry().dispose();
          if (kept_points.length > 0) {
            const new_geo = CoreGeometry.geometryFromPoints(kept_points, objectTypeFromConstructor(object.constructor));
            if (new_geo) {
              object.geometry = new_geo;
              objects.push(object);
            }
          }
        }
      }
    }
    this.setObjects(objects);
  }
  _point_object(core_object) {
    const core_points = core_object.points();
    const geometry = CoreGeometry.geometryFromPoints(core_points, ObjectType.POINTS);
    if (geometry)
      return this.create_object(geometry, ObjectType.POINTS);
  }
}
