"""Shared base classes and constants for airfoil camber lines."""

from abc import abstractmethod

import numpy as np

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

from .curve import Curve

CLASSIC_NACA_DIGIT_MAX = 9
CLASSIC_NACA_5DIGIT_IDEAL_LIFT_INDEX = 2
CLASSIC_NACA_5DIGIT_LOCATION_MIN = 1
CLASSIC_NACA_5DIGIT_LOCATION_MAX = 6
CLASSIC_NACA_5DIGIT_LIFT_MIN = 1
CLASSIC_NACA_5DIGIT_LIFT_MAX = 4
CLASSIC_NACA_5DIGIT_PARAMETERS = {
    1: (0.0580, 361.4),
    2: (0.1260, 51.64),
    3: (0.2025, 15.957),
    4: (0.2900, 6.643),
    5: (0.3910, 3.230),
}
REFLEXED_NACA_5DIGIT_PARAMETERS = {
    2: (0.1300, 51.99, 0.03972036),
    3: (0.2170, 15.793, 0.10691861),
    4: (0.3180, 6.520, 0.197556),
    5: (0.4410, 3.191, 0.4323805),
}


class Camber(Curve):
    """
    Base class for camber lines.

    The camber lines will have a parameterization from 0 to 1 and are not
    defined outside of that range.
    """

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

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

        Returns
        -------
        numpy.ndarray
            X-coordinate of point.
        numpy.ndarray
            Y-coordinate of point.
        """
        t = as_float_array(t)
        return t, self._y(t)

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

        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.
        """
        t = as_float_array(t)
        return np.ones_like(t), self._y_t(t)

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

        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.
        """
        t = as_float_array(t)
        return np.zeros_like(t), self._y_tt(t)

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

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

        Returns
        -------
        numpy.ndarray
            Parametric third derivative of the x-coordinate of point.
        numpy.ndarray
            Parametric third derivative of the y-coordinate of point.
        """
        t = as_float_array(t)
        return np.zeros_like(t), self._y_ttt(t)

    def k_t(self, t: FloatInput) -> FloatArray:
        """
        Calculate the rate of change of curvature at parameter location.

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

        Returns
        -------
        numpy.ndarray
            Rate of change of curvature of surface at point.
        """
        t = as_float_array(t)
        xt, yt = self.xy_t(t)
        xtt, ytt = self.xy_tt(t)
        xttt, yttt = self.xy_ttt(t)
        k = self.k(t)
        term1 = xt**2 + yt**2
        p = xt / np.sqrt(term1)
        q = yt / np.sqrt(term1)

        # return ((yttt*p - yttt*q)/term1
        #         - k*(xtt*xt + ytt*yt)/term1**2)*(k*term1 - 2*(xtt*q - ytt*p))
        return (yttt * p - xttt * q) / term1 - 3 * k * (
            xtt * xt + ytt * yt
        ) / term1

    @abstractmethod
    def max_camber_parameter(self) -> FloatScalar:
        """
        Return parameter where the camber is maximum.

        Returns
        -------
        float
            Parameter where camber is maximum.
        """

    @abstractmethod
    def _y(self, t: FloatArray) -> FloatArray:
        """
        Return the camber location at specified chord location.

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

        Returns
        -------
        numpy.ndarray
            Camber at specified point.
        """

    @abstractmethod
    def _y_t(self, t: FloatArray) -> FloatArray:
        """
        Return first derivative of camber at specified chord location.

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

        Returns
        -------
        numpy.ndarray
            First derivative of camber at specified point.
        """

    @abstractmethod
    def _y_tt(self, t: FloatArray) -> FloatArray:
        """
        Return second derivative of camber at specified chord location.

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

        Returns
        -------
        numpy.ndarray
            Second derivative of camber at specified point.
        """

    @abstractmethod
    def _y_ttt(self, t: FloatArray) -> FloatArray:
        """
        Return third derivative of camber at specified chord location.

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

        Returns
        -------
        numpy.ndarray
            Third derivative of camber at specified point.
        """


class NoCamber(Camber):
    """
    Representation of the case where there is no camber.

    This camber line evaluates to zero everywhere along the chord.
    """

    def __init__(self) -> None:
        """
        Initialize the zero-camber representation.

        Returns
        -------
        None
            This initializer stores no additional state.
        """
        pass

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

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

    def max_camber_parameter(self) -> FloatScalar:  # noqa: PLR6301
        """
        Return parameter where the camber is maximum.

        Returns
        -------
        float
            Parameter where camber is maximum.
        """
        return 0.0

    def _y(self, t: FloatArray) -> FloatArray:  # noqa: PLR6301
        """
        Return the camber location at specified chord location.

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

        Returns
        -------
        numpy.ndarray
            Camber at specified point.
        """
        return np.zeros_like(t)

    def _y_t(self, t: FloatArray) -> FloatArray:  # noqa: PLR6301
        """
        Return first derivative of camber at specified chord location.

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

        Returns
        -------
        numpy.ndarray
            First derivative of camber at specified point.
        """
        return np.zeros_like(t)

    def _y_tt(self, t: FloatArray) -> FloatArray:  # noqa: PLR6301
        """
        Return second derivative of camber at specified chord location.

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

        Returns
        -------
        numpy.ndarray
            Second derivative of camber at specified point.
        """
        return np.zeros_like(t)

    def _y_ttt(self, t: FloatArray) -> FloatArray:  # noqa: PLR6301
        """
        Return third derivative of camber at specified chord location.

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

        Returns
        -------
        numpy.ndarray
            Third derivative of camber at specified point.
        """
        return np.zeros_like(t)
