"""Cylinder implementation used as airfoil geometry."""

import numpy as np

from buffalo_wings.internal.numeric import as_float_array
from buffalo_wings.type_aliases import FloatArray, FloatInput

from .airfoil import Airfoil


class Cylinder(Airfoil):
    """Cylindrical 2D airfoil."""

    def __init__(self, radius: float) -> None:
        """
        Initialize the cylinder radius.

        Parameters
        ----------
        radius : float
            Radius of the cylinder.
        """
        super().__init__()
        self._r = radius

    @property
    def radius(self) -> float:
        """
        Return the cylinder radius.

        Returns
        -------
        float
            Radius of the cylinder.
        """
        return self._r

    @radius.setter
    def radius(self, radius: float) -> None:
        """
        Set the cylinder radius.

        Parameters
        ----------
        radius : float
            New cylinder radius.
        """
        self._r = radius

    def xy(self, t: FloatInput) -> tuple[FloatArray, FloatArray]:
        """
        Calculate the coordinates of geometry at parameter location.

        Notes
        -----
        Parameter goes from -1 (trailing edge lower surface) to +1 (trailing
        edge upper surface) with 0 representing the leading edge.

        Parameters
        ----------
        t : numpy.ndarray
            Parameter for desired locations.

        Returns
        -------
        numpy.ndarray
            X-coordinate of point.
        numpy.ndarray
            Y-coordinate of point.
        """
        theta = self._convert_theta(t)
        x = self.radius * (1 + np.cos(theta))
        y = self.radius * np.sin(theta)
        return x, y

    def xy_t(self, t: FloatInput) -> tuple[FloatArray, FloatArray]:
        """
        Calculate rates of change of the coordinates at parameter location.

        Notes
        -----
        Parameter goes from -1 (trailing edge lower surface) to +1 (trailing
        edge upper surface) with 0 representing the leading edge.

        Parameters
        ----------
        t : numpy.ndarray
            Parameter for desired locations.

        Returns
        -------
        numpy.ndarray
            Parametric rate of change of the x-coordinate of point.
        numpy.ndarray
            Parametric rate of change of the y-coordinate of point.
        """
        theta = self._convert_theta(t)
        x_t = np.pi * self.radius * np.sin(theta)
        y_t = -np.pi * self.radius * np.cos(theta)
        return x_t, y_t

    def xy_tt(self, t: FloatInput) -> tuple[FloatArray, FloatArray]:
        """
        Return second derivative of the coordinates at parameter location.

        Notes
        -----
        Parameter goes from -1 (trailing edge lower surface) to +1 (trailing
        edge upper surface) with 0 representing the leading edge.

        Parameters
        ----------
        t : numpy.ndarray
            Parameter for desired locations.

        Returns
        -------
        numpy.ndarray
            Parametric second derivative of the x-coordinate of point.
        numpy.ndarray
            Parametric second derivative of the y-coordinate of point.
        """
        theta = self._convert_theta(t)
        x_tt = -(np.pi**2) * self.radius * np.cos(theta)
        y_tt = -(np.pi**2) * self.radius * np.sin(theta)
        return x_tt, y_tt

    @staticmethod
    def camber_location(
        t: FloatInput,
    ) -> tuple[FloatArray, FloatArray]:
        """
        Return the amount of camber at specified parameter location.

        Parameters
        ----------
        t : numpy.ndarray
            Parameter location of interest.

        Returns
        -------
        numpy.ndarray
            X-coordinate of camber at specified point.
        numpy.ndarray
            Y-coordinate of camber at specified point.
        """
        t_array = as_float_array(t)
        return t_array, np.zeros_like(t_array)

    def thickness_value(self, t: FloatInput) -> FloatArray:
        """
        Return the amount of thickness at specified parameter location.

        Parameters
        ----------
        t : numpy.ndarray
            Parameter location of interest.

        Returns
        -------
        numpy.ndarray
            Thickness at specified point.
        """
        return self.xy(t)[1]

    def joints(self) -> list[float]:  # noqa: PLR6301
        """
        Return the locations of any joints/discontinuities in the curve.

        Returns
        -------
        List[float]
            Parametric coordinates of any discontinuities.
        """
        return [-1.0, 1.0]

    @staticmethod
    def _convert_theta(t: FloatInput) -> FloatArray:
        """
        Convert the airfoil parameter to the cylinder polar angle.

        Parameters
        ----------
        t : FloatArray
            Signed airfoil parameter values in ``[-1, 1]``.

        Returns
        -------
        FloatArray
            Polar angle values in radians.
        """
        t_array = as_float_array(t)
        return np.pi * (1 - t_array)
