import {CoreString} from "../../../../core/String";
import {NodeEvent as NodeEvent2} from "../../../poly/NodeEvent";
import {NameController as NameController2} from "../NameController";
import {CoreNodeSelection} from "../../../../core/NodeSelection";
import {Poly as Poly2} from "../../../Poly";
import {SopOperationContainer} from "../../../../core/operations/container/sop";
export class HierarchyChildrenController {
  constructor(node, _context) {
    this.node = node;
    this._context = _context;
    this._children = {};
    this._children_by_type = {};
    this._children_and_grandchildren_by_context = {};
  }
  get selection() {
    return this._selection = this._selection || new CoreNodeSelection(this.node);
  }
  dispose() {
    const children = this.children();
    for (let child of children) {
      this.node.removeNode(child);
    }
    this._selection = void 0;
  }
  get context() {
    return this._context;
  }
  set_output_node_find_method(method) {
    this._output_node_find_method = method;
  }
  output_node() {
    if (this._output_node_find_method) {
      return this._output_node_find_method();
    }
  }
  set_child_name(node, new_name) {
    let current_child_with_name;
    new_name = new_name.replace(/[^A-Za-z0-9]/g, "_");
    new_name = new_name.replace(/^[0-9]/, "_");
    if ((current_child_with_name = this._children[new_name]) != null) {
      if (node.name() === new_name && current_child_with_name.graphNodeId() === node.graphNodeId()) {
        return;
      }
      new_name = CoreString.increment(new_name);
      return this.set_child_name(node, new_name);
    } else {
      const current_name = node.name();
      const current_child = this._children[current_name];
      if (current_child) {
        delete this._children[current_name];
      }
      this._children[new_name] = node;
      node.nameController.update_name_from_parent(new_name);
      this._add_to_nodesByType(node);
      this.node.scene().nodesController.addToInstanciatedNode(node);
    }
  }
  node_context_signature() {
    return `${this.node.nodeContext()}/${this.node.type()}`;
  }
  available_children_classes() {
    return Poly2.registeredNodes(this._context, this.node.type());
  }
  is_valid_child_type(node_type) {
    const node_class = this.available_children_classes()[node_type];
    return node_class != null;
  }
  createNode(node_class_or_string, params_init_value_overrides, node_type = "") {
    if (typeof node_class_or_string == "string") {
      const node_class = this._find_node_class(node_class_or_string);
      return this._create_and_init_node(node_class, params_init_value_overrides, node_type);
    } else {
      return this._create_and_init_node(node_class_or_string, params_init_value_overrides, node_type);
    }
  }
  _create_and_init_node(node_class, params_init_value_overrides, node_type = "") {
    const child_node = new node_class(this.node.scene(), `child_node_${node_type}`, params_init_value_overrides);
    child_node.initialize_base_and_node();
    this.add_node(child_node);
    child_node.lifecycle.set_creation_completed();
    return child_node;
  }
  _find_node_class(node_type) {
    const node_class = this.available_children_classes()[node_type];
    if (node_class == null) {
      const message = `child node type '${node_type}' not found for node '${this.node.fullPath()}'. Available types are: ${Object.keys(this.available_children_classes()).join(", ")}, ${this._context}, ${this.node.type()}`;
      console.error(message);
      throw message;
    }
    return node_class;
  }
  create_operation_container(operation_type, operation_container_name, params_init_value_overrides) {
    const operation_class = Poly2.registeredOperation(this._context, operation_type);
    if (operation_class == null) {
      const message = `no operation found with context ${this._context}/${operation_type}`;
      console.error(message);
      throw message;
    } else {
      const operation = new operation_class(this.node.scene());
      const operation_container = new SopOperationContainer(operation, operation_container_name, params_init_value_overrides || {});
      return operation_container;
    }
  }
  add_node(child_node) {
    child_node.setParent(this.node);
    child_node.params.init();
    child_node.parentController.onSetParent();
    child_node.nameController.run_post_set_fullPath_hooks();
    if (child_node.childrenAllowed() && child_node.childrenController) {
      for (let child of child_node.childrenController.children()) {
        child.nameController.run_post_set_fullPath_hooks();
      }
    }
    this.node.emit(NodeEvent2.CREATED, {child_node_json: child_node.toJSON()});
    if (this.node.scene().lifecycleController.onCreateHookAllowed()) {
      child_node.lifecycle.run_on_create_hooks();
    }
    child_node.lifecycle.run_on_add_hooks();
    this.set_child_name(child_node, NameController2.base_name(child_node));
    this.node.lifecycle.run_on_child_add_hooks(child_node);
    if (child_node.require_webgl2()) {
      this.node.scene().webgl_controller.set_require_webgl2();
    }
    this.node.scene().missingExpressionReferencesController.check_for_missing_references(child_node);
    return child_node;
  }
  removeNode(child_node) {
    if (child_node.parent() != this.node) {
      return console.warn(`node ${child_node.name()} not under parent ${this.node.fullPath()}`);
    } else {
      if (this.selection.contains(child_node)) {
        this.selection.remove([child_node]);
      }
      const first_connection = child_node.io.connections.first_input_connection();
      const input_connections = child_node.io.connections.input_connections();
      const output_connections = child_node.io.connections.output_connections();
      if (input_connections) {
        for (let input_connection of input_connections) {
          if (input_connection) {
            input_connection.disconnect({setInput: true});
          }
        }
      }
      if (output_connections) {
        for (let output_connection of output_connections) {
          if (output_connection) {
            output_connection.disconnect({setInput: true});
            if (first_connection) {
              const old_src = first_connection.node_src;
              const old_output_index = output_connection.output_index;
              const old_dest = output_connection.node_dest;
              const old_input_index = output_connection.input_index;
              old_dest.io.inputs.setInput(old_input_index, old_src, old_output_index);
            }
          }
        }
      }
      child_node.setParent(null);
      delete this._children[child_node.name()];
      this._remove_from_nodesByType(child_node);
      this.node.scene().nodesController.removeFromInstanciatedNode(child_node);
      child_node.setSuccessorsDirty(this.node);
      child_node.graphDisconnectSuccessors();
      this.node.lifecycle.run_on_child_remove_hooks(child_node);
      child_node.lifecycle.run_on_delete_hooks();
      child_node.dispose();
      child_node.emit(NodeEvent2.DELETED, {parent_id: this.node.graphNodeId()});
    }
  }
  _add_to_nodesByType(node) {
    const node_id = node.graphNodeId();
    const type = node.type();
    this._children_by_type[type] = this._children_by_type[type] || [];
    if (!this._children_by_type[type].includes(node_id)) {
      this._children_by_type[type].push(node_id);
    }
    this.add_to_children_and_grandchildren_by_context(node);
  }
  _remove_from_nodesByType(node) {
    const node_id = node.graphNodeId();
    const type = node.type();
    if (this._children_by_type[type]) {
      const index = this._children_by_type[type].indexOf(node_id);
      if (index >= 0) {
        this._children_by_type[type].splice(index, 1);
        if (this._children_by_type[type].length == 0) {
          delete this._children_by_type[type];
        }
      }
    }
    this.remove_from_children_and_grandchildren_by_context(node);
  }
  add_to_children_and_grandchildren_by_context(node) {
    const node_id = node.graphNodeId();
    const type = node.nodeContext();
    this._children_and_grandchildren_by_context[type] = this._children_and_grandchildren_by_context[type] || [];
    if (!this._children_and_grandchildren_by_context[type].includes(node_id)) {
      this._children_and_grandchildren_by_context[type].push(node_id);
    }
    const parent = this.node.parent();
    if (parent && parent.childrenAllowed()) {
      parent.childrenController?.add_to_children_and_grandchildren_by_context(node);
    }
  }
  remove_from_children_and_grandchildren_by_context(node) {
    const node_id = node.graphNodeId();
    const type = node.nodeContext();
    if (this._children_and_grandchildren_by_context[type]) {
      const index = this._children_and_grandchildren_by_context[type].indexOf(node_id);
      if (index >= 0) {
        this._children_and_grandchildren_by_context[type].splice(index, 1);
        if (this._children_and_grandchildren_by_context[type].length == 0) {
          delete this._children_and_grandchildren_by_context[type];
        }
      }
    }
    const parent = this.node.parent();
    if (parent && parent.childrenAllowed()) {
      parent.childrenController?.remove_from_children_and_grandchildren_by_context(node);
    }
  }
  nodesByType(type) {
    const node_ids = this._children_by_type[type] || [];
    const graph = this.node.scene().graph;
    const nodes = [];
    for (let node_id of node_ids) {
      const node = graph.node_from_id(node_id);
      if (node) {
        nodes.push(node);
      }
    }
    return nodes;
  }
  child_by_name(name) {
    return this._children[name];
  }
  has_children_and_grandchildren_with_context(context) {
    return this._children_and_grandchildren_by_context[context] != null;
  }
  children() {
    return Object.values(this._children);
  }
  children_names() {
    return Object.keys(this._children).sort();
  }
  traverse_children(callback) {
    for (let child of this.children()) {
      callback(child);
      child.childrenController?.traverse_children(callback);
    }
  }
}
