import time
import argparse
import fn_gen.errors as fg_err
from common import (
    close_output,
    get_preamplified,
    get_postamplified,
    AMPLIFICATION,
    N_CYCLES,
    V_STEPS,
    F_STEPS,
    FREQ_START,
    FREQ_STOP,
    STEP_DURATION,
)
from fn_gen import DG2052
import numpy as np


def calculate_n_samples(freq: float, ts: float, n_cycles: int) -> int:
    """
    Calculates the number of samples based on the frequency of the signal, the sampling time and the number of cycles within one step.

    Parameters
    ---------
    freq: int
        The frequency of the signal
    ts: float
        The sampling time
    n_cycles: int
        The number of cycles within a step

    Returns
    ------
    n_samples: int
        The number of samples (can be multiplied by the sampling time (ts) to get the total duration)
    """
    return int(round(n_cycles / (ts * freq)))


def discrete_sweep(
    v_min: float,
    v_max: float,
    v_steps: int,
    adaptive: bool,
    step_duration: int,
    freq_start: int,
    freq_stop: int,
    f_steps: int,
):
    ##################### PROGRAM START ###########
    freqs = np.logspace(np.log10(freq_start), np.log10(freq_stop), f_steps)
    v_min = get_preamplified(AMPLIFICATION, v_min)
    v_max = get_preamplified(AMPLIFICATION, v_max)
    volts = np.linspace(v_min, v_max, v_steps)
    print(freqs)
    fg = DG2052("TCPIP::192.168.1.11::INSTR")
    channel = 2
    try:
        print(fg.whoami())
        print("")
        print(f"Output{channel} Impedance: {fg.get_output_impedance(channel)} Ohm")
        print(f"Output{channel} Load: {fg.get_output_load(channel)} Ohm")
        print(f"Output{channel} Voltage Limits: {fg.get_output_volt_limits(channel)} V")
        for v in volts:
            for freq in freqs:
                sampling_rate = 10 * freq
                n_samples = calculate_n_samples(freq, 1 / sampling_rate, N_CYCLES)
                if adaptive:
                    step_duration = n_samples * (1 / sampling_rate)
                print(f"V: {get_postamplified(AMPLIFICATION, v)} V")
                print(f"Freq: {freq} Hz")
                print(f"Duration: {step_duration} s")
                print(f"N_Samples: {n_samples} samples")
                fg.set_output(channel, False)
                fg.set_sine_wave(channel, freq, v, 0, 0)
                fg.set_output(channel, True)
                print(
                    f"Output{channel}: {fg.get_output_signal(channel)} | {fg.get_output_state(channel)}"
                )
                time.sleep(step_duration)
                # fg.set_output(channel, False)
        print(f"Output{channel} State: {fg.get_output_state(channel)}")
    except fg_err.ValueOutOfBoundsError as err:
        print(err)
    except fg_err.UndefinedValueError as err:
        print(err)
    except KeyboardInterrupt:
        close_output(fg, channel)
    finally:
        close_output(fg, channel)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="This program is for testing the DG2052 function genrator library. It does a discrete sweep with the supplied parameters."
    )
    parser.add_argument(
        "--vmin", type=float, required=True, help="The minimum voltage supplied"
    )
    parser.add_argument(
        "--vmax", type=float, required=True, help="The maximum voltage supplied"
    )
    parser.add_argument(
        "--v-steps", type=int, default=V_STEPS, help="The number of voltage steps"
    )
    parser.add_argument(
        "--freq-start", type=int, default=FREQ_START, help="The starting frequency"
    )
    parser.add_argument(
        "--freq-stop", type=int, default=FREQ_STOP, help="The stop frequency"
    )
    parser.add_argument(
        "--f-steps", type=int, default=F_STEPS, help="The number of steps"
    )
    parser.add_argument(
        "--step-duration",
        type=int,
        default=STEP_DURATION,
        help="The duration of each step",
    )
    parser.add_argument(
        "--adaptive-step",
        action="store_true",
        help="Adapts the step duration to the frequency of the step",
    )
    args = parser.parse_args()
    discrete_sweep(
        v_min=args.vmin,
        v_max=args.vmax,
        v_steps=args.v_steps,
        adaptive=args.adaptive_step,
        step_duration=args.step_duration,
        freq_start=args.freq_start,
        freq_stop=args.freq_stop,
        f_steps=args.f_steps,
    )
