import {BaseSopOperation} from "./_Base";
import {TypedNodePathParamValue} from "../../Walker";
import {NodeContext as NodeContext2} from "../../../engine/poly/NodeContext";
import {CoreMaterial} from "../../geometry/Material";
import {GlobalsGeometryHandler} from "../../../engine/nodes/gl/code/globals/Geometry";
import {InputCloneMode as InputCloneMode2} from "../../../engine/poly/InputCloneMode";
import {ShaderMaterial as ShaderMaterial2} from "three/src/materials/ShaderMaterial";
import {CoreObject} from "../../geometry/Object";
export class MaterialSopOperation extends BaseSopOperation {
  constructor() {
    super(...arguments);
    this._globals_handler = new GlobalsGeometryHandler();
    this._old_mat_by_old_new_id = new Map();
    this._materials_by_uuid = new Map();
  }
  static type() {
    return "material";
  }
  async cook(input_contents, params) {
    const core_group = input_contents[0];
    this._old_mat_by_old_new_id.clear();
    await this._apply_materials(core_group, params);
    this._swap_textures(core_group, params);
    return core_group;
  }
  async _apply_materials(core_group, params) {
    if (!params.assignMat) {
      return;
    }
    const material_node = params.material.ensure_node_context(NodeContext2.MAT, this.states?.error);
    if (material_node) {
      const material = material_node.material;
      const assembler_controller = material_node.assemblerController;
      if (assembler_controller) {
        assembler_controller.set_assembler_globals_handler(this._globals_handler);
      }
      await material_node.requestContainer();
      if (material) {
        if (params.applyToChildren) {
          for (let object of core_group.objects()) {
            object.traverse((grand_child) => {
              this._apply_material(grand_child, material, params);
            });
          }
        } else {
          for (let object of core_group.objectsFromGroup(params.group)) {
            this._apply_material(object, material, params);
          }
        }
        return core_group;
      } else {
        this.states?.error.set(`material invalid. (error: '${material_node.states.error.message()}')`);
      }
    } else {
      this.states?.error.set(`no material node found`);
    }
  }
  _swap_textures(core_group, params) {
    if (!params.swapCurrentTex) {
      return;
    }
    this._materials_by_uuid.clear();
    for (let object of core_group.objectsFromGroup(params.group)) {
      if (params.applyToChildren) {
        object.traverse((child) => {
          const mat = object.material;
          this._materials_by_uuid.set(mat.uuid, mat);
        });
      } else {
        const mat = object.material;
        this._materials_by_uuid.set(mat.uuid, mat);
      }
    }
    this._materials_by_uuid.forEach((mat, mat_uuid) => {
      this._swap_texture(mat, params);
    });
  }
  _apply_material(object, src_material, params) {
    if (params.group) {
      if (!CoreObject.isInGroup(params.group, object)) {
        return;
      }
    }
    const used_material = params.cloneMat ? CoreMaterial.clone(src_material) : src_material;
    if (src_material instanceof ShaderMaterial2 && used_material instanceof ShaderMaterial2) {
      for (let uniform_name in src_material.uniforms) {
        used_material.uniforms[uniform_name] = src_material.uniforms[uniform_name];
      }
    }
    const object_with_material = object;
    this._old_mat_by_old_new_id.set(used_material.uuid, object_with_material.material);
    object_with_material.material = used_material;
    CoreMaterial.apply_render_hook(object, used_material);
    CoreMaterial.apply_custom_materials(object, used_material);
  }
  _swap_texture(target_mat, params) {
    if (params.texSrc0 == "" || params.texDest0 == "") {
      return;
    }
    let src_mat = this._old_mat_by_old_new_id.get(target_mat.uuid);
    src_mat = src_mat || target_mat;
    const src_tex = src_mat[params.texSrc0];
    if (src_tex) {
      target_mat[params.texDest0] = src_tex;
      const uniforms = target_mat.uniforms;
      if (uniforms) {
        const uniforms_map = uniforms[params.texDest0];
        if (uniforms_map) {
          uniforms[params.texDest0] = {value: src_tex};
        }
      }
    }
  }
}
MaterialSopOperation.DEFAULT_PARAMS = {
  group: "",
  assignMat: true,
  material: new TypedNodePathParamValue("/MAT/mesh_standard1"),
  applyToChildren: true,
  cloneMat: false,
  shareUniforms: true,
  swapCurrentTex: false,
  texSrc0: "emissiveMap",
  texDest0: "map"
};
MaterialSopOperation.INPUT_CLONED_STATE = InputCloneMode2.FROM_NODE;
