import {TypedEventNode} from "./_Base";
import {NodeContext as NodeContext2} from "../../poly/NodeContext";
import {RaycastCPUController} from "./utils/raycast/CPUController";
import {CPUIntersectWith, CPU_INTERSECT_WITH_OPTIONS} from "./utils/raycast/CpuConstants";
import {RaycastGPUController} from "./utils/raycast/GPUController";
import {AttribType, ATTRIBUTE_TYPES, AttribTypeMenuEntries} from "../../../core/geometry/Constant";
import {EventConnectionPoint, EventConnectionPointType} from "../utils/io/connections/Event";
import {ParamType as ParamType2} from "../../poly/ParamType";
const TIMESTAMP = 1e3 / 60;
var RaycastMode;
(function(RaycastMode2) {
  RaycastMode2["CPU"] = "cpu";
  RaycastMode2["GPU"] = "gpu";
})(RaycastMode || (RaycastMode = {}));
const RAYCAST_MODES = [RaycastMode.CPU, RaycastMode.GPU];
function visible_for_cpu(options = {}) {
  options["mode"] = RAYCAST_MODES.indexOf(RaycastMode.CPU);
  return {visibleIf: options};
}
function visible_for_cpu_geometry(options = {}) {
  options["mode"] = RAYCAST_MODES.indexOf(RaycastMode.CPU);
  options["intersectWith"] = CPU_INTERSECT_WITH_OPTIONS.indexOf(CPUIntersectWith.GEOMETRY);
  return {visibleIf: options};
}
function visible_for_cpu_plane(options = {}) {
  options["mode"] = RAYCAST_MODES.indexOf(RaycastMode.CPU);
  options["intersectWith"] = CPU_INTERSECT_WITH_OPTIONS.indexOf(CPUIntersectWith.PLANE);
  return {visibleIf: options};
}
function visible_for_gpu(options = {}) {
  options["mode"] = RAYCAST_MODES.indexOf(RaycastMode.GPU);
  return {visibleIf: options};
}
export var TargetType;
(function(TargetType2) {
  TargetType2["SCENE_GRAPH"] = "scene graph";
  TargetType2["NODE"] = "node";
})(TargetType || (TargetType = {}));
export const TARGET_TYPES = [TargetType.SCENE_GRAPH, TargetType.NODE];
import {NodeParamsConfig, ParamConfig} from "../utils/params/ParamsConfig";
class RaycastParamsConfig extends NodeParamsConfig {
  constructor() {
    super(...arguments);
    this.mode = ParamConfig.INTEGER(RAYCAST_MODES.indexOf(RaycastMode.CPU), {
      menu: {
        entries: RAYCAST_MODES.map((name, value) => {
          return {
            name,
            value
          };
        })
      }
    });
    this.mouse = ParamConfig.VECTOR2([0, 0], {cook: false});
    this.overrideCamera = ParamConfig.BOOLEAN(0);
    this.overrideRay = ParamConfig.BOOLEAN(0, {
      visibleIf: {
        mode: RAYCAST_MODES.indexOf(RaycastMode.CPU),
        overrideCamera: 1
      }
    });
    this.camera = ParamConfig.OPERATOR_PATH("/perspective_camera1", {
      nodeSelection: {
        context: NodeContext2.OBJ
      },
      dependentOnFoundNode: false,
      visibleIf: {
        overrideCamera: 1,
        overrideRay: 0
      }
    });
    this.rayOrigin = ParamConfig.VECTOR3([0, 0, 0], {
      visibleIf: {
        overrideCamera: 1,
        overrideRay: 1
      }
    });
    this.rayDirection = ParamConfig.VECTOR3([0, 0, 1], {
      visibleIf: {
        overrideCamera: 1,
        overrideRay: 1
      }
    });
    this.material = ParamConfig.OPERATOR_PATH("/MAT/mesh_basic_builder1", {
      nodeSelection: {
        context: NodeContext2.MAT
      },
      dependentOnFoundNode: false,
      callback: (node, param) => {
        RaycastGPUController.PARAM_CALLBACK_update_material(node);
      },
      ...visible_for_gpu()
    });
    this.pixelValue = ParamConfig.VECTOR4([0, 0, 0, 0], {
      cook: false,
      ...visible_for_gpu()
    });
    this.hitThreshold = ParamConfig.FLOAT(0.5, {
      cook: false,
      ...visible_for_gpu()
    });
    this.intersectWith = ParamConfig.INTEGER(CPU_INTERSECT_WITH_OPTIONS.indexOf(CPUIntersectWith.GEOMETRY), {
      menu: {
        entries: CPU_INTERSECT_WITH_OPTIONS.map((name, value) => {
          return {name, value};
        })
      },
      ...visible_for_cpu()
    });
    this.pointsThreshold = ParamConfig.FLOAT(1, {
      range: [0, 100],
      rangeLocked: [true, false],
      ...visible_for_cpu()
    });
    this.planeDirection = ParamConfig.VECTOR3([0, 1, 0], {
      ...visible_for_cpu_plane()
    });
    this.planeOffset = ParamConfig.FLOAT(0, {
      ...visible_for_cpu_plane()
    });
    this.targetType = ParamConfig.INTEGER(0, {
      menu: {
        entries: TARGET_TYPES.map((name, value) => {
          return {name, value};
        })
      }
    });
    this.targetNode = ParamConfig.NODE_PATH("/geo1", {
      nodeSelection: {
        context: NodeContext2.OBJ
      },
      dependentOnFoundNode: false,
      callback: (node, param) => {
        RaycastCPUController.PARAM_CALLBACK_update_target(node);
      },
      ...visible_for_cpu_geometry({targetType: TARGET_TYPES.indexOf(TargetType.NODE)})
    });
    this.objectMask = ParamConfig.STRING("*geo1*", {
      callback: (node, param) => {
        RaycastCPUController.PARAM_CALLBACK_update_target(node);
      },
      ...visible_for_cpu_geometry({targetType: TARGET_TYPES.indexOf(TargetType.SCENE_GRAPH)})
    });
    this.printFoundObjectsFromMask = ParamConfig.BUTTON(null, {
      callback: (node, param) => {
        RaycastCPUController.PARAM_CALLBACK_print_resolve(node);
      },
      ...visible_for_cpu_geometry({targetType: TARGET_TYPES.indexOf(TargetType.SCENE_GRAPH)})
    });
    this.traverseChildren = ParamConfig.BOOLEAN(0, {
      callback: (node, param) => {
        RaycastCPUController.PARAM_CALLBACK_update_target(node);
      },
      ...visible_for_cpu_geometry()
    });
    this.sep = ParamConfig.SEPARATOR(null, {
      ...visible_for_cpu_geometry()
    });
    this.tpositionTarget = ParamConfig.BOOLEAN(0, {
      cook: false,
      ...visible_for_cpu()
    });
    this.position = ParamConfig.VECTOR3([0, 0, 0], {
      cook: false,
      ...visible_for_cpu({tpositionTarget: 0})
    });
    this.positionTarget = ParamConfig.OPERATOR_PATH("", {
      cook: false,
      ...visible_for_cpu({tpositionTarget: 1}),
      paramSelection: ParamType2.VECTOR3,
      computeOnDirty: true
    });
    this.tvelocity = ParamConfig.BOOLEAN(0, {
      cook: false
    });
    this.tvelocityTarget = ParamConfig.BOOLEAN(0, {
      cook: false,
      ...visible_for_cpu({tvelocity: 1})
    });
    this.velocity = ParamConfig.VECTOR3([0, 0, 0], {
      cook: false,
      ...visible_for_cpu({tvelocity: 1, tvelocityTarget: 0})
    });
    this.velocityTarget = ParamConfig.OPERATOR_PATH("", {
      cook: false,
      ...visible_for_cpu({tvelocity: 1, tvelocityTarget: 1}),
      paramSelection: ParamType2.VECTOR3,
      computeOnDirty: true
    });
    this.geoAttribute = ParamConfig.BOOLEAN(0, visible_for_cpu_geometry());
    this.geoAttributeName = ParamConfig.STRING("id", {
      cook: false,
      ...visible_for_cpu_geometry({geoAttribute: 1})
    });
    this.geoAttributeType = ParamConfig.INTEGER(ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC), {
      menu: {
        entries: AttribTypeMenuEntries
      },
      ...visible_for_cpu_geometry({geoAttribute: 1})
    });
    this.geoAttributeValue1 = ParamConfig.FLOAT(0, {
      cook: false,
      ...visible_for_cpu_geometry({
        geoAttribute: 1,
        geoAttributeType: ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC)
      })
    });
    this.geoAttributeValues = ParamConfig.STRING("", {
      ...visible_for_cpu_geometry({
        geoAttribute: 1,
        geoAttributeType: ATTRIBUTE_TYPES.indexOf(AttribType.STRING)
      })
    });
  }
}
const ParamsConfig2 = new RaycastParamsConfig();
const RaycastEventNode2 = class extends TypedEventNode {
  constructor() {
    super(...arguments);
    this.params_config = ParamsConfig2;
    this.cpu_controller = new RaycastCPUController(this);
    this.gpu_controller = new RaycastGPUController(this);
    this._last_event_processed_at = performance.now();
  }
  static type() {
    return "raycast";
  }
  initializeNode() {
    this.io.inputs.setNamedInputConnectionPoints([
      new EventConnectionPoint("trigger", EventConnectionPointType.BASE, this._process_trigger_event_throttled.bind(this)),
      new EventConnectionPoint("mouse", EventConnectionPointType.MOUSE, this._process_mouse_event.bind(this)),
      new EventConnectionPoint("update_objects", EventConnectionPointType.BASE, this._process_trigger_update_objects.bind(this)),
      new EventConnectionPoint("trigger_vel_reset", EventConnectionPointType.BASE, this._process_trigger_vel_reset.bind(this))
    ]);
    this.io.outputs.setNamedOutputConnectionPoints([
      new EventConnectionPoint(RaycastEventNode2.OUTPUT_HIT, EventConnectionPointType.BASE),
      new EventConnectionPoint(RaycastEventNode2.OUTPUT_MISS, EventConnectionPointType.BASE)
    ]);
  }
  trigger_hit(context) {
    this.dispatch_event_to_output(RaycastEventNode2.OUTPUT_HIT, context);
  }
  trigger_miss(context) {
    this.dispatch_event_to_output(RaycastEventNode2.OUTPUT_MISS, context);
  }
  _process_mouse_event(context) {
    if (this.pv.mode == RAYCAST_MODES.indexOf(RaycastMode.CPU)) {
      this.cpu_controller.update_mouse(context);
    } else {
      this.gpu_controller.update_mouse(context);
    }
  }
  _process_trigger_event_throttled(context) {
    const previous = this._last_event_processed_at;
    const now = performance.now();
    this._last_event_processed_at = now;
    const delta = now - previous;
    if (delta < TIMESTAMP) {
      setTimeout(() => {
        this._process_trigger_event(context);
      }, TIMESTAMP - delta);
    } else {
      this._process_trigger_event(context);
    }
  }
  _process_trigger_event(context) {
    if (this.pv.mode == RAYCAST_MODES.indexOf(RaycastMode.CPU)) {
      this.cpu_controller.process_event(context);
    } else {
      this.gpu_controller.process_event(context);
    }
  }
  _process_trigger_update_objects(context) {
    if (this.pv.mode == RAYCAST_MODES.indexOf(RaycastMode.CPU)) {
      this.cpu_controller.update_target();
    }
  }
  _process_trigger_vel_reset(context) {
    if (this.pv.mode == RAYCAST_MODES.indexOf(RaycastMode.CPU)) {
      this.cpu_controller.velocity_controller.reset();
    }
  }
};
export let RaycastEventNode = RaycastEventNode2;
RaycastEventNode.OUTPUT_HIT = "hit";
RaycastEventNode.OUTPUT_MISS = "miss";
