# -*- coding: utf-8 -*-
"""
pyradarmet.variables
====================

Functions to calculate radar-derived variables.

References
----------
Rinehart (1997), Radar for Meteorologists.
Aydin et al. (1986; JCAM), Remote Sensing of Hail with Dual Linear
Polarization Radar
"""
import numpy as np


def reflectivity(power_t, gain, pulse_width, wavelength, bw_h, bw_v,
                 aloss, rloss, power_return, r, dielectric=0.93):
    """
    Radar reflectivity [mm^6/m^3].

    From Rinehart (1993), Eqn 5.17 (See Eqn 5.14-5.16 also)

    Parameters
    ----------
    power_t : float
        Transmitted power [W]
    gain : float
        Antenna Gain [dB]
    pulse_width : float
        Pulse Width [s]
    wavelength : float
        Radar wavelength [m]
    bwidth : float
        Antenna beamwidth [degrees]
    aloss : float
        Antenna/waveguide/coupler loss [dB]
    rloss : float
        Receiver loss [dB]
    dielectric : float
        Dielectric factor [unitless]
    power_return : float
        Returned power [W]
    r : float or array
        Range to target [m]

    Notes
    -----
    This routine calls the radar_constant function.
    The default is for a dielectric factor value for water.  This can be
        changed by the user, e.g. K=0.208 for particle sizes of equivalent melted
        diameters or K=0.176 for particle sizes of equivalent ice spheres.
    """
    # Call the radar constant function
    C1 = radar_constant(power_t, gain, pulse_width, wavelength, bw_h, bw_v, aloss, rloss)
    return power_return * np.asarray(r)**2 / (C1 * dielectric**2)


def radial_velocity(frequency, wavelength):
    """
    Radial velocity [m/s].

    From Rinehart (1997), Eqn 6.6

    Parameters
    ----------
    frequency : float or array
        Frequency shift [Hz]
    wavelength : float or array
        Radar wavelength [m]

    Notes
    -----
    If arrays are used, either for one okay, or both must be the same length.
    """
    return np.asarray(frequency) * np.asarray(wavelength) / 2.


def cdr(refl_parallel, refl_orthogonal):
    """
    Circular depolarization ratio [dB].

    From Rinehart (1997), Eqn 10.2

    Parameters
    ----------
    refl_parallel : float or array
        Reflectivity in the parallel channel [mm^6/m^3]
    refl_orthogonal : float or array
        Reflectivity in the orthogonal channel [mm^6/m^3]

    Notes
    -----
    Ensure that both powers have the same units!

    Radars that transmit right-hand circular polarization and receive and
       receive both left- and right-hand circular polarization (using two
       antennas) and acquiring the same pulse.
    The parallel (orthogonal) component refers to the same
      (opposite) polarization as transmitted. Non-spericity of hydrometeors
      may be detected (inf long, thin scatterers have CDR = 0 dB, while perfect
     spheres have CDR = -infinity

    Can also use power measurements instead of reflectivity.

    If arrays are used, either for one okay, or both must be the same length.
    """
    return 10. * np.log10(np.asarray(refl_parallel)/np.asarray(refl_orthogonal))


def ldr(z_h, z_v):
    """
    Linear depolarization ratio [dB].

    From Rinehart (1997), Eqn 10.3

    Parameters
    ----------
    z_h : float or array
        Horizontal reflectivity [mm^6/m^3]
    z_v : float or array
        Vertical reflectivity [mm^6/m^3]

    Notes
    -----
    Ensure that both powers have the same units!

    Uses both polarizations in a dual-pol radar from a single pulse.

    Perfect spheres yield LDR => -infinity (though antenna limitations limit
    LDR values to -40 dB for small spheres.
    Long, thin targets, LDR => 0

    Typical values in the range -15 > LDR > -35 dB

    If arrays are used, either for one okay, or both must be the same length.
    """
    return 10. * np.log10(np.asarray(z_h) / np.asarray(z_v))


def zdr(z_h, z_v):
    """
    Differential reflectivity [dB].

    From Rinehart (1997), Eqn 10.3 and Seliga and Bringi (1976)

    Parameters
    ----------
    z_h : float or array
        Horizontal reflectivity [mm^6/m^3]
    z_v : float or array
        Vertical reflectivity [mm^6/m^3]

    Notes
    -----
    Ensure that both powers have the same units!

    Alternating horizontally and linearly polarized pulses are averaged.

    Notes
    -----
    If arrays are used, either for one okay, or both must be the same length.
    """
    return 10. * np.log10(np.asarray(z_h) / np.asarray(z_v))


def zdp(z_h, z_v):
    """
    Reflectivity difference [dB].

    From Rinehart (1997), Eqn 10.3

    Parameters
    ----------
    z_h : float
        Horizontal reflectivity [mm^6/m^3]
    z_v : float
        Horizontal reflectivity [mm^6/m^3]

    Notes
    -----
    Ensure that both powers have the same units!

    Alternating horizontally and linearly polarized pulses are averaged.
    """
    zh = np.atleast_1d(z_h)
    zv = np.atleast_1d(z_v)
    if len(zh) != len(zv):
        raise ValueError('Input variables must be same length')
        return

    zdp = np.full_like(zh, np.nan)
    good = np.where(zh > zv)
    zdp[good] = 10.* np.log10(zh[good] - zv[good])
    return zdp


def hdr(dbz_h, zdr):
    """
    Differential reflectivity [dB] hail signature.

    From Aydin et al. (1986), Eqns 4-5

    Parameters
    ----------
    dbz_h : float or array
        Horizontal reflectivity [dBZ]
    zdr : float or array
        Differential reflectivity [dBZ]

    Notes
    -----
    Ensure that both powers have the same units!

    Positive HDR and strong gradients at edges signify ice. The larger HDR,
       the greater likelihood that ice is present.

    Considerations for this equation (see paper for more details):
       1) Standar error of disdrometer data allowed for
       2) Drop oscillation accounted for based on 50% model of Seliga et al (1984)
       3) Lower (27) bound chose to provide constant Zh ref level
       4) Upper cutoff of 60 (may be too low)

    Picca and Ryzhkof (2012) mention that this does not take into account
    the hail melting process.  So use at your own risk!
    """
    zdr = np.atleast_1d(zdr)
    # Set the f(zdr) based upon observations
    f = np.full_like(zdr, np.nan)
    negind = np.where(zdr <= 0)
    lowind = np.where((zdr > 0) & (zdr <= 1.74))
    highind = np.where(zdr > 1.74)
    f[negind] = 27.
    f[lowind] = 19. * zdr[lowind] + 27.
    f[highind] = 60.
    # Calculate HDR
    return np.asarray(dbz_h) - f