using System.Reflection;
using System.Runtime.InteropServices;
using OpenTK.Graphics.OpenGL;

namespace Engine.Graphics.Buffer.Vertex;

public interface IVertex
{
  public static IOrderedEnumerable<FieldInfo> GetFields<T>()
  {
    return GetFields(typeof(T));
  }

  public static IOrderedEnumerable<FieldInfo> GetFields(Type parType)
  {
    return parType.GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(parF => parF.MetadataToken);
  }

  public static bool IsValid(Type parType)
  {
    if (!parType.IsValueType || !parType.IsAssignableTo(typeof(IVertex)))
    {
      return false;
    }

    var fields = GetFields(parType);
    var totalSize = 0;
    foreach (var field in fields)
    {
      if (!field.FieldType.IsValueType)
      {
        return false;
      }

      var attribute = field.GetCustomAttribute<VertexAttribute>();
      if (attribute == null)
      {
        return false;
      }

      var size = AttributeSize(attribute.Type) * attribute.ComponentCount * attribute.RepeatCount;
      if (size != Marshal.SizeOf(field.FieldType))
      {
        return false;
      }

      totalSize += size;
    }

    return totalSize == Marshal.SizeOf(parType);
  }

  public static int AttributeSize(VertexAttribType parType)
  {
    return parType switch
    {
      VertexAttribType.Byte or VertexAttribType.UnsignedByte => sizeof(byte),
      VertexAttribType.Short or VertexAttribType.UnsignedShort => sizeof(short),
      VertexAttribType.Int or VertexAttribType.UnsignedInt => sizeof(int),
      VertexAttribType.HalfFloat => Marshal.SizeOf<Half>(),
      VertexAttribType.Float => sizeof(float),
      VertexAttribType.Double => sizeof(double),
      _ => 0
    };
  }
}