import {WebGLRenderTarget as WebGLRenderTarget2} from "three/src/renderers/WebGLRenderTarget";
import {ShaderMaterial as ShaderMaterial2} from "three/src/materials/ShaderMaterial";
import {Scene as Scene2} from "three/src/scenes/Scene";
import {
  FloatType,
  HalfFloatType,
  RGBAFormat,
  NearestFilter,
  LinearFilter,
  ClampToEdgeWrapping
} from "three/src/constants";
import {PlaneBufferGeometry as PlaneBufferGeometry2} from "three/src/geometries/PlaneBufferGeometry";
import {Mesh as Mesh2} from "three/src/objects/Mesh";
import {Camera as Camera2} from "three/src/cameras/Camera";
import {TypedCopNode} from "./_Base";
import {GlobalsGeometryHandler} from "../gl/code/globals/Geometry";
import {GlNodeFinder} from "../gl/code/utils/NodeFinder";
import {NodeContext as NodeContext2} from "../../poly/NodeContext";
const VERTEX_SHADER = `
void main()	{
	gl_Position = vec4( position, 1.0 );
}
`;
const RESOLUTION_DEFAULT = [256, 256];
import {NodeParamsConfig, ParamConfig} from "../utils/params/ParamsConfig";
import {DataTextureController as DataTextureController2, DataTextureControllerBufferType} from "./utils/DataTextureController";
import {CopRendererController} from "./utils/RendererController";
import {AssemblerName} from "../../poly/registers/assemblers/_BaseRegister";
import {Poly as Poly2} from "../../Poly";
import {TexturePersistedConfig} from "../gl/code/assemblers/textures/PersistedConfig";
class BuilderCopParamsConfig extends NodeParamsConfig {
  constructor() {
    super(...arguments);
    this.resolution = ParamConfig.VECTOR2(RESOLUTION_DEFAULT);
    this.useCameraRenderer = ParamConfig.BOOLEAN(0);
  }
}
const ParamsConfig2 = new BuilderCopParamsConfig();
export class BuilderCopNode extends TypedCopNode {
  constructor() {
    super(...arguments);
    this.params_config = ParamsConfig2;
    this.persisted_config = new TexturePersistedConfig(this);
    this._assembler_controller = this._create_assembler_controller();
    this._texture_mesh = new Mesh2(new PlaneBufferGeometry2(2, 2));
    this.texture_material = new ShaderMaterial2({
      uniforms: {},
      vertexShader: VERTEX_SHADER,
      fragmentShader: ""
    });
    this._texture_scene = new Scene2();
    this._texture_camera = new Camera2();
    this._children_controller_context = NodeContext2.GL;
    this._cook_main_without_inputs_when_dirty_bound = this._cook_main_without_inputs_when_dirty.bind(this);
  }
  static type() {
    return "builder";
  }
  usedAssembler() {
    return AssemblerName.GL_TEXTURE;
  }
  _create_assembler_controller() {
    const assembler_controller = Poly2.assemblersRegister.assembler(this, this.usedAssembler());
    if (assembler_controller) {
      const globals_handler = new GlobalsGeometryHandler();
      assembler_controller.set_assembler_globals_handler(globals_handler);
      return assembler_controller;
    }
  }
  get assemblerController() {
    return this._assembler_controller;
  }
  initializeNode() {
    this._texture_mesh.material = this.texture_material;
    this._texture_mesh.scale.multiplyScalar(0.25);
    this._texture_scene.add(this._texture_mesh);
    this._texture_camera.position.z = 1;
    this.addPostDirtyHook("_cook_main_without_inputs_when_dirty", () => {
      setTimeout(this._cook_main_without_inputs_when_dirty_bound, 0);
    });
  }
  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 _cook_main_without_inputs_when_dirty() {
    await this.cookController.cook_main_without_inputs();
  }
  async cook() {
    this.compile_if_required();
    this.render_on_target();
  }
  shaders_by_name() {
    return {
      fragment: this._fragment_shader
    };
  }
  compile_if_required() {
    if (this.assemblerController?.compile_required()) {
      this.compile();
    }
  }
  compile() {
    const assemblerController = this.assemblerController;
    if (!assemblerController) {
      return;
    }
    const output_nodes = GlNodeFinder.find_output_nodes(this);
    if (output_nodes.length > 1) {
      this.states.error.set("only one output node allowed");
      return;
    }
    const output_node = output_nodes[0];
    if (output_node) {
      const root_nodes = output_nodes;
      assemblerController.assembler.set_root_nodes(root_nodes);
      assemblerController.assembler.update_fragment_shader();
      const fragment_shader = assemblerController.assembler.fragment_shader();
      const uniforms = assemblerController.assembler.uniforms();
      if (fragment_shader && uniforms) {
        this._fragment_shader = fragment_shader;
        this._uniforms = uniforms;
      }
      BuilderCopNode.handle_dependencies(this, assemblerController.assembler.uniforms_time_dependent());
    }
    if (this._fragment_shader && this._uniforms) {
      this.texture_material.fragmentShader = this._fragment_shader;
      this.texture_material.uniforms = this._uniforms;
      this.texture_material.needsUpdate = true;
      this.texture_material.uniforms.resolution = {
        value: this.pv.resolution
      };
    }
    assemblerController.post_compile();
  }
  static handle_dependencies(node, time_dependent, uniforms) {
    const scene = node.scene();
    const id = node.graphNodeId();
    const id_s = `${id}`;
    if (time_dependent) {
      node.states.time_dependent.force_time_dependent();
      if (uniforms) {
        scene.uniforms_controller.add_time_dependent_uniform_owner(id_s, uniforms);
      }
    } else {
      node.states.time_dependent.unforce_time_dependent();
      scene.uniforms_controller.remove_time_dependent_uniform_owner(id_s);
    }
  }
  async render_on_target() {
    this.create_render_target_if_required();
    if (!this._render_target) {
      return;
    }
    this._renderer_controller = this._renderer_controller || new CopRendererController(this);
    const renderer = await this._renderer_controller.renderer();
    const prev_target = renderer.getRenderTarget();
    renderer.setRenderTarget(this._render_target);
    renderer.clear();
    renderer.render(this._texture_scene, this._texture_camera);
    renderer.setRenderTarget(prev_target);
    if (this._render_target.texture) {
      if (this.pv.useCameraRenderer) {
        this.set_texture(this._render_target.texture);
      } else {
        this._data_texture_controller = this._data_texture_controller || new DataTextureController2(DataTextureControllerBufferType.Float32Array);
        const data_texture = this._data_texture_controller.from_render_target(renderer, this._render_target);
        this.set_texture(data_texture);
      }
    } else {
      this.cookController.end_cook();
    }
  }
  render_target() {
    return this._render_target = this._render_target || this._create_render_target(this.pv.resolution.x, this.pv.resolution.y);
  }
  create_render_target_if_required() {
    if (!this._render_target || !this._render_target_resolution_valid()) {
      this._render_target = this._create_render_target(this.pv.resolution.x, this.pv.resolution.y);
      this._data_texture_controller?.reset();
    }
  }
  _render_target_resolution_valid() {
    if (this._render_target) {
      const image = this._render_target.texture.image;
      if (image.width != this.pv.resolution.x || image.height != this.pv.resolution.y) {
        return false;
      } else {
        return true;
      }
    } else {
      return false;
    }
  }
  _create_render_target(width, height) {
    if (this._render_target) {
      const image = this._render_target.texture.image;
      if (image.width == width && image.height == height) {
        return this._render_target;
      }
    }
    const wrapS = ClampToEdgeWrapping;
    const wrapT = ClampToEdgeWrapping;
    const minFilter = LinearFilter;
    const magFilter = NearestFilter;
    var renderTarget = new WebGLRenderTarget2(width, height, {
      wrapS,
      wrapT,
      minFilter,
      magFilter,
      format: RGBAFormat,
      type: /(iPad|iPhone|iPod)/g.test(navigator.userAgent) ? HalfFloatType : FloatType,
      stencilBuffer: false,
      depthBuffer: false
    });
    Poly2.warn("created render target", this.fullPath(), width, height);
    return renderTarget;
  }
}
