import {UpdateFromControlsMode, UPDATE_FROM_CONTROLS_MODES} from "../../_BaseCamera";
import {CameraControlsConfig} from "../../../event/utils/CameraControlConfig";
import {TypeAssert} from "../../../../poly/Assert";
import {CAMERA_CONTROLS_NODE_TYPES} from "../../../../poly/NodeContext";
const CONTROLS_PARAM_NAME = "controls";
export class ThreejsCameraControlsController {
  constructor(node) {
    this.node = node;
    this._applied_controls_by_element_id = new Map();
    this._controls_node = null;
  }
  controls_param() {
    if (this.node.params.has(CONTROLS_PARAM_NAME)) {
      return this.node.params.get(CONTROLS_PARAM_NAME);
    }
    return null;
  }
  async controls_node() {
    const controls_param = this.node.p.controls;
    const raw_input = controls_param.raw_input;
    if (raw_input && raw_input != "") {
      if (controls_param.isDirty()) {
        await controls_param.compute();
      }
      const node = controls_param.value.node();
      if (node) {
        if (CAMERA_CONTROLS_NODE_TYPES.includes(node.type())) {
          return node;
        } else {
          this.node.states.error.set("found node is not of a camera control type");
        }
      } else {
        this.node.states.error.set("no node has been found");
      }
    }
    return null;
  }
  async update_controls() {
    const controls_node = await this.controls_node();
    if (controls_node) {
      if (this._controls_node != controls_node) {
        this._dispose_control_refs();
      }
    }
    this._controls_node = controls_node;
  }
  async apply_controls(viewer) {
    const canvas = viewer.canvas();
    if (!canvas) {
      return;
    }
    const controls_node = await this.controls_node();
    if (controls_node) {
      const controls_id = controls_node.controls_id();
      let controls_aleady_applied = false;
      let map_for_element = this._applied_controls_by_element_id.get(canvas.id);
      if (map_for_element && map_for_element.get(controls_id)) {
        controls_aleady_applied = true;
      }
      if (!controls_aleady_applied) {
        map_for_element = new Map();
        this._applied_controls_by_element_id.set(canvas.id, map_for_element);
        map_for_element.set(controls_id, controls_node);
        const controls = await controls_node.apply_controls(this.node.object, viewer);
        if (!controls) {
          return;
        }
        const config = new CameraControlsConfig(this.node.graphNodeId(), controls_node, controls);
        this.set_controls_events(controls);
        return config;
      }
    }
  }
  _dispose_control_refs() {
    this._applied_controls_by_element_id.forEach((map_for_element, element_id) => {
      this._dispose_controls_for_element_id(element_id);
    });
    this._applied_controls_by_element_id.clear();
  }
  _dispose_controls_for_element_id(html_element_id) {
    const map_for_element = this._applied_controls_by_element_id.get(html_element_id);
    if (map_for_element) {
      map_for_element.forEach((controls_node, controls_id) => {
        controls_node.dispose_controls_for_html_element_id(html_element_id);
      });
    }
    this._applied_controls_by_element_id.delete(html_element_id);
  }
  async dispose_controls(html_element) {
    this._dispose_controls_for_element_id(html_element.id);
  }
  set_controls_events(controls) {
    const update_mode = UPDATE_FROM_CONTROLS_MODES[this.node.pv.updateFromControlsMode];
    switch (update_mode) {
      case UpdateFromControlsMode.ON_END:
        return this._set_controls_events_to_update_on_end(controls);
      case UpdateFromControlsMode.ALWAYS:
        return this._set_controls_events_to_update_always(controls);
      case UpdateFromControlsMode.NEVER:
        return this._reset(controls);
    }
    TypeAssert.unreachable(update_mode);
  }
  _reset(controls) {
    if (this.controls_change_listener) {
      controls.removeEventListener("change", this.controls_change_listener);
      this.controls_change_listener = void 0;
    }
    if (this.controls_end_listener) {
      controls.removeEventListener("end", this.controls_end_listener);
      this.controls_end_listener = void 0;
    }
  }
  _set_controls_events_to_update_on_end(controls) {
    this._reset(controls);
    this.controls_end_listener = () => {
      this.node.update_transform_params_from_object();
    };
    controls.addEventListener("end", this.controls_end_listener);
  }
  _set_controls_events_to_update_always(controls) {
    this._reset(controls);
    this.controls_change_listener = () => {
      this.node.update_transform_params_from_object();
    };
    controls.addEventListener("change", this.controls_change_listener);
  }
}
