import {TypedSopNode} from "./_Base";
import {GlobalsTextureHandler} from "../gl/code/globals/Texture";
import {InputCloneMode as InputCloneMode2} from "../../poly/InputCloneMode";
import {NodeContext as NodeContext2} from "../../poly/NodeContext";
import {ParticlesSystemGpuRenderController} from "./utils/ParticlesSystemGPU/RenderController";
import {
  ParticlesSystemGpuComputeController,
  PARTICLE_DATA_TYPES
} from "./utils/ParticlesSystemGPU/GPUComputeController";
import {NodeParamsConfig, ParamConfig} from "../utils/params/ParamsConfig";
import {GlNodeFinder} from "../gl/code/utils/NodeFinder";
import {AssemblerName} from "../../poly/registers/assemblers/_BaseRegister";
import {Poly as Poly2} from "../../Poly";
import {ParticlesPersistedConfig} from "../gl/code/assemblers/particles/PersistedConfig";
class ParticlesSystemGpuSopParamsConfig extends NodeParamsConfig {
  constructor() {
    super(...arguments);
    this.startFrame = ParamConfig.FLOAT(1, {range: [1, 100]});
    this.autoTexturesSize = ParamConfig.BOOLEAN(1);
    this.maxTexturesSize = ParamConfig.VECTOR2([1024, 1024], {visibleIf: {autoTexturesSize: 1}});
    this.texturesSize = ParamConfig.VECTOR2([64, 64], {visibleIf: {autoTexturesSize: 0}});
    this.dataType = ParamConfig.INTEGER(0, {
      menu: {
        entries: PARTICLE_DATA_TYPES.map((value, index) => {
          return {value: index, name: value};
        })
      }
    });
    this.reset = ParamConfig.BUTTON(null, {
      callback: (node, param) => {
        ParticlesSystemGpuSopNode.PARAM_CALLBACK_reset(node);
      }
    });
    this.material = ParamConfig.OPERATOR_PATH("", {
      nodeSelection: {
        context: NodeContext2.MAT
      },
      dependentOnFoundNode: false
    });
  }
}
const ParamsConfig2 = new ParticlesSystemGpuSopParamsConfig();
export class ParticlesSystemGpuSopNode extends TypedSopNode {
  constructor() {
    super(...arguments);
    this.params_config = ParamsConfig2;
    this._assembler_controller = this._create_assembler_controller();
    this.persisted_config = new ParticlesPersistedConfig(this);
    this.globals_handler = new GlobalsTextureHandler(GlobalsTextureHandler.PARTICLE_SIM_UV);
    this._shaders_by_name = new Map();
    this.gpu_controller = new ParticlesSystemGpuComputeController(this);
    this.render_controller = new ParticlesSystemGpuRenderController(this);
    this._reset_material_if_dirty_bound = this._reset_material_if_dirty.bind(this);
    this._children_controller_context = NodeContext2.GL;
  }
  static type() {
    return "particlesSystemGpu";
  }
  dispose() {
    super.dispose();
    this.gpu_controller.dispose();
  }
  get assemblerController() {
    return this._assembler_controller;
  }
  usedAssembler() {
    return AssemblerName.GL_PARTICLES;
  }
  _create_assembler_controller() {
    return Poly2.assemblersRegister.assembler(this, this.usedAssembler());
  }
  shaders_by_name() {
    return this._shaders_by_name;
  }
  static require_webgl2() {
    return true;
  }
  static PARAM_CALLBACK_reset(node) {
    node.PARAM_CALLBACK_reset();
  }
  PARAM_CALLBACK_reset() {
    this.gpu_controller.reset_gpu_compute_and_set_dirty();
  }
  static displayedInputNames() {
    return ["points to emit particles from"];
  }
  initializeNode() {
    this.io.inputs.setCount(1);
    this.io.inputs.initInputsClonedState(InputCloneMode2.NEVER);
    this.addPostDirtyHook("_reset_material_if_dirty", this._reset_material_if_dirty_bound);
  }
  createNode(node_class, params_init_value_overrides) {
    return super.createNode(node_class, params_init_value_overrides);
  }
  children() {
    return super.children();
  }
  nodesByType(type) {
    return super.nodesByType(type);
  }
  childrenAllowed() {
    if (this.assemblerController) {
      return super.childrenAllowed();
    }
    this.scene().markAsReadOnly(this);
    return false;
  }
  async _reset_material_if_dirty() {
    if (this.p.material.isDirty()) {
      this.render_controller.reset_render_material();
      if (!this.is_on_frame_start()) {
        await this.render_controller.init_render_material();
      }
    }
  }
  is_on_frame_start() {
    return this.scene().frame() == this.pv.startFrame;
  }
  async cook(input_contents) {
    this.gpu_controller.set_restart_not_required();
    const core_group = input_contents[0];
    this.compile_if_required();
    if (this.is_on_frame_start()) {
      this.gpu_controller.reset_particle_groups();
    }
    if (!this.gpu_controller.initialized) {
      await this.gpu_controller.init(core_group);
    }
    if (!this.render_controller.initialized) {
      this.render_controller.init_core_group(core_group);
      await this.render_controller.init_render_material();
    }
    this.gpu_controller.restart_simulation_if_required();
    this.gpu_controller.compute_similation_if_required();
    if (this.is_on_frame_start()) {
      this.setCoreGroup(core_group);
    } else {
      this.cookController.end_cook();
    }
  }
  async compile_if_required() {
    if (this.assemblerController?.compile_required()) {
      await this.run_assembler();
    }
  }
  async run_assembler() {
    const assemblerController = this.assemblerController;
    if (!assemblerController) {
      return;
    }
    const export_nodes = this._find_export_nodes();
    if (export_nodes.length > 0) {
      const root_nodes = export_nodes;
      assemblerController.set_assembler_globals_handler(this.globals_handler);
      assemblerController.assembler.set_root_nodes(root_nodes);
      assemblerController.assembler.compile();
      assemblerController.post_compile();
    }
    const shaders_by_name = assemblerController.assembler.shaders_by_name();
    this._set_shader_names(shaders_by_name);
  }
  _set_shader_names(shaders_by_name) {
    this._shaders_by_name = shaders_by_name;
    this.gpu_controller.set_shaders_by_name(this._shaders_by_name);
    this.render_controller.set_shaders_by_name(this._shaders_by_name);
    this.gpu_controller.reset_gpu_compute();
    this.gpu_controller.reset_particle_groups();
  }
  init_with_persisted_config() {
    const shaders_by_name = this.persisted_config.shaders_by_name();
    const texture_allocations_controller = this.persisted_config.texture_allocations_controller();
    if (shaders_by_name && texture_allocations_controller) {
      this._set_shader_names(shaders_by_name);
      this.gpu_controller.set_persisted_texture_allocation_controller(texture_allocations_controller);
    }
  }
  _find_export_nodes() {
    const nodes = GlNodeFinder.find_attribute_export_nodes(this);
    const output_nodes = GlNodeFinder.find_output_nodes(this);
    if (output_nodes.length > 1) {
      this.states.error.set("only one output node is allowed");
      return [];
    }
    const output_node = output_nodes[0];
    if (output_node) {
      nodes.push(output_node);
    }
    return nodes;
  }
}
