#pragma once

#include "MayaFlux/Nodes/Graphics/VertexSpec.hpp"

namespace MayaFlux::Kinesis {

/**
 * @brief Generate vertices along a circular path
 * @param center Circle center in 3D space
 * @param radius Circle radius
 * @param segments Number of subdivisions (must be >= 3)
 * @param normal Circle plane normal (default: XY plane, Z-up)
 * @return Vertices generated by angle θ ∈ [0, 2π)
 *
 * Generates N+1 vertices where last vertex equals first (closed curve).
 * For LINE_STRIP topology. For POINT_LIST, use N vertices.
 */
std::vector<glm::vec3> generate_circle(
    const glm::vec3& center,
    float radius,
    size_t segments,
    const glm::vec3& normal = glm::vec3(0, 0, 1));

/**
 * @brief Generate vertices along an elliptical path
 * @param center Ellipse center
 * @param semi_major Semi-major axis length
 * @param semi_minor Semi-minor axis length
 * @param segments Number of subdivisions
 * @param normal Ellipse plane normal
 * @return Vertices generated by eccentric anomaly
 */
std::vector<glm::vec3> generate_ellipse(
    const glm::vec3& center,
    float semi_major,
    float semi_minor,
    size_t segments,
    const glm::vec3& normal = glm::vec3(0, 0, 1));

/**
 * @brief Generate vertices of an axis-aligned rectangular path
 * @param center Rectangle centroid
 * @param width Extent along local X axis
 * @param height Extent along local Y axis
 * @param normal Rectangle plane normal
 * @return 5 vertices (closed rectangular path for LINE_STRIP)
 */
std::vector<glm::vec3> generate_rectangle(
    const glm::vec3& center,
    float width,
    float height,
    const glm::vec3& normal = glm::vec3(0, 0, 1));

/**
 * @brief Generate vertices of a regular n-gon
 * @param center Polygon centroid
 * @param radius Circumradius (center to vertex distance)
 * @param sides Number of sides (must be >= 3)
 * @param normal Polygon plane normal
 * @param phase_offset Angular offset in radians (default: 0)
 * @return N+1 vertices (closed path)
 *
 * Vertices positioned at angles: θ_k = (2πk/N) + phase_offset
 */
std::vector<glm::vec3> generate_regular_polygon(
    const glm::vec3& center,
    float radius,
    size_t sides,
    const glm::vec3& normal = glm::vec3(0, 0, 1),
    float phase_offset = 0.0F);

/**
 * @brief Apply rigid transformation to vertex set
 * @param vertices Vertices to transform (modified in-place)
 * @param transform Affine transformation matrix (4x4)
 */
void apply_transform(
    std::vector<glm::vec3>& vertices,
    const glm::mat4& transform);

/**
 * @brief Apply rotation to vertex set around arbitrary axis
 * @param vertices Vertices to transform (modified in-place)
 * @param axis Rotation axis (will be normalized)
 * @param angle Rotation angle in radians (right-hand rule)
 * @param origin Rotation pivot (default: coordinate origin)
 */
void apply_rotation(
    std::vector<glm::vec3>& vertices,
    const glm::vec3& axis,
    float angle,
    const glm::vec3& origin = glm::vec3(0));

/**
 * @brief Apply translation to vertex set
 * @param vertices Vertices to transform (modified in-place)
 * @param displacement Translation vector
 */
void apply_translation(
    std::vector<glm::vec3>& vertices,
    const glm::vec3& displacement);

/**
 * @brief Apply uniform scaling to vertex set
 * @param vertices Vertices to transform (modified in-place)
 * @param scale Scale factor (must be > 0)
 * @param origin Scaling pivot (default: coordinate origin)
 */
void apply_uniform_scale(
    std::vector<glm::vec3>& vertices,
    float scale,
    const glm::vec3& origin = glm::vec3(0));

/**
 * @brief Apply non-uniform scaling to vertex set
 * @param vertices Vertices to transform (modified in-place)
 * @param scale Per-axis scale factors
 * @param origin Scaling center
 */
void apply_scale(
    std::vector<glm::vec3>& vertices,
    const glm::vec3& scale,
    const glm::vec3& origin = glm::vec3(0));

/**
 * @brief Compute normal vectors along a piecewise-linear path
 * @param path_vertices Sequential vertices defining curve
 * @param normal_length Magnitude of normal vectors
 * @param stride Sample every stride-th vertex (default: 1)
 * @return Line segments (pairs) representing normals (for LINE_LIST topology)
 *
 * Normal at vertex i: perpendicular to tangent (v[i+1] - v[i])
 * Returned as pairs: [midpoint - normal/2, midpoint + normal/2]
 */
std::vector<Nodes::LineVertex> compute_path_normals(
    const std::vector<Nodes::LineVertex>& path_vertices,
    float normal_length,
    size_t stride = 1);

/**
 * @brief Compute tangent vectors along a piecewise-linear path
 * @param path_vertices Sequential vertices defining curve
 * @param tangent_length Magnitude of tangent vectors
 * @param stride Sample every stride-th vertex
 * @return Line segments (pairs) representing tangents (for LINE_LIST topology)
 *
 * Tangent at vertex i: direction (v[i+1] - v[i])
 * Returned as pairs: [vertex - tangent/2, vertex + tangent/2]
 */
std::vector<Nodes::LineVertex> compute_path_tangents(
    const std::vector<Nodes::LineVertex>& path_vertices,
    float tangent_length,
    size_t stride = 1);

/**
 * @brief Compute curvature vectors along a path (2nd derivative approximation)
 * @param path_vertices Sequential vertices defining curve
 * @param curvature_scale Magnitude scaling factor
 * @param stride Sample every stride-th vertex
 * @return Line segments representing discrete curvature
 *
 * Curvature at i approximated by: (v[i+1] - 2*v[i] + v[i-1])
 */
std::vector<Nodes::LineVertex> compute_path_curvature(
    const std::vector<Nodes::LineVertex>& path_vertices,
    float curvature_scale,
    size_t stride = 1);

/**
 * @brief Sample parametric curve uniformly in parameter space
 * @param curve Parametric function: [0,1] → ℝ³
 * @param samples Number of samples
 * @return Vertices sampled at t_k = k/(N-1) for k ∈ [0, N-1]
 */
std::vector<glm::vec3> sample_parametric_curve(
    const std::function<glm::vec3(float)>& curve,
    size_t samples);

/**
 * @brief Resample path vertices for arc-length parameterization
 * @param path_vertices Input vertices (arbitrary spacing)
 * @param num_samples Desired output sample count
 * @return Vertices uniformly distributed by arc length
 *
 * Uses piecewise linear arc length estimation.
 * Output vertices have constant spacing along curve.
 */
std::vector<Nodes::LineVertex> reparameterize_by_arc_length(
    const std::vector<Nodes::LineVertex>& path_vertices,
    size_t num_samples);

/**
 * @brief Project vertices onto plane defined by normal
 * @param vertices Vertices to project (modified in-place)
 * @param plane_point Point on projection plane
 * @param plane_normal Plane normal vector
 */
void project_onto_plane(
    std::vector<glm::vec3>& vertices,
    const glm::vec3& plane_point,
    const glm::vec3& plane_normal);

/**
 * @brief Compute convex hull of vertex set (2D projection)
 * @param vertices Input vertex set
 * @param projection_normal Normal of projection plane (default: Z-axis)
 * @return Vertices forming convex hull boundary (closed path)
 *
 * Uses Graham scan on projected coordinates.
 */
std::vector<glm::vec3> compute_convex_hull_2d(
    const std::vector<glm::vec3>& vertices,
    const glm::vec3& projection_normal = glm::vec3(0, 0, 1));

/**
 * @brief Apply color interpolation to position vertices
 * @param positions Input position vertices
 * @param colors Color stops for interpolation
 * @param color_positions Normalized positions [0,1] for each color stop
 * @param default_thickness Line thickness for all vertices (default: 1.0)
 * @return LineVertex array with interpolated colors
 *
 * Example:
 *   colors = {red, blue}
 *   color_positions = {0.0, 1.0}
 *   → Linear gradient from red to blue along path
 *
 * If color_positions.empty(), distributes colors uniformly.
 */
std::vector<Nodes::LineVertex> apply_color_gradient(
    const std::vector<glm::vec3>& positions,
    const std::vector<glm::vec3>& colors,
    const std::vector<float>& color_positions = {},
    float default_thickness = 1.0F);

/**
 * @brief Apply uniform color to position vertices
 */
std::vector<Nodes::LineVertex> apply_uniform_color(
    const std::vector<glm::vec3>& positions,
    const glm::vec3& color,
    float default_thickness = 1.0F);

/**
 * @brief Convert positions to LineVertex with per-vertex colors
 */
std::vector<Nodes::LineVertex> apply_vertex_colors(
    const std::vector<glm::vec3>& positions,
    const std::vector<glm::vec3>& colors,
    float default_thickness = 1.0F);

/**
 * @struct QuadGeometry
 * @brief Textured quad vertex data together with its semantic layout descriptor.
 *
 * Returned by generate_quad(). Callers memcpy vertices into their staging
 * buffer and forward layout to set_vertex_layout().
 */
struct QuadGeometry {
    std::array<Nodes::TextureQuadVertex, 4> vertices;
    Kakshya::VertexLayout layout;
};

/**
 * @brief Generate a textured quad centred on the origin.
 * @param position Translation in XY (z remains 0). Default: no translation.
 * @param scale Extents in XY before rotation. Default: {1,1} → NDC-spanning quad.
 * @param rotation Rotation angle in radians around Z axis. Default: 0.
 * @return QuadGeometry ready for TRIANGLE_STRIP draw.
 *
 * UV origin is bottom-left (0,1) to match Vulkan image layout.
 * Base vertices sit at ±scale before rotation and translation are applied.
 */
[[nodiscard]] QuadGeometry generate_quad(
    glm::vec2 position = glm::vec2(0.0F),
    glm::vec2 scale = glm::vec2(1.0F),
    float rotation = 0.0F);

} // namespace MayaFlux::Kinesis
