"""
File tests ASA work and checks correct work of binding.
"""

from __future__ import print_function
import time
import unittest
from ctypes import c_double, c_uint32
import matplotlib.pyplot as plt
import numpy as np
from asa import (ASA_OK, AsaSettings, COMPONENT_MODEL_TYPE_RESISTOR, GetIVCurve, GetLastOperationResult, HOST, IvCurve,
                 MODE_MANUAL, N_POINTS, PORT, Server, SetSettings, TriggerMeasurement)


def get_resistor(voltage: float, current: float) -> int:
    """
    Function returns current limiting resistance for given values of max
    current and max voltage.
    :param voltage: max voltage;
    :param current: max current.
    :return: current limiting resistance.
    """

    currents_and_resistors_for_voltage = {
        1: ((0.5, 1, 5, 10), (2000, 1000, 200, 100)),
        1.5: ((5, 15), (300, 100)),
        2: ((1, 2, 5, 10), (2000, 1000, 400, 200)),
        2.5: ((25,), (100,)),
        3: ((10, 15), (300, 200)),
        4: ((2, 10), (2000, 400)),
        4.5: ((15,), (300,)),
        5: ((5, 25, 50), (1000, 200, 100)),
        6: ((15,), (400,)),
        6.7: ((10,), (670,)),
        7.5: ((25, 75), (300, 100)),
        10: ((5, 10, 25, 50, 90), (2000, 1000, 400, 200, 100))}
    currents, resistors = currents_and_resistors_for_voltage[voltage]
    return resistors[currents.index(current)]


class TestStringMethods(unittest.TestCase):

    def test_not_empty(self):
        print("\ntest_not_empty", flush=True)
        print("Set measurement settings to device on server", flush=True)
        server = Server(HOST, PORT)
        settings = AsaSettings()
        settings.number_points = c_uint32(N_POINTS)
        settings.number_charge_points = c_uint32(N_POINTS)
        settings.measure_flags = c_uint32(0)
        settings.sampling_rate_hz = c_double(10000000)
        settings.probe_signal_frequency_hz = c_double(100000)
        settings.voltage_ampl_v = c_double(1)
        settings.max_current_m_a = c_double(10)
        settings.debug_model_type = c_uint32(COMPONENT_MODEL_TYPE_RESISTOR)
        settings.debug_model_nominal = c_double(200)
        settings.trigger_mode = c_uint32(MODE_MANUAL)
        SetSettings(server, settings)
        print("Start measurement", flush=True)
        TriggerMeasurement(server)
        while GetLastOperationResult(server) != ASA_OK:
            time.sleep(0.5)
        print("Get IV-curve", flush=True)
        iv_curve = IvCurve()
        GetIVCurve(server, iv_curve, settings.number_points)
        currents = iv_curve.currents[:settings.number_points]
        voltages = iv_curve.voltages[:settings.number_points]
        self.assertTrue(sum(currents) != 0 and sum(voltages) != 0)
        # Plot ivc if test ok
        plt.figure(figsize=(8, 5))
        plt.clf()
        plt.subplot(111)
        plt.title("Voltage and current")
        plt.plot(voltages, color="blue", label="Voltage, V")
        plt.plot(currents, color="red", label="Current, mA")
        plt.xlabel("Time, rel. units")
        plt.ylabel("Voltage and current")
        plt.legend()
        plt.show()

    def test_iv_form(self):
        print("\ntest_iv_form", flush=True)
        print("Set measurement settings to device on server", flush=True)
        server = Server(HOST, PORT)
        settings = AsaSettings()
        settings.number_points = c_uint32(N_POINTS)
        settings.number_charge_points = c_uint32(N_POINTS)
        settings.measure_flags = c_uint32(0)
        settings.sampling_rate_hz = c_double(10000000)
        settings.probe_signal_frequency_hz = c_double(100000)
        settings.voltage_ampl_v = c_double(1)
        settings.max_current_m_a = c_double(10)
        settings.debug_model_type = c_uint32(COMPONENT_MODEL_TYPE_RESISTOR)
        settings.debug_model_nominal = c_double(150)
        settings.trigger_mode = c_uint32(MODE_MANUAL)
        SetSettings(server, settings)
        print("Start measurement", flush=True)
        TriggerMeasurement(server)
        while GetLastOperationResult(server) != ASA_OK:
            time.sleep(0.5)
        print("Get IV-curve", flush=True)
        iv_curve = IvCurve()
        GetIVCurve(server, iv_curve, settings.number_points)
        currents = iv_curve.currents[:]
        voltages = iv_curve.voltages[:]
        times = np.linspace(0., 1 / settings.sampling_rate_hz, settings.number_points)
        r = settings.debug_model_nominal
        r_lim = get_resistor(settings.voltage_ampl_v, settings.max_current_m_a)
        # Check sin zero
        self.assertTrue(np.abs(currents[len(times) // 2]) < settings.max_current_m_a / 20)
        # Check sin max
        self.assertTrue(np.abs(voltages[len(times) // 4]) > settings.voltage_ampl_v * r / (2 * (r + r_lim)))


if __name__ == "__main__":
    unittest.main()
