import os
import sys
from gnuradio import soapy
import gnuradio.satnogs as satnogs
import Hamlib

def get_state_directory() -> str:
    oldpath = os.path.expanduser("~/.grc_gnuradio")
    try:
        from gnuradio.gr import paths
        newpath = paths.persistent()
        if os.path.exists(newpath):
            return newpath
        if os.path.exists(oldpath):
            return oldpath
    except (ImportError, NameError):
        return oldpath

sys.path.append(os.environ.get('GRC_HIER_PATH', get_state_directory()))

from iq_dump_hier import iq_dump_hier
from fsk_hier import fsk_hier
from ogg_hier import ogg_hier
from frame_decoder_hier import frame_decoder_hier


def soapy_source_init(dev_driver, output_stream_format, channels, dev_args,
                      stream_args, tune_args, other_settings, samp_rate,
                      dc_removal, dc_offset, iq_balance, gain_mode, freq,
                      bb_freq, ppm, antenna, filter_bw, gain):
    """
    Return gnuradio.soapy_source block

    The function takes as parameters all needed arguments to create a
    soapy_source block and then returns it.

    :param dev_driver: The device driver as string (e.g "driver=rtlsdr")
    :type dev_driver: str
    :param output_stream_format: The output format of the block
    :type output_stream_format: str
    :param channels: Number of channels of the block
    :type channels: int
    :param dev_args: Device arguments string
    :type dev_args: str
    :param stream_args: Device stream args string
    :type stream_args: str
    :param tune_args: Device tune args list or empty string
    :type tune_args: list(str) or str
    :param other_settings: Device other settings args list or empty string
    :type other_settings: list(str) or str
    :param samp_rate: Device sampling rate
    :type samp_rate: float
    :param dc_removal: Device automatic dc removal option
    :type dc_removal: str
    :param dc_offset: Device dc offset correction
    :type dc_offset: complex
    :param iq_balance: Device iq balance correction
    :type iq_balance: complex
    :param gain_mode: Device gain mode option string
    :type gain_mode: str
    :param freq: Device center frequncy
    :type freq: float
    :param bb_freq: Device baseband CORDIC frequency
    :type bb_freq: float
    :param ppm: Device LO frequency correction in ppm
    :type ppm: int
    :param antenna: Device antenna name
    :type antenna: str
    :param filter_bw: Device filter bandwidth
    :type filter_bw: float
    :param gain: Device gain value
    :type gain: float
    :return: soapy_source block
    :rtype: gnuradio.soapy.soapy_python.source
    """

    if (gain_mode not in ['Overall', 'Specific', 'Settings Field']):
        raise ValueError("Wrong gain mode on channel 0. Allowed gain modes: "
                         "['Overall', Specific', 'Settings Field']")

    if (not isinstance(tune_args, list)):
        tune_args = [tune_args]
    if (not isinstance(other_settings, list)):
        other_settings = [other_settings]

    soapy_source = soapy.source(dev_driver, output_stream_format, channels,
                                dev_args, stream_args, tune_args,
                                other_settings)
    soapy_source.set_sample_rate(0, samp_rate)
    soapy_source.set_dc_offset_mode(0, dc_removal)
    soapy_source.set_dc_offset(0, dc_offset)
    soapy_source.set_iq_balance(0, iq_balance)
    soapy_source.set_gain_mode(0, False)
    soapy_source.set_frequency(0, freq)
    soapy_source.set_frequency(0, "BB", bb_freq)
    soapy_source.set_frequency_correction(0, ppm)
    soapy_source.set_antenna(0, antenna)
    soapy_source.set_bandwidth(0, filter_bw)

    if (gain_mode != "Settings Field"):
        soapy_source.set_gain(0, gain)

    return soapy_source


def doppler_compensation_init(rigctl_host, rigctl_port, samp_rate, freq,
                              lo_offset, target_samp_rate, compensate_doppler):
    """
    Return satnogs.doppler_compensation block

    The function takes as parameters all needed arguments to create a
    doppler_compensation block and then returns it. Also creates a
    rigctl_doppler_correction object to be passed to the
    doppler_compensation object.

    :param rigctl_host: Rotcld host IP
    :type rigct_host: str
    :param rigctl_port: Rotctld host port
    :type rigctl_port: str
    :param samp_rate: The input sampling rate
    :type samp_rate: float
    :param freq: The center frequency
    :type freq: float
    :param lo_offset: The local oscillator offset
    :type lo_offset: float
    :param target_samp_rate: The target sampling rate
    :type target_samp_rate: float
    :param compensate_doppler: Flag to enable doppler compensation
    :type compensate_doppler: int
    :return: The doppler compensation block
    :rtype: satnogs.doppler_compensation
    """

    rigctl = satnogs.rigctl_doppler_correction.make(freq,
                                                    Hamlib.RIG_MODEL_NETRIGCTL)
    rigctl.set_param("rig_pathname", f"{rigctl_host}:{rigctl_port}")

    doppler_compensation = satnogs.doppler_compensation(
        doppler_src=rigctl,
        samp_rate=samp_rate,
        sat_freq=freq,
        lo_offset=lo_offset,
        out_samp_rate=target_samp_rate,
        compensate=compensate_doppler)

    return doppler_compensation


def iq_dump_init(iq_file_path, scale_factor=32768):
    """
    Return iq_dump block

    The function takes as parameters all needed arguments to create a
    iq_dump block and then returns it.

    :param enable_iq_dump: Whether iq file sink sould be enabled
    :type enable_iq_dump: int
    :param iq_file_path: The path for the iq dump to be saved
    :type iq_file_path: str
    :param scale_factor: Scale factor to used when converting complex samples to shorts
    :type scale_factro: float
    :return: The iq dump block
    :rtype: iq_dump
    """
    iq_dump = iq_dump_hier(iq_file_path=iq_file_path,
                           scale_factor=scale_factor)

    return iq_dump


def fsk_init(audio_samp_rate, baudrate, quad_demod):
    """
    Return fsk chain hier block

    The function takes the roll-off of the FLL band-edge filter, the
    audio sampling rate and the baudarate.

    :param alpha: The roll-off of the FLL band-edge filter
    :type alpha: float
    :param audio_samp_rate: The generated audio sampling rate
    :type audio_samp_rate: float
    :param baudrate: The signal baudrate
    :type baudrate: float
    :return: The fsk chain blocks
    :rtype: fsk
    """

    fsk = fsk_hier(audio_samp_rate=audio_samp_rate,
                   baudrate=baudrate,
                   quad_demod=quad_demod)

    return fsk


def ogg_init(audio_samp_rate=48e3,
             cutoff_freq=3e3,
             enable_fsk=1,
             file_path=None,
             phase_incr=0,
             quad_demod=None,
             resample_rate=1,
             transition_w=1e3):
    """
    Return ogg chain hier block

    The function takes as parameters the sampling rate of the generated
    audio file, the file path, resample rate of the polyphase resampler,
    if the blocks are connected to an FSK rf chain, the phace increment
    and the transition width of the low pass filter.
    Depending on the  optional parameter enable_fsk how the samples are
    processed to finaly create the audio file has differences. If
    enable_fsk is 1 then only the resample_rate parameter should also be
    set. For FSK signals the enable_fsk should be set to 1. For PSK the
    cutoff frequency the phase incremete and the transition width
    parameters used by a low pass filter must be passed.
    Resample rate is used by both paths.

    :param audio_samp_rate: The audio file sampling rate
    :type audio_samp_rate: float
    :param file_path: The path to save the file
    :type file_name: str
    :param cutoff_freq: The cutoff frequency in the low pass filter if
        BPSK chain is used, defaults to 3e3
    :type cutoff_freq: float, optional
    :param enable_fsk: Follow FSK audio chain or BPSK, defaults to 1
    :type enable_fsk: int 0 | 1, optional
    :param phase_incr: PSK chain rotator phase increment, defaults to 0
    :type phase_incr: float, optional
    :param resample_rate: PSK chain Polyphase Resampler resampling rate,
        defaults to 0
    :type resample_rate: float, optional
    :param transition_w: PSK chain low pass filter transition width,
        defaults to 1e3
    :type transition_w: float, optional
    :rerurn: The oog chain blocks
    :rtype: ogg_hier
    """

    ogg = ogg_hier(audio_samp_rate=audio_samp_rate,
                   cutoff_freq=cutoff_freq,
                   enable_fsk=enable_fsk,
                   file_path=file_path,
                   phase_incr=phase_incr,
                   quad_demod=quad_demod,
                   resample_rate=resample_rate,
                   transition_w=transition_w)

    return ogg


def frame_decoder_init(ax25_descrambling=False,
                       callsign='GND',
                       check_crc=False,
                       custom_crc=satnogs.crc.NONE,
                       error_correction=False,
                       framing="",
                       g3ruh_descramble=False,
                       max_frame_len=0,
                       preamble=[],
                       preamble_thres=0,
                       promiscuous_mode=False,
                       rs=False,
                       ssid=0,
                       sync_word=[0x00],
                       sync_word_thres=0,
                       variable_len=False,
                       whitening=None):
    """
    Return frame decoder chain hier block

    The function takes all parameters necessary to initialize the
    available decoders. The available decoders are ax25, ax100_mode5
    and ax100_mode6. Since not all parameters are necessary for all
    decoders the default value is empty or False and the caller must
    pass the needed parameters according to the framing.

    :param ax25_descrambling: Enables ax25 descrambling for ax100_mode6,
        defaults to False
    :type ax25_descrambling: bool, optional
    :param callsign: The callsing, defaults to ''
    :type callsign: str, optional
    :param check_crc: Enable crc check, defaults to False
    :type check_crc: bool, optional
    :param custom_crc: The CRC to use, defaults to satnogs.crc.NONE
    :type custom_crc: satnogs.crc, optional
    :param error_correction: Enables error correction, defaults to False
    :type error_correction: bool, optional
    :param framing: The framing name, defaults to ''
    :type framing: str, optional
    :param g3ruh_descramble: Enable G3RUH descramblin, defaults to False
    :type g3ruh_descramble: bool, optional
    :param max_frame_len: The maximum frame length, defaults to 0
    :type max_frame_len: int, optional
    :param preamble: The preamble as a list of ints, defaults to []
    :type preamble: list(int), optional
    :param preamble_thres: The preamble threshold, defaults to 0
    :type preamble_thres: int, optional
    :param promiscuous_mode: Enables promiscuous mode, defaults to False
    :type promiscuous_mode: bool, optional
    :param rs: Enables Reed-Solomon error correction, defaults to False
    :type rs: bool, optional
    :param ssid: Receiver ssid, defaults to 0
    :type ssid: int, optional
    :param sync_word: List of syncword bits as integers, defaults to [0x00]
    :type sync_word: list(int), optional
    :param variable_len: Enables variable frame lenght, defaults to False
    :type variable_len: bool, optional
    :param whitening: Enable CCSD whitening, defaults to None
    :type whitening: satnogs.whitening, optional
    :return: The frame decoder chain blocks
    :rtype: frame_decoder
    """

    frame_decoder = frame_decoder_hier(ax25_descrambling=ax25_descrambling,
                                       callsign=callsign,
                                       check_crc=check_crc,
                                       custom_crc=custom_crc,
                                       error_correction=error_correction,
                                       framing=framing,
                                       g3ruh_descramble=g3ruh_descramble,
                                       max_frame_len=max_frame_len,
                                       preamble=preamble,
                                       preamble_thres=preamble_thres,
                                       promiscuous_mode=promiscuous_mode,
                                       rs=rs,
                                       ssid=ssid,
                                       sync_word=sync_word,
                                       sync_word_thres=sync_word_thres,
                                       variable_len=variable_len,
                                       whitening=whitening)

    return frame_decoder
