using System.Globalization;
using OpenTK.Mathematics;

namespace Engine.Asset.Mesh.Loader;

public class StlMeshLoader : IMeshLoader
{
  private static readonly StlMeshLoader INSTANCE = new();

  public static Mesh Load(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
  {
    return INSTANCE.LoadMesh(parReader, parParameters);
  }

  private StlMeshLoader()
  {
  }

  public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
  {
    var mesh = new Mesh();

    var loadNormals = parParameters.HasFlag(MeshLoaderParameters.LoadNormals);

    var currentNormal = new Vector3();
    var index = 0u;

    while (parReader.ReadLine() is { } line)
    {
      line = line.Trim();

      if (string.IsNullOrWhiteSpace(line) || line.StartsWith("solid") || line.StartsWith("outer loop") ||
          line.StartsWith("endloop"))
      {
        continue;
      }

      if (line.StartsWith("endsolid"))
      {
        break;
      }

      var parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);

      switch (parts[0])
      {
        case "facet" when parts[1] == "normal" && loadNormals:
          currentNormal = new Vector3(
            float.Parse(parts[2], CultureInfo.InvariantCulture),
            float.Parse(parts[3], CultureInfo.InvariantCulture),
            float.Parse(parts[4], CultureInfo.InvariantCulture)
          );
          break;
        case "vertex":
          {
            var vertex = new Vector3(
              float.Parse(parts[1], CultureInfo.InvariantCulture),
              float.Parse(parts[2], CultureInfo.InvariantCulture),
              float.Parse(parts[3], CultureInfo.InvariantCulture)
            );

            mesh.VerticesInternal.Add(new Mesh.Vertex { _position = vertex, _normal = currentNormal });
            mesh.IndicesInternal.Add(index++);
            break;
          }
      }
    }

    if (parParameters.HasFlag(MeshLoaderParameters.Optimize))
    {
      mesh = IMeshLoader.Optimize(mesh);
    }

    return mesh;
  }
}