from FreeSimpleGUI import TIMEOUT_KEY, WIN_CLOSED
import json
import math
import socket
import subprocess

import device_interaction as dev

import gui

use_client = False

sending_param = {}

#### ---- Constants

GUI_TIMEOUT_INTERVAL = 5#505 - dev.WAIT_AFTER_SEND*1000 # GUI refresh time in milliseconds

SAVE_POINTS_NUMBER = 1000 # Number of most recent data points kept in memory

INITIAL_TEMPERATURE_1 = 22.40 # Set initial temperature for Laser 1 in Celsius: from -1 to 45 C ??
INITIAL_TEMPERATURE_2 = 16.70 # Set initial temperature for Laser 2 in Celsius: from -1 to 45 C ??
INITIAL_CURRENT_1 = 32.0 # 64.0879 max # Set initial current for Laser 1, in mA
INITIAL_CURRENT_2 = 32.0 # 64.0879 max # Set initial current for Laser 2, in mA

#### ---- Functions

def start_task(prt):
    global sending_param
    dev.send_task_command(prt, sending_param)

def stop_task(prt):
    global sending_param
    sending_param = {}
    dev.reset_port_settings(prt)
    dev.send_control_parameters(prt, params)

def get_float(values, strId):
    value = 0.0
    try:
        value = float(values[strId])
    except:
        value = float("nan")
        window['-StartCycle-'].update(disabled = True)
    return value

def shorten(i):
    return "{:.2f}".format(round(i, 2))


def set_initial_params():
    params = {}
    params['Temp_1'] = INITIAL_TEMPERATURE_1 # Initial temperature for Laser 1
    params['Temp_2'] = INITIAL_TEMPERATURE_2 # Initial temperature for Laser 2
    params['ProportionalCoeff_1'] = int(10*256) # Proportional coefficient for temperature stabilizatoin for Laser 1 <-- ToDo (why int?)
    params['ProportionalCoeff_2'] = int(10*256) # Proportional coefficient for temperature stabilizatoin for Laser 2 <-- ToDo (why int?)
    params['IntegralCoeff_1'] = int(0.5*256) # Integral coefficient for temperature stabilizatoin for Laser 1 <-- ToDo (why int?)
    params['IntegralCoeff_2'] = int(0.5*256) # Integral coefficient for temperature stabilizatoin for Laser 2 <-- ToDo (why int?)
    params['Message_ID'] = "00FF" # Send Message ID (hex format)
    params['Iset_1'] = INITIAL_CURRENT_1 # Currency value array for Laser 1, in mA
    params['Iset_2'] = INITIAL_CURRENT_2 # Currency value array for Laser 2, in mA
    params['Min_Temp_1'] = INITIAL_TEMPERATURE_1
    params['Max_Temp_1'] = 28
    params['Min_Current_1'] = INITIAL_CURRENT_1
    params['Max_Current_1'] = 50.0
    params['Delta_Temp_1'] = 0.05
    params['Delta_Current_1'] = 0.05
    params['Min_Temp_2'] = INITIAL_TEMPERATURE_2
    params['Max_Temp_2'] = 28
    params['Min_Current_2'] = INITIAL_CURRENT_2
    params['Max_Current_2'] = 50.0
    params['Delta_Temp_2'] = 0.05
    params['Delta_Current_2'] = 0.05
    params['Delta_Time'] = 50
    params['Tau'] = 3
    return params

def update_data_lists():
    saved_data.append(data)
    if len(saved_data)>SAVE_POINTS_NUMBER:
        saved_data.pop(0)

    draw_data.append(data)
    if len(draw_data)>gui.GRAPH_POINTS_NUMBER:
        draw_data.pop(0)



######## ---- Main program

if __name__ == "__main__":
    saved_data = []
    draw_data = []
    
    params = set_initial_params()
    
    prt = dev.create_port_connection()
    if prt is None:
        print('Can\'t create connection. Closing program...')
        exit(1)
    
    # dev.request_state(prt)
    dev.send_control_parameters(prt, params)
    saved_data.append(dev.request_data(prt))
    draw_data.append(saved_data[0])
    
    window = gui.setup_gui(params)
    axes_signs = gui.sign_axes(window)
    
    current_and_temperature_settings_available = True
    disableStartButton = False

    if use_client:
        p = subprocess.Popen("path/to/oscilloscope.exe")
        sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sck.bind(("127.0.0.1", 9090))
        sck.listen()
        conn, _ = sck.accept()

    while True:
        event, values = window.read(timeout=GUI_TIMEOUT_INTERVAL)
    
        enable_manual_settings = window['-EnableManualSettings-'].get()
        if current_and_temperature_settings_available:
            window['-EnableT1-'].update(disabled = enable_manual_settings)
            window['-EnableT2-'].update(disabled = enable_manual_settings)
            window['-EnableC1-'].update(disabled = enable_manual_settings)
            window['-EnableC2-'].update(disabled = enable_manual_settings)
        
            window['-InputMinT1-'].update(disabled   = enable_manual_settings)
            window['-InputMaxT1-'].update(disabled   = enable_manual_settings)
            window['-InputDeltaT1-'].update(disabled = enable_manual_settings)
            window['-InputMinT2-'].update(disabled   = enable_manual_settings)
            window['-InputMaxT2-'].update(disabled   = enable_manual_settings)
            window['-InputDeltaT2-'].update(disabled = enable_manual_settings)
            window['-InputMinC1-'].update(disabled   = enable_manual_settings)
            window['-InputMaxC1-'].update(disabled   = enable_manual_settings)
            window['-InputDeltaC1-'].update(disabled = enable_manual_settings)
            window['-InputMinC2-'].update(disabled   = enable_manual_settings)
            window['-InputMaxC2-'].update(disabled   = enable_manual_settings)
            window['-InputDeltaC2-'].update(disabled = enable_manual_settings)
    
            window['-InputT1-'].update(disabled = not enable_manual_settings)
            window['-InputT2-'].update(disabled = not enable_manual_settings)
            window['-InputI1-'].update(disabled = not enable_manual_settings)
            window['-InputI2-'].update(disabled = not enable_manual_settings)
    
            window['-StartCycle-'].update(disabled = not enable_manual_settings)
    
        if current_and_temperature_settings_available and not enable_manual_settings: 
            enable_t1 = window['-EnableT1-'].get()
            enable_t2 = window['-EnableT2-'].get()
            enable_c1 = window['-EnableC1-'].get()
            enable_c2 = window['-EnableC2-'].get()
    
            sending_param['ProportionalCoeff_1'] = params['ProportionalCoeff_1']
            sending_param['IntegralCoeff_1']     = params['IntegralCoeff_1']
            sending_param['ProportionalCoeff_2'] = params['ProportionalCoeff_2']
            sending_param['IntegralCoeff_2']     = params['IntegralCoeff_2']
            
            if enable_t1     and \
               not enable_t2 and \
               not enable_c1 and \
               not enable_c2:
                sending_param['TaskType'] = dev.cmd.TaskType.ChangeTemperatureLD1
                sending_param['MinT1']    = get_float(values, '-InputMinT1-')
                sending_param['MaxT1']    = get_float(values, '-InputMaxT1-')
                sending_param['DeltaT1']  = get_float(values, '-InputDeltaT1-')
                sending_param['I1']  = get_float(values, '-InputI1-')
                sending_param['I2']  = get_float(values, '-InputI2-')
                sending_param['T2']  = get_float(values, '-InputT2-')
                sending_param['Dt']  = get_float(values ,'-InputDeltaTime-')
                sending_param['Tau'] = get_float(values ,'-InputTau-')

                disableStartButton = math.isnan(sending_param['MinT1']) or \
                                     math.isnan(sending_param['MaxT1']) or \
                                     math.isnan(sending_param['DeltaT1']) or \
                                     math.isnan(sending_param['I1']) or \
                                     math.isnan(sending_param['I2']) or \
                                     math.isnan(sending_param['T2']) or \
                                     math.isnan(sending_param['Dt']) or \
                                     math.isnan(sending_param['Tau'])
    
                window['-EnableT2-'].update(disabled = enable_t1)
                window['-EnableC1-'].update(disabled = enable_t1)
                window['-EnableC2-'].update(disabled = enable_t1)
                enable_t2 = window['-EnableT2-'].get()
                enable_c1 = window['-EnableC1-'].get()
                enable_c2 = window['-EnableC2-'].get()
                window['-InputMinT1-'].update(disabled   = not enable_t1)
                window['-InputMaxT1-'].update(disabled   = not enable_t1)
                window['-InputDeltaT1-'].update(disabled = not enable_t1)
                window['-InputI1-'].update(disabled      = not enable_t1)
                window['-InputI2-'].update(disabled      = not enable_t1)
                window['-InputT2-'].update(disabled      = not enable_t1)
                window['-InputMinT2-'].update(disabled   = enable_t1)
                window['-InputMaxT2-'].update(disabled   = enable_t1)
                window['-InputDeltaT2-'].update(disabled = enable_t1)
                window['-InputMinC1-'].update(disabled   = enable_t1)
                window['-InputMaxC1-'].update(disabled   = enable_t1)
                window['-InputDeltaC1-'].update(disabled = enable_t1)
                window['-InputMinC2-'].update(disabled   = enable_t1)
                window['-InputMaxC2-'].update(disabled   = enable_t1)
                window['-InputDeltaC2-'].update(disabled = enable_t1)
                window['-EnableManualSettings-'].update(disabled = True)
            elif enable_t2     and \
                 not enable_t1 and \
                 not enable_c1 and \
                 not enable_c2:
                sending_param['TaskType'] = dev.cmd.TaskType.ChangeTemperatureLD2
                sending_param['MinT2']    = get_float(values, '-InputMinT2-')
                sending_param['MaxT2']    = get_float(values, '-InputMaxT2-')
                sending_param['DeltaT2']  = get_float(values, '-InputDeltaT2-')
                sending_param['I1']  = get_float(values, '-InputI1-')
                sending_param['I2']  = get_float(values, '-InputI2-')
                sending_param['T1']  = get_float(values, '-InputT1-')
                sending_param['Dt']  = get_float(values ,'-InputDeltaTime-')
                sending_param['Tau'] = get_float(values ,'-InputTau-')
                disableStartButton = math.isnan(sending_param['MinT2']) or \
                                     math.isnan(sending_param['MaxT2']) or \
                                     math.isnan(sending_param['DeltaT2']) or \
                                     math.isnan(sending_param['I1']) or \
                                     math.isnan(sending_param['I2']) or \
                                     math.isnan(sending_param['T1']) or \
                                     math.isnan(sending_param['Dt']) or \
                                     math.isnan(sending_param['Tau'])
    
                window['-EnableT1-'].update(disabled = enable_t2)
                window['-EnableC1-'].update(disabled = enable_t2)
                window['-EnableC2-'].update(disabled = enable_t2)
                enable_t1 = window['-EnableT1-'].get()
                enable_c1 = window['-EnableC1-'].get()
                enable_c2 = window['-EnableC2-'].get()
                window['-InputMinT1-'].update(disabled   = enable_t2)
                window['-InputMaxT1-'].update(disabled   = enable_t2)
                window['-InputDeltaT1-'].update(disabled = enable_t2)
                window['-InputT1-'].update(disabled      = not enable_t2)
                window['-InputI1-'].update(disabled      = not enable_t2)
                window['-InputI2-'].update(disabled      = not enable_t2)
                window['-InputMinT2-'].update(disabled   = not enable_t2)
                window['-InputMaxT2-'].update(disabled   = not enable_t2)
                window['-InputDeltaT2-'].update(disabled = not enable_t2)
                window['-InputMinC1-'].update(disabled   = enable_t2)
                window['-InputMaxC1-'].update(disabled   = enable_t2)
                window['-InputDeltaC1-'].update(disabled = enable_t2)
                window['-InputMinC2-'].update(disabled   = enable_t2)
                window['-InputMaxC2-'].update(disabled   = enable_t2)
                window['-InputDeltaC2-'].update(disabled = enable_t2)
                window['-EnableManualSettings-'].update(disabled = True)
            elif enable_c1 and \
                 not enable_c2 and \
                 not enable_t1 and \
                 not enable_t2:
                sending_param['TaskType'] = dev.cmd.TaskType.ChangeCurrentLD1
                sending_param['MinC1']    = get_float(values, '-InputMinC1-')
                sending_param['MaxC1']    = get_float(values, '-InputMaxC1-')
                sending_param['DeltaC1']  = get_float(values, '-InputDeltaC1-')
                sending_param['T1']  = get_float(values, '-InputT1-')
                sending_param['T2']  = get_float(values, '-InputT2-')
                sending_param['I2']  = get_float(values, '-InputI2-')
                sending_param['Dt']  = get_float(values ,'-InputDeltaTime-')
                sending_param['Tau'] = get_float(values ,'-InputTau-')
                disableStartButton = math.isnan(sending_param['MinC1']) or \
                                     math.isnan(sending_param['MaxC1']) or \
                                     math.isnan(sending_param['DeltaC1']) or \
                                     math.isnan(sending_param['T1']) or \
                                     math.isnan(sending_param['T2']) or \
                                     math.isnan(sending_param['I2']) or \
                                     math.isnan(sending_param['Dt']) or \
                                     math.isnan(sending_param['Tau'])
    
                window['-EnableT1-'].update(disabled = enable_c1)
                window['-EnableT2-'].update(disabled = enable_c1)
                window['-EnableC2-'].update(disabled = enable_c1)
                enable_t1 = window['-EnableT1-'].get()
                enable_t2 = window['-EnableT2-'].get()
                enable_c2 = window['-EnableC2-'].get()
                window['-InputMinT1-'].update(disabled   = enable_c1)
                window['-InputMaxT1-'].update(disabled   = enable_c1)
                window['-InputDeltaT1-'].update(disabled = enable_c1)
                window['-InputT1-'].update(disabled      = not enable_c1)
                window['-InputT2-'].update(disabled      = not enable_c1)
                window['-InputI2-'].update(disabled      = not enable_c1)
                window['-InputMinT2-'].update(disabled   = enable_c1)
                window['-InputMaxT2-'].update(disabled   = enable_c1)
                window['-InputDeltaT2-'].update(disabled = enable_c1)
                window['-InputMinC1-'].update(disabled   = not enable_c1)
                window['-InputMaxC1-'].update(disabled   = not enable_c1)
                window['-InputDeltaC1-'].update(disabled = not enable_c1)
                window['-InputMinC2-'].update(disabled   = enable_c1)
                window['-InputMaxC2-'].update(disabled   = enable_c1)
                window['-InputDeltaC2-'].update(disabled = enable_c1)
                window['-EnableManualSettings-'].update(disabled = True)
            elif enable_c2 and \
             not enable_c1 and \
             not enable_t1 and \
             not enable_t2:
                sending_param['TaskType'] = dev.cmd.TaskType.ChangeCurrentLD2
                sending_param['MinC2']    = get_float(values, '-InputMinC2-')
                sending_param['MaxC2']    = get_float(values, '-InputMaxC2-')
                sending_param['DeltaC2']  = get_float(values, '-InputDeltaC2-')
                sending_param['T1']  = get_float(values, '-InputT1-')
                sending_param['T2']  = get_float(values, '-InputT2-')
                sending_param['I1']  = get_float(values, '-InputI1-')
                sending_param['Dt']  = get_float(values ,'-InputDeltaTime-')
                sending_param['Tau'] = get_float(values ,'-InputTau-')
                disableStartButton = math.isnan(sending_param['MinC2']) or \
                                     math.isnan(sending_param['MaxC2']) or \
                                     math.isnan(sending_param['DeltaC2']) or \
                                     math.isnan(sending_param['T1']) or \
                                     math.isnan(sending_param['T2']) or \
                                     math.isnan(sending_param['I1']) or \
                                     math.isnan(sending_param['Dt']) or \
                                     math.isnan(sending_param['Tau'])
    
                window['-EnableT1-'].update(disabled = enable_c2)
                window['-EnableT2-'].update(disabled = enable_c2)
                window['-EnableC1-'].update(disabled = enable_c2)
                enable_t1 = window['-EnableT1-'].get()
                enable_t2 = window['-EnableT2-'].get()
                enable_c1 = window['-EnableC1-'].get()
                window['-InputMinT1-'].update(disabled   = enable_c2)
                window['-InputMaxT1-'].update(disabled   = enable_c2)
                window['-InputDeltaT1-'].update(disabled = enable_c2)
                window['-InputI1-'].update(disabled      = not enable_c2)
                window['-InputT1-'].update(disabled      = not enable_c2)
                window['-InputT2-'].update(disabled      = not enable_c2)
                window['-InputMinT2-'].update(disabled   = enable_c2)
                window['-InputMaxT2-'].update(disabled   = enable_c2)
                window['-InputDeltaT2-'].update(disabled = enable_c2)
                window['-InputMinC1-'].update(disabled   = enable_c2)
                window['-InputMaxC1-'].update(disabled   = enable_c2)
                window['-InputDeltaC1-'].update(disabled = enable_c2)
                window['-InputMinC2-'].update(disabled   = not enable_c2)
                window['-InputMaxC2-'].update(disabled   = not enable_c2)
                window['-InputDeltaC2-'].update(disabled = not enable_c2)
                window['-EnableManualSettings-'].update(disabled = True)
            elif not enable_t1 and \
                 not enable_t2 and \
                 not enable_c1 and \
                 not enable_c2:
                sending_param = {}
    
                window['-EnableT1-'].update(disabled = False)
                window['-EnableT2-'].update(disabled = False)
                window['-EnableC1-'].update(disabled = False)
                window['-EnableC2-'].update(disabled = False)
                window['-InputMinT1-'].update(disabled   = True)
                window['-InputMaxT1-'].update(disabled   = True)
                window['-InputDeltaT1-'].update(disabled = True)
                window['-InputMinT2-'].update(disabled   = True)
                window['-InputMaxT2-'].update(disabled   = True)
                window['-InputDeltaT2-'].update(disabled = True)
                window['-InputMinC1-'].update(disabled   = True)
                window['-InputMaxC1-'].update(disabled   = True)
                window['-InputDeltaC1-'].update(disabled = True)
                window['-InputMinC2-'].update(disabled   = True)
                window['-InputMaxC2-'].update(disabled   = True)
                window['-InputDeltaC2-'].update(disabled = True)
                window['-InputT1-'].update(disabled      = True)
                window['-InputT2-'].update(disabled      = True)
                window['-InputI1-'].update(disabled      = True)
                window['-InputI2-'].update(disabled      = True)
                window['-EnableManualSettings-'].update(disabled = False)
    
            window['-InputDeltaTime-'].update(disabled = not enable_c1 and not enable_t1 and not enable_c2 and not enable_t2)
            window['-InputTau-'].update(disabled       = not enable_c1 and not enable_t1 and not enable_c2 and not enable_t2)
    
            window['-StartCycle-'].update(disabled = not enable_c1 and not enable_t1 and not enable_c2 and not enable_t2 or disableStartButton)
        
        if event == WIN_CLOSED or event == '-EXIT-':
            if use_client:
                p.terminate()
                conn.close()
                sck.close()
            dev.reset_port_settings(prt)
            break

        elif event == '-StartCycle-':
            if not enable_manual_settings:
                window['-StopCycle-'].update(disabled  = False)
                window['-StartCycle-'].update(disabled = True)
                window['-EnableT1-'].update(disabled   = True)
                window['-EnableC1-'].update(disabled   = True)
                window['-EnableT1-'].update(False)
                window['-EnableC1-'].update(False)
                window['-EnableT2-'].update(disabled = True)
                window['-EnableC2-'].update(disabled = True)
                window['-EnableT2-'].update(False)
                window['-EnableC2-'].update(False)
                window['-InputMinT1-'].update(disabled     = True)
                window['-InputMaxT1-'].update(disabled     = True)
                window['-InputDeltaT1-'].update(disabled   = True)
                window['-InputMinT2-'].update(disabled     = True)
                window['-InputMaxT2-'].update(disabled     = True)
                window['-InputDeltaT2-'].update(disabled   = True)
                window['-InputMinC1-'].update(disabled     = True)
                window['-InputMaxC1-'].update(disabled     = True)
                window['-InputDeltaC1-'].update(disabled   = True)
                window['-InputMinC2-'].update(disabled     = True)
                window['-InputMaxC2-'].update(disabled     = True)
                window['-InputDeltaC2-'].update(disabled   = True)
                window['-InputDeltaTime-'].update(disabled = True)
                window['-InputTau-'].update(disabled       = True)
                window['-InputT1-'].update(disabled        = True)
                window['-InputT2-'].update(disabled        = True)
                window['-InputI1-'].update(disabled        = True)
                window['-InputI2-'].update(disabled        = True)
                current_and_temperature_settings_available = False
                # TODO get task parameters from gui and put its to params

                if use_client:
                    jsondoc_str = json.dumps(sending_param)
                    jsondoc = bytearray()
                    jsondoc.extend(jsondoc_str.encode())
                    conn.sendall(jsondoc)

                start_task(prt)
            else:
                params['Temp_1'] = float(values['-InputT1-'])
                params['Temp_2'] = float(values['-InputT2-'])
                params['Iset_1'] = float(values['-InputI1-'])
                params['Iset_2'] = float(values['-InputI2-'])
                dev.send_control_parameters(prt, params)
            #print(sending_param)
        elif event == '-StopCycle-':
            window['-StopCycle-'].update(disabled = True)
            current_and_temperature_settings_available = True
            stop_task(prt)
        elif event == TIMEOUT_KEY:
            data = dev.request_data(prt)
    
            update_data_lists()
    
            window['-TOUT_1-'].update(gui.READ_TEMPERATURE_TEXT+' 1: '+shorten(data['Temp_1'])+' C')
            window['-TOUT_2-'].update(gui.READ_TEMPERATURE_TEXT+' 2: '+shorten(data['Temp_2'])+' C')
            window['-IOUT_1-'].update(gui.READ_CURRENT_TEXT+' 1: '+shorten(data['I1'])+' мА')
            window['-IOUT_2-'].update(gui.READ_CURRENT_TEXT+' 2: '+shorten(data['I2'])+' мА')
            window['-DateTime-'].update(data['datetime'].strftime('%d-%m-%Y %H:%M:%S:%f')[:-3])
            window['-TTerm1-'].update('T терм 1: '+shorten(data['Temp_Ext_1'])+' C')
            window['-TTerm2-'].update('T терм 2: '+shorten(data['Temp_Ext_2'])+' C')
            window['-3V3-'].update('3V3: '+shorten(data['MON_3V3'])+' В')
            window['-5V1-'].update('5V1: '+shorten(data['MON_5V1'])+' В')
            window['-5V2-'].update('5V2: '+shorten(data['MON_5V2'])+' В')
            window['-7V0-'].update('7V0: '+shorten(data['MON_7V0'])+' В')
    
            window['-GraphT1-'].draw_line((len(draw_data)-1, draw_data[-2]['Temp_1']), (len(draw_data), draw_data[-1]['Temp_1']), color='yellow')
            window['-GraphT2-'].draw_line((len(draw_data)-1, draw_data[-2]['Temp_2']), (len(draw_data), draw_data[-1]['Temp_2']), color='yellow')
            window['-GraphI1-'].draw_line((len(draw_data)-1, draw_data[-2]['I1']), (len(draw_data), draw_data[-1]['I1']), color='yellow')
            window['-GraphI2-'].draw_line((len(draw_data)-1, draw_data[-2]['I2']), (len(draw_data), draw_data[-1]['I2']), color='yellow')

            # When graphs reach end of X scale, start scrolling
            if len(draw_data)>=gui.GRAPH_POINTS_NUMBER:
                # Scroll graphs
                window['-GraphT1-'].move(-1, 0)
                window['-GraphT2-'].move(-1, 0)
                window['-GraphI1-'].move(-1, 0)
                window['-GraphI2-'].move(-1, 0)
                
                # Scroll back graphs' labels
                for key, sgn in axes_signs.items():
                    window[key].MoveFigure(sgn[0], 1, 0)
                    window[key].MoveFigure(sgn[1], 1, 0)  
    
    window.close()
    
    dev.close_connection(prt)


