"""
Spline utilities for Catmull-Rom interpolation and trajectory sampling.
"""

import numpy as np


def catmull_rom_spline(points, num_points=100):
    """
    Calcola una Catmull-Rom spline che passa per tutti i punti dati.
    Args:
        points: lista di tuple (x, y) o (x, y, z)
        num_points: numero di punti da campionare lungo la curva
    Returns:
        Lista di punti campionati sulla spline
    """
    points = np.asarray(points, dtype=float)
    n = len(points)
    if n < 4:
        # Not enough points for a spline, return a polyline
        return points.tolist()

    # Pad the points to ensure continuity at the ends
    p_start = points[0]
    p_end = points[-1]
    extended_points = np.vstack([p_start, points, p_end])

    # Define the Catmull-Rom matrix
    C = 0.5 * np.array([
        [0, 2, 0, 0],
        [-1, 0, 1, 0],
        [2, -5, 4, -1],
        [-1, 3, -3, 1]
    ])

    result = []
    total_segments = n - 1
    if total_segments <= 0:
        return points.tolist()

    for k in range(num_points):
        s = (k / (num_points - 1)) * total_segments
        seg = int(np.floor(s))
        if seg >= total_segments:
            seg = total_segments - 1
        t = s - seg

        # Control points for the segment
        # The segment is between P1 and P2 of the control points
        # extended_points is indexed s.t. extended_points[i+1] = points[i]
        P = extended_points[seg : seg + 4]
        
        # Powers of t
        T = np.array([1, t, t**2, t**3])
        
        # Calculate the point
        pt = T @ C @ P
        result.append(pt.tolist())

    # Ensure exact endpoints match control points
    result[0] = points[0].tolist()
    result[-1] = points[-1].tolist()
    return result