import numpy as np

from ewgeo.utils import sinc_derivative


def make_gain_functions(aperture_type, d_lam, psi_0):
    """
    Generate function handles for the gain pattern (g) and gradient (g_dot),
    given the specified aperture type, aperture size, and mechanical steering
    angle.

    Ported from MATLAB Code

    Nicholas O'Donoughue
    9 January 2021

    :param aperture_type: String indicating the type of aperture requested.  Supports 'omni', 'adcock', 'rectangular'
    :param d_lam: Aperture length, in units of wavelength (d/lambda)
    :param psi_0: Mechanical steering angle (in radians) of the array [default=0]
    :return g: Function handle to the antenna pattern g(psi), for psi in radians
    :return g_dot: Function handle to the gradient of the antenna pattern, g_dot(psi), for psi in radians.
    """

    #  type = 'adcock' or 'Rectangular'
    # params
    #   d_lam = baseline (in wavelengths)
    #   psi_0 = central angle

    # Define all the possible functions
    def g_omni(psi):
        return np.ones_like(psi)

    def g_dot_omni(psi):
        return np.zeros_like(psi)

    def g_adcock(psi):
        return 2*np.sin(np.pi * d_lam * np.cos(psi-psi_0))

    def g_dot_adcock(psi):
        return -2*np.pi*d_lam*np.sin(psi-psi_0)*np.cos(np.pi*d_lam*np.cos(psi-psi_0))

    def g_rect(psi):
        # In the text, the function is sinc((psi-psi_0)*d/2*lam), but the sinc function is defined sin(x)/x
        # In numpy, the sinc function is sin(pi*x)/(pi*x), thus we should divide the argument to numpy.sinc by pi
        return np.abs(np.sinc((psi-psi_0)*d_lam/np.pi))  # sinc includes implicit pi

    def g_dot_rect(psi):
        return sinc_derivative((psi - psi_0) * d_lam) * d_lam

    switcher = {'omni': (g_omni, g_dot_omni),
                'adcock': (g_adcock, g_dot_adcock),
                'rectangular': (g_rect, g_dot_rect)}

    result = switcher.get(aperture_type.lower())

    return result[0], result[1]
