import { am as Y, bR as h, V as E, bH as p, H as U, Q as R, ay as b, cW as V, c0 as D } from "./chunk-CrnJfeNw.js";
import { B as g } from "./chunk-DfdFCrVY.js";
const O = "Xposition", F = "Yposition", k = "Zposition", x = "Xrotation", v = "Yrotation", M = "Zrotation", j = "HIERARCHY", K = "MOTION";
class X {
  constructor(t) {
    this.loopMode = h.ANIMATIONLOOPMODE_CYCLE, this.list = [], this.root = _(), this.numFrames = 0, this.frameRate = 0, this.skeleton = t;
  }
}
function _() {
  return {
    name: "",
    type: "",
    offset: new E(),
    channels: [],
    children: [],
    frames: [],
    parent: null
  };
}
function Z() {
  return {
    frame: 0,
    position: new E(),
    rotation: new R()
  };
}
function z(n) {
  const t = n.offset.x, o = n.offset.y, i = n.offset.z;
  return p.Translation(t, o, i);
}
function Q(n, t) {
  if (n.frames.length === 0)
    return [];
  const o = [], i = n.channels.some((f) => f === O || f === F || f === k), e = n.channels.some((f) => f === x || f === v || f === M), r = new h(`${n.name}_pos`, "position", t.frameRate, h.ANIMATIONTYPE_VECTOR3, t.loopMode), a = new h(`${n.name}_rot`, "rotationQuaternion", t.frameRate, h.ANIMATIONTYPE_QUATERNION, t.loopMode), s = [], c = [];
  for (let f = 0; f < n.frames.length; f++) {
    const l = n.frames[f];
    i && l.position && s.push({
      frame: l.frame,
      value: l.position.clone()
    }), e && c.push({
      frame: l.frame,
      value: l.rotation.clone()
    });
  }
  return s.length > 0 && (r.setKeys(s), o.push(r)), c.length > 0 && (a.setKeys(c), o.push(a)), o;
}
function P(n, t, o) {
  const i = z(n), e = new b(n.name, o.skeleton, t, i), r = Q(n, o);
  for (const a of r)
    a.getKeys() && a.getKeys().length > 0 && e.animations.push(a);
  for (const a of n.children)
    P(a, e, o);
}
function B(n, t, o, i) {
  if (o.type === "ENDSITE")
    return;
  const e = Z();
  e.frame = t, e.position = new E(), e.rotation = new R(), o.frames.push(e);
  let r = p.Identity();
  for (let a = 0; a < o.channels.length; ++a) {
    const s = o.channels[a], c = n[i.i++];
    if (!c)
      continue;
    const f = parseFloat(c.trim());
    if (s.endsWith("position"))
      switch (s) {
        case O:
          e.position.x = f;
          break;
        case F:
          e.position.y = f;
          break;
        case k:
          e.position.z = f;
          break;
      }
    else if (s.endsWith("rotation")) {
      const l = U.ToRadians(f);
      let m;
      switch (s) {
        case x:
          m = p.RotationX(l);
          break;
        case v:
          m = p.RotationY(l);
          break;
        case M:
          m = p.RotationZ(l);
          break;
      }
      r = m.multiply(r);
    }
  }
  R.FromRotationMatrixToRef(r, e.rotation);
  for (const a of o.children)
    B(n, t, a, i);
}
function L(n, t, o, i) {
  const e = _();
  e.parent = o, i.list.push(e);
  let r = t.trim().split(/\s+/);
  if (r[0].toUpperCase() === "END" && r[1].toUpperCase() === "SITE" ? (e.type = "ENDSITE", e.name = "ENDSITE") : (e.name = r[1], e.type = r[0].toUpperCase()), n.shift()?.trim() != "{")
    throw new Error("Expected opening { after type & name");
  const a = n.shift()?.trim().split(/\s+/);
  if (!a)
    throw new Error("Unexpected end of file: missing OFFSET");
  if (r = a, r[0].toUpperCase() != "OFFSET")
    throw new Error("Expected OFFSET, but got: " + r[0]);
  if (r.length != 4)
    throw new Error("OFFSET: Invalid number of values");
  const s = new E(parseFloat(r[1]), parseFloat(r[2]), parseFloat(r[3]));
  if (isNaN(s.x) || isNaN(s.y) || isNaN(s.z))
    throw new Error("OFFSET: Invalid values");
  if (e.offset = s, e.type != "ENDSITE") {
    if (r = n.shift()?.trim().split(/\s+/), !r)
      throw new Error("Unexpected end of file: missing CHANNELS");
    if (r[0].toUpperCase() != "CHANNELS")
      throw new Error("Expected CHANNELS definition");
    const c = parseInt(r[1]);
    e.channels = r.splice(2, c), e.children = [];
  }
  for (; n.length > 0; ) {
    const c = n.shift()?.trim();
    if (c === "}")
      return e;
    c && e.children.push(L(n, c, e, i));
  }
  throw new Error("Unexpected end of file: missing closing brace");
}
function A(n, t, o, i) {
  const e = n.split(`
`), { loopMode: r } = i;
  t._blockEntityCollection = !!o;
  const a = new Y("", "", t);
  a._parentContainer = o, t._blockEntityCollection = !1;
  const s = new X(a);
  s.loopMode = r;
  const c = e.shift();
  if (!c || c.trim().toUpperCase() !== j)
    throw new Error("HIERARCHY expected");
  const f = e.shift();
  if (!f)
    throw new Error("Unexpected end of file after HIERARCHY");
  const l = L(e, f.trim(), null, s), m = e.shift();
  if (!m || m.trim().toUpperCase() !== K)
    throw new Error("MOTION expected");
  const H = e.shift();
  if (!H)
    throw new Error("Unexpected end of file before frame count");
  const y = H.trim().split(/[\s]+/);
  if (y.length < 2)
    throw new Error("Invalid frame count line");
  const w = parseInt(y[1]);
  if (isNaN(w))
    throw new Error("Failed to read number of frames.");
  s.numFrames = w;
  const C = e.shift();
  if (!C)
    throw new Error("Unexpected end of file before frame time");
  const I = C.trim().split(/[\s]+/);
  if (I.length < 3)
    throw new Error("Invalid frame time line");
  const u = parseFloat(I[2]);
  if (isNaN(u))
    throw new Error("Failed to read frame time.");
  if (u <= 0)
    throw new Error("Failed to read frame time. Invalid value " + u);
  s.frameRate = 1 / u;
  for (let N = 0; N < w; ++N) {
    const T = e.shift();
    if (!T)
      continue;
    const S = T.trim().split(/[\s]+/) || [];
    B(S, N, l, { i: 0 });
  }
  return s.root = l, P(s.root, null, s), s.skeleton.returnToRest(), s.skeleton;
}
class d {
  /**
   * Creates loader for bvh motion files
   * @param loadingOptions - Options for the bvh loader
   */
  constructor(t) {
    this.name = g.name, this.extensions = g.extensions, this._loadingOptions = { ...d._DefaultLoadingOptions, ...t ?? {} };
  }
  static get _DefaultLoadingOptions() {
    return {
      loopMode: h.ANIMATIONLOOPMODE_CYCLE
    };
  }
  /** @internal */
  createPlugin(t) {
    return new d(t[g.name]);
  }
  /**
   * If the data string can be loaded directly.
   * @param data - direct load data
   * @returns if the data can be loaded directly
   */
  canDirectLoad(t) {
    return this.isBvhHeader(t);
  }
  isBvhHeader(t) {
    return t.split(`
`)[0] == "HIERARCHY";
  }
  isNotBvhHeader(t) {
    return !this.isBvhHeader(t);
  }
  /**
   * Imports  from the loaded gaussian splatting data and adds them to the scene
   * @param _meshesNames a string or array of strings of the mesh names that should be loaded from the file
   * @param scene the scene the meshes should be added to
   * @param data the bvh data to load
   * @returns a promise containing the loaded skeletons and animations
   */
  // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax
  importMeshAsync(t, o, i) {
    if (typeof i != "string")
      return Promise.reject("BVH loader expects string data.");
    if (this.isNotBvhHeader(i))
      return Promise.reject("BVH loader expects HIERARCHY header.");
    try {
      const e = A(i, o, null, this._loadingOptions);
      return Promise.resolve({
        meshes: [],
        particleSystems: [],
        skeletons: [e],
        animationGroups: [],
        transformNodes: [],
        geometries: [],
        lights: [],
        spriteManagers: []
      });
    } catch (e) {
      return Promise.reject(e);
    }
  }
  /**
   * Imports all objects from the loaded bvh data and adds them to the scene
   * @param scene the scene the objects should be added to
   * @param data the bvh data to load
   * @returns a promise which completes when objects have been loaded to the scene
   */
  // eslint-disable-next-line no-restricted-syntax, @typescript-eslint/promise-function-async
  loadAsync(t, o) {
    return typeof o != "string" ? Promise.reject("BVH loader expects string data.") : this.isNotBvhHeader(o) ? Promise.reject("BVH loader expects HIERARCHY header.") : this.importMeshAsync(null, t, o).then(() => {
    });
  }
  /**
   * Load into an asset container.
   * @param scene The scene to load into
   * @param data The data to import
   * @returns The loaded asset container
   */
  // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax
  loadAssetContainerAsync(t, o) {
    if (typeof o != "string")
      return Promise.reject("BVH loader expects string data.");
    if (this.isNotBvhHeader(o))
      return Promise.reject("BVH loader expects HIERARCHY header.");
    const i = new V(t);
    try {
      const e = A(o, t, i, this._loadingOptions);
      return i.skeletons.push(e), Promise.resolve(i);
    } catch (e) {
      return Promise.reject(e);
    }
  }
}
D(new d());
export {
  d as BVHFileLoader
};