/**
 * @file vertex.hpp
 * @brief Defines the vertex structure and its associated utilities for 3D rendering.
 *
 * This file contains the Vertex structure used for 3D model representation in the Vulkan
 * graphics pipeline. It includes position, color, texture coordinate, and normal data, along with
 * Vulkan-specific descriptors for vertex input handling.
 */

#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtx/hash.hpp>

#define VULKAN_HPP_NO_CONSTRUCTORS
#include <vulkan/vulkan.hpp>

#include "types.hpp"

/**
 * @brief Represents a vertex in 3D space with color, texture, and normal information.
 *
 * This structure defines a vertex with all its attributes required for rendering,
 * including position in 3D space, RGB color, texture coordinates, and normal vector. It also
 * provides methods for Vulkan vertex input configuration.
 */
struct Vertex {
  glm::vec3 pos;       ///< Position of the vertex in 3D space (x, y, z)
  glm::vec3 color;     ///< RGB color values, each component in range [0, 1]
  glm::vec2 tex_coord; ///< Texture coordinates (u, v) for texture mapping
  glm::vec3 normal;    ///< Normal vector of the vertex

  /**
   * @brief Provides the vertex binding description for Vulkan.
   *
   * @return vk::VertexInputBindingDescription Describes how to bind vertex data to GPU memory.
   *
   * The binding description specifies:
   * - Binding index (0)
   * - Stride (size of one vertex)
   * - Input rate (per-vertex data)
   */
  static fn getBindingDescription() -> vk::VertexInputBindingDescription {
    return vk::VertexInputBindingDescription {
      .binding   = 0,
      .stride    = sizeof(Vertex),
      .inputRate = vk::VertexInputRate::eVertex,
    };
  }

  /**
   * @brief Provides attribute descriptions for vertex data interpretation.
   *
   * @return std::array<vk::VertexInputAttributeDescription, 4> Array of descriptions for position, color,
   * texture coordinates, and normal.
   *
   * The attribute descriptions specify:
   * - Location indices (0 for position, 1 for color, 2 for texture coordinates, 3 for normal)
   * - Binding point (0)
   * - Data format (R32G32B32 for vec3, R32G32 for vec2)
   * - Offset of each attribute in the vertex structure
   */
  static fn getAttributeDescriptions() -> std::array<vk::VertexInputAttributeDescription, 4> {
    return {
      vk::VertexInputAttributeDescription {
                                           .location = 0,
                                           .binding  = 0,
                                           .format   = vk::Format::eR32G32B32Sfloat,
                                           .offset   = offsetof(Vertex,       pos),
                                           },
      vk::VertexInputAttributeDescription {
                                           .location = 1,
                                           .binding  = 0,
                                           .format   = vk::Format::eR32G32B32Sfloat,
                                           .offset   = offsetof(Vertex,     color),
                                           },
      vk::VertexInputAttributeDescription {
                                           .location = 2,
                                           .binding  = 0,
                                           .format   = vk::Format::eR32G32Sfloat,
                                           .offset   = offsetof(Vertex, tex_coord),
                                           },
      vk::VertexInputAttributeDescription {
                                           .location = 3,
                                           .binding  = 0,
                                           .format   = vk::Format::eR32G32B32Sfloat,
                                           .offset   = offsetof(Vertex,    normal),
                                           },
    };
  }

  /**
   * @brief Compares two vertices for equality.
   *
   * @param other The vertex to compare with.
   * @return bool True if vertices are identical in position, color, texture coordinates, and normal.
   */
  fn operator==(const Vertex& other) const->bool {
    return pos == other.pos && color == other.color && tex_coord == other.tex_coord && normal == other.normal;
  }
};

namespace std {
  /**
   * @brief Hash function specialization for Vertex type.
   *
   * This specialization allows Vertex objects to be used as keys in unordered containers.
   * The hash combines position, color, texture coordinate, and normal data using bit operations
   * to create a unique hash value.
   */
  template <>
  struct hash<Vertex> {
    /**
     * @brief Computes hash value for a vertex.
     *
     * @param vertex The vertex to hash.
     * @return size_t Hash value combining all vertex attributes.
     */
    fn operator()(Vertex const& vertex) const->size_t {
      return ((hash<glm::vec3>()(vertex.pos) ^ (hash<glm::vec3>()(vertex.color) << 1)) >> 1) ^
             (hash<glm::vec2>()(vertex.tex_coord) << 1) ^ (hash<glm::vec3>()(vertex.normal) << 2);
    }
  };
}
