import {TypedNodeConnection} from "./NodeConnection";
import {CoreGraphNode as CoreGraphNode2} from "../../../../core/graph/CoreGraphNode";
import {NodeEvent as NodeEvent2} from "../../../poly/NodeEvent";
import {ClonedStatesController as ClonedStatesController2} from "./utils/ClonedStatesController";
import {CoreType} from "../../../../core/Type";
export class InputsController {
  constructor(node) {
    this.node = node;
    this._graph_node_inputs = [];
    this._inputs = [];
    this._has_named_inputs = false;
    this._min_inputs_count = 0;
    this._max_inputs_count = 0;
    this._depends_on_inputs = true;
  }
  dispose() {
    if (this._graph_node) {
      this._graph_node.dispose();
    }
    for (let graph_node of this._graph_node_inputs) {
      if (graph_node) {
        graph_node.dispose();
      }
    }
    this._on_update_hooks = void 0;
    this._on_update_hook_names = void 0;
  }
  set_depends_on_inputs(depends_on_inputs) {
    this._depends_on_inputs = depends_on_inputs;
  }
  set_min_inputs_count(min_inputs_count) {
    this._min_inputs_count = min_inputs_count;
  }
  set_max_inputs_count(max_inputs_count) {
    this._max_inputs_count = max_inputs_count;
    this.init_graph_node_inputs();
  }
  named_input_connection_points_by_name(name) {
    if (this._named_input_connection_points) {
      for (let connection_point of this._named_input_connection_points) {
        if (connection_point && connection_point.name() == name) {
          return connection_point;
        }
      }
    }
  }
  setNamedInputConnectionPoints(connection_points) {
    this._has_named_inputs = true;
    const connections = this.node.io.connections.input_connections();
    if (connections) {
      for (let connection of connections) {
        if (connection) {
          if (connection.input_index >= connection_points.length) {
            connection.disconnect({setInput: true});
          }
        }
      }
    }
    this._named_input_connection_points = connection_points;
    this.set_min_inputs_count(0);
    this.set_max_inputs_count(connection_points.length);
    this.init_graph_node_inputs();
    this.node.emit(NodeEvent2.NAMED_INPUTS_UPDATED);
  }
  get has_named_inputs() {
    return this._has_named_inputs;
  }
  get named_input_connection_points() {
    return this._named_input_connection_points || [];
  }
  init_graph_node_inputs() {
    for (let i = 0; i < this._max_inputs_count; i++) {
      this._graph_node_inputs[i] = this._graph_node_inputs[i] || this._create_graph_node_input(i);
    }
  }
  _create_graph_node_input(index) {
    const graph_input_node = new CoreGraphNode2(this.node.scene(), `input_${index}`);
    if (!this._graph_node) {
      this._graph_node = new CoreGraphNode2(this.node.scene(), "inputs");
      this.node.addGraphInput(this._graph_node, false);
    }
    this._graph_node.addGraphInput(graph_input_node, false);
    return graph_input_node;
  }
  get max_inputs_count() {
    return this._max_inputs_count || 0;
  }
  input_graph_node(input_index) {
    return this._graph_node_inputs[input_index];
  }
  setCount(min, max) {
    if (max == null) {
      max = min;
    }
    this.set_min_inputs_count(min);
    this.set_max_inputs_count(max);
    this.init_connections_controller_inputs();
  }
  init_connections_controller_inputs() {
    this.node.io.connections.init_inputs();
  }
  is_any_input_dirty() {
    return this._graph_node?.isDirty() || false;
  }
  async containers_without_evaluation() {
    const containers = [];
    for (let i = 0; i < this._inputs.length; i++) {
      const input_node = this._inputs[i];
      let container = void 0;
      if (input_node) {
        container = await input_node.requestContainer();
      }
      containers.push(container);
    }
    return containers;
  }
  existing_input_indices() {
    const existing_input_indices = [];
    if (this._max_inputs_count > 0) {
      for (let i = 0; i < this._inputs.length; i++) {
        if (this._inputs[i]) {
          existing_input_indices.push(i);
        }
      }
    }
    return existing_input_indices;
  }
  async eval_required_inputs() {
    let containers = [];
    if (this._max_inputs_count > 0) {
      const existing_input_indices = this.existing_input_indices();
      if (existing_input_indices.length < this._min_inputs_count) {
        this.node.states.error.set("inputs are missing");
      } else {
        if (existing_input_indices.length > 0) {
          const promises = [];
          let input;
          for (let i = 0; i < this._inputs.length; i++) {
            input = this._inputs[i];
            if (input) {
              promises.push(this.eval_required_input(i));
            }
          }
          containers = await Promise.all(promises);
          this._graph_node?.removeDirtyState();
        }
      }
    }
    return containers;
  }
  async eval_required_input(input_index) {
    let container = void 0;
    const input_node = this.input(input_index);
    if (input_node) {
      container = await input_node.requestContainer();
      this._graph_node_inputs[input_index].removeDirtyState();
    }
    if (container && container.coreContent()) {
    } else {
      const input_node2 = this.input(input_index);
      if (input_node2) {
        const input_error_message = input_node2.states.error.message();
        if (input_error_message) {
          this.node.states.error.set(`input ${input_index} is invalid (error: ${input_error_message})`);
        }
      }
    }
    return container;
  }
  get_named_input_index(name) {
    if (this._named_input_connection_points) {
      for (let i = 0; i < this._named_input_connection_points.length; i++) {
        if (this._named_input_connection_points[i]?.name() == name) {
          return i;
        }
      }
    }
    return -1;
  }
  get_input_index(input_index_or_name) {
    if (CoreType.isString(input_index_or_name)) {
      if (this.has_named_inputs) {
        return this.get_named_input_index(input_index_or_name);
      } else {
        throw new Error(`node ${this.node.fullPath()} has no named inputs`);
      }
    } else {
      return input_index_or_name;
    }
  }
  setInput(input_index_or_name, node, output_index_or_name = 0) {
    const input_index = this.get_input_index(input_index_or_name) || 0;
    if (input_index < 0) {
      const message = `invalid input (${input_index_or_name}) for node ${this.node.fullPath()}`;
      console.warn(message);
      throw new Error(message);
    }
    let output_index = 0;
    if (node) {
      if (node.io.outputs.has_named_outputs) {
        output_index = node.io.outputs.get_output_index(output_index_or_name);
        if (output_index == null || output_index < 0) {
          const connection_points = node.io.outputs.named_output_connection_points;
          const names = connection_points.map((cp) => cp.name());
          console.warn(`node ${node.fullPath()} does not have an output named ${output_index_or_name}. inputs are: ${names.join(", ")}`);
          return;
        }
      }
    }
    const graph_input_node = this._graph_node_inputs[input_index];
    if (graph_input_node == null) {
      const message = `graph_input_node not found at index ${input_index}`;
      console.warn(message);
      throw new Error(message);
    }
    if (node && this.node.parent() != node.parent()) {
      return;
    }
    const old_input_node = this._inputs[input_index];
    let old_output_index = null;
    let old_connection = void 0;
    if (this.node.io.connections) {
      old_connection = this.node.io.connections.input_connection(input_index);
    }
    if (old_connection) {
      old_output_index = old_connection.output_index;
    }
    if (node !== old_input_node || output_index != old_output_index) {
      if (old_input_node != null) {
        if (this._depends_on_inputs) {
          graph_input_node.removeGraphInput(old_input_node);
        }
      }
      if (node != null) {
        if (graph_input_node.addGraphInput(node)) {
          if (!this._depends_on_inputs) {
            graph_input_node.removeGraphInput(node);
          }
          if (old_connection) {
            old_connection.disconnect({setInput: false});
          }
          this._inputs[input_index] = node;
          new TypedNodeConnection(node, this.node, output_index, input_index);
        } else {
          console.warn(`cannot connect ${node.fullPath()} to ${this.node.fullPath()}`);
        }
      } else {
        this._inputs[input_index] = null;
        if (old_connection) {
          old_connection.disconnect({setInput: false});
        }
      }
      this._run_on_set_input_hooks();
      graph_input_node.setSuccessorsDirty();
      this.node.emit(NodeEvent2.INPUTS_UPDATED);
    }
  }
  remove_input(node) {
    const inputs = this.inputs();
    let input;
    for (let i = 0; i < inputs.length; i++) {
      input = inputs[i];
      if (input != null && node != null) {
        if (input.graphNodeId() === node.graphNodeId()) {
          this.setInput(i, null);
        }
      }
    }
  }
  input(input_index) {
    return this._inputs[input_index];
  }
  named_input(input_name) {
    if (this.has_named_inputs) {
      const input_index = this.get_input_index(input_name);
      return this._inputs[input_index];
    } else {
      return null;
    }
  }
  named_input_connection_point(input_name) {
    if (this.has_named_inputs && this._named_input_connection_points) {
      const input_index = this.get_input_index(input_name);
      return this._named_input_connection_points[input_index];
    }
  }
  has_named_input(name) {
    return this.get_named_input_index(name) >= 0;
  }
  has_input(input_index) {
    return this._inputs[input_index] != null;
  }
  inputs() {
    return this._inputs;
  }
  initInputsClonedState(states) {
    if (!this._cloned_states_controller) {
      this._cloned_states_controller = new ClonedStatesController2(this);
      this._cloned_states_controller.init_inputs_cloned_state(states);
    }
  }
  override_cloned_state_allowed() {
    return this._cloned_states_controller?.override_cloned_state_allowed() || false;
  }
  override_cloned_state(state) {
    this._cloned_states_controller?.override_cloned_state(state);
  }
  cloned_state_overriden() {
    return this._cloned_states_controller?.overriden() || false;
  }
  clone_required(index) {
    const state = this._cloned_states_controller?.clone_required_state(index);
    if (state != null) {
      return state;
    }
    return true;
  }
  clone_required_states() {
    const states = this._cloned_states_controller?.clone_required_states();
    if (states != null) {
      return states;
    }
    return true;
  }
  add_on_set_input_hook(name, hook) {
    this._on_update_hooks = this._on_update_hooks || [];
    this._on_update_hook_names = this._on_update_hook_names || [];
    if (!this._on_update_hook_names.includes(name)) {
      this._on_update_hooks.push(hook);
      this._on_update_hook_names.push(name);
    } else {
      console.warn(`hook with name ${name} already exists`, this.node);
    }
  }
  _run_on_set_input_hooks() {
    if (this._on_update_hooks) {
      for (let hook of this._on_update_hooks) {
        hook();
      }
    }
  }
}
