import { Mesh } from "../../../../../../../three@0.154.0/node_modules/three/build/three.module.js";
import { Material, $setActive, $variantSet } from "./material.js";
import { PrimitiveNode, Node } from "./nodes/primitive-node.js";
import { $correlatedObjects } from "./three-dom-element.js";
/* @license
 * Copyright 2020 Google LLC. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var _a, _b, _c, _d, _e, _f;
const $materials = Symbol("materials");
const $hierarchy = Symbol("hierarchy");
const $roots = Symbol("roots");
const $primitivesList = Symbol("primitives");
const $prepareVariantsForExport = Symbol("prepareVariantsForExport");
const $switchVariant = Symbol("switchVariant");
const $materialFromPoint = Symbol("materialFromPoint");
const $nodeFromPoint = Symbol("nodeFromPoint");
const $nodeFromIndex = Symbol("nodeFromIndex");
const $variantData = Symbol("variantData");
const $availableVariants = Symbol("availableVariants");
const $modelOnUpdate = Symbol("modelOnUpdate");
const $cloneMaterial = Symbol("cloneMaterial");
class LazyLoader {
  constructor(gltf, gltfElementMap, mapKey, doLazyLoad) {
    this.gltf = gltf;
    this.gltfElementMap = gltfElementMap;
    this.mapKey = mapKey;
    this.doLazyLoad = doLazyLoad;
  }
}
class Model {
  constructor(correlatedSceneGraph, onUpdate = () => {
  }) {
    this[_a] = new Array();
    this[_b] = new Array();
    this[_c] = new Array();
    this[_d] = new Array();
    this[_e] = () => {
    };
    this[_f] = /* @__PURE__ */ new Map();
    this[$modelOnUpdate] = onUpdate;
    const { gltf, threeGLTF, gltfElementMap } = correlatedSceneGraph;
    for (const [i, material] of gltf.materials.entries()) {
      const correlatedMaterial = gltfElementMap.get(material);
      if (correlatedMaterial != null) {
        this[$materials].push(new Material(onUpdate, i, true, this[$variantData], correlatedMaterial, material.name));
      } else {
        const elementArray = gltf["materials"] || [];
        const gltfMaterialDef = elementArray[i];
        const capturedMatIndex = i;
        const materialLoadCallback = async () => {
          const threeMaterial = await threeGLTF.parser.getDependency("material", capturedMatIndex);
          const threeMaterialSet = /* @__PURE__ */ new Set();
          gltfElementMap.set(gltfMaterialDef, threeMaterialSet);
          threeMaterialSet.add(threeMaterial);
          return { set: threeMaterialSet, material: threeMaterial };
        };
        this[$materials].push(new Material(onUpdate, i, false, this[$variantData], correlatedMaterial, material.name, new LazyLoader(gltf, gltfElementMap, gltfMaterialDef, materialLoadCallback)));
      }
    }
    const parentMap = /* @__PURE__ */ new Map();
    const nodeStack = new Array();
    for (const object of threeGLTF.scene.children) {
      nodeStack.push(object);
    }
    while (nodeStack.length > 0) {
      const object = nodeStack.pop();
      let node = null;
      if (object instanceof Mesh) {
        node = new PrimitiveNode(object, this.materials, this[$variantData], correlatedSceneGraph);
        this[$primitivesList].push(node);
      } else {
        node = new Node(object.name);
      }
      const parent = parentMap.get(object);
      if (parent != null) {
        parent.children.push(node);
      } else {
        this[$roots].push(node);
      }
      this[$hierarchy].push(node);
      for (const child of object.children) {
        nodeStack.push(child);
        parentMap.set(object, node);
      }
    }
  }
  /**
   * Materials are listed in the order of the GLTF materials array, plus a
   * default material at the end if one is used.
   *
   * TODO(#1003): How do we handle non-active scenes?
   */
  get materials() {
    return this[$materials];
  }
  [(_a = $materials, _b = $hierarchy, _c = $roots, _d = $primitivesList, _e = $modelOnUpdate, _f = $variantData, $availableVariants)]() {
    const variants = Array.from(this[$variantData].values());
    variants.sort((a, b) => {
      return a.index - b.index;
    });
    return variants.map((data) => {
      return data.name;
    });
  }
  getMaterialByName(name) {
    const matches = this[$materials].filter((material) => {
      return material.name === name;
    });
    if (matches.length > 0) {
      return matches[0];
    }
    return null;
  }
  [$nodeFromIndex](mesh, primitive) {
    const found = this[$hierarchy].find((node) => {
      if (node instanceof PrimitiveNode) {
        const { meshes, primitives } = node.mesh.userData.associations;
        if (meshes == mesh && primitives == primitive) {
          return true;
        }
      }
      return false;
    });
    return found == null ? null : found;
  }
  [$nodeFromPoint](hit) {
    return this[$hierarchy].find((node) => {
      if (node instanceof PrimitiveNode) {
        const primitive = node;
        if (primitive.mesh === hit.object) {
          return true;
        }
      }
      return false;
    });
  }
  /**
   * Intersects a ray with the Model and returns the first material whose
   * object was intersected.
   */
  [$materialFromPoint](hit) {
    return this[$nodeFromPoint](hit).getActiveMaterial();
  }
  /**
   * Switches model variant to the variant name provided, or switches to
   * default/initial materials if 'null' is provided.
   */
  async [$switchVariant](variantName) {
    for (const primitive of this[$primitivesList]) {
      await primitive.enableVariant(variantName);
    }
    for (const material of this.materials) {
      material[$setActive](false);
    }
    for (const primitive of this[$primitivesList]) {
      this.materials[primitive.getActiveMaterial().index][$setActive](true);
    }
  }
  async [$prepareVariantsForExport]() {
    const promises = new Array();
    for (const primitive of this[$primitivesList]) {
      promises.push(primitive.instantiateVariants());
    }
    await Promise.all(promises);
  }
  [$cloneMaterial](index, newMaterialName) {
    const material = this.materials[index];
    if (!material.isLoaded) {
      console.error(`Cloning an unloaded material,
           call 'material.ensureLoaded() before cloning the material.`);
    }
    const threeMaterialSet = material[$correlatedObjects];
    const clonedSet = /* @__PURE__ */ new Set();
    for (const [i, threeMaterial] of threeMaterialSet.entries()) {
      const clone = threeMaterial.clone();
      clone.name = newMaterialName + (threeMaterialSet.size > 1 ? "_inst" + i : "");
      clonedSet.add(clone);
    }
    const clonedMaterial = new Material(
      this[$modelOnUpdate],
      this[$materials].length,
      false,
      // Cloned as inactive.
      this[$variantData],
      clonedSet,
      newMaterialName
    );
    this[$materials].push(clonedMaterial);
    return clonedMaterial;
  }
  createMaterialInstanceForVariant(originalMaterialIndex, newMaterialName, variantName, activateVariant = true) {
    let variantMaterialInstance = null;
    for (const primitive of this[$primitivesList]) {
      const variantData = this[$variantData].get(variantName);
      if (variantData != null && primitive.variantInfo.has(variantData.index)) {
        continue;
      }
      if (primitive.getMaterial(originalMaterialIndex) == null) {
        continue;
      }
      if (!this.hasVariant(variantName)) {
        this.createVariant(variantName);
      }
      if (variantMaterialInstance == null) {
        variantMaterialInstance = this[$cloneMaterial](originalMaterialIndex, newMaterialName);
      }
      primitive.addVariant(variantMaterialInstance, variantName);
    }
    if (activateVariant && variantMaterialInstance != null) {
      variantMaterialInstance[$setActive](true);
      this.materials[originalMaterialIndex][$setActive](false);
      for (const primitive of this[$primitivesList]) {
        primitive.enableVariant(variantName);
      }
    }
    return variantMaterialInstance;
  }
  createVariant(variantName) {
    if (!this[$variantData].has(variantName)) {
      this[$variantData].set(variantName, { name: variantName, index: this[$variantData].size });
    } else {
      console.warn(`Variant '${variantName}'' already exists`);
    }
  }
  hasVariant(variantName) {
    return this[$variantData].has(variantName);
  }
  setMaterialToVariant(materialIndex, targetVariantName) {
    if (this[$availableVariants]().find((name) => name === targetVariantName) == null) {
      console.warn(`Can't add material to '${targetVariantName}', the variant does not exist.'`);
      return;
    }
    if (materialIndex < 0 || materialIndex >= this.materials.length) {
      console.error(`setMaterialToVariant(): materialIndex is out of bounds.`);
      return;
    }
    for (const primitive of this[$primitivesList]) {
      const material = primitive.getMaterial(materialIndex);
      if (material != null) {
        primitive.addVariant(material, targetVariantName);
      }
    }
  }
  updateVariantName(currentName, newName) {
    const variantData = this[$variantData].get(currentName);
    if (variantData == null) {
      return;
    }
    variantData.name = newName;
    this[$variantData].set(newName, variantData);
    this[$variantData].delete(currentName);
  }
  deleteVariant(variantName) {
    const variant = this[$variantData].get(variantName);
    if (variant == null) {
      return;
    }
    for (const material of this.materials) {
      if (material.hasVariant(variantName)) {
        material[$variantSet].delete(variant.index);
      }
    }
    for (const primitive of this[$primitivesList]) {
      primitive.deleteVariant(variant.index);
    }
    this[$variantData].delete(variantName);
  }
}
export {
  $availableVariants,
  $materialFromPoint,
  $materials,
  $nodeFromIndex,
  $nodeFromPoint,
  $prepareVariantsForExport,
  $primitivesList,
  $switchVariant,
  $variantData,
  LazyLoader,
  Model
};
//# sourceMappingURL=model.js.map
