import queue
import socket

import zmq
import globalvars
import tcptools as TCP
#TCP/IP client thread
def clientthread(conn):
    global lock,busy
    welcom_msg = '--> Welcome to the Time-Bin-temperature controller <--\n'
    welcom_msg = welcom_msg + ' '*(1024-len(welcom_msg))
    conn.send(welcom_msg.encode())
    print('\nopen thread...')

    #Connect to SPI thread via zmq tcp socket
    context = zmq.Context(1)
    SPIclient = context.socket(zmq.REQ)
    SPIclient.connect("tcp://localhost:5555")

    #Connect to each pid threads
    pidclient = [0]
    context = zmq.Context(1)
    pidclient[0] = context.socket(zmq.REQ)
    pidclient[0].connect("tcp://localhost:5556")


    while not globalvars.stopall:
        try:
            cmd_recv = conn.recv(1024)
        except:
            print('\nSocket broken')
            break
        if not cmd_recv:
            break
        if globalvars.stopall:
            break

        ans = ''

        #split command
        tmp = cmd_recv.decode().split('\t')

        try:
            tmp[2] = int(str2num(tmp[2]))
            tmp[3] = str2num(tmp[3])
        except IndexError:
            pass

        #print(tmp)

        if tmp[0] == 'CMD:':
            if tmp[1] == 'close':
                TCP.send_msg(conn,'ANS: Bye')
                break
            elif tmp[1] == 'IDLE':
                globalvars.pidrun[tmp[2]] = False
                TCP.send_msg(conn, 'I set PID {:.0f} to {:.0f}'.format(tmp[2],False))
            elif tmp[1] == 'LOCK':
                globalvars.pidrun[tmp[2]] = True
                #out_queue[tmp[2]].put(['lock'])
                pidclient[tmp[2]].send_string('lock')
                pidclient[tmp[2]].recv_string()
                TCP.send_msg(conn, 'I set PID {:.0f} to {:.0f}'.format(tmp[2],True))
            elif tmp[1] == 'SET_SETPOINT':
                #out_queue[tmp[2]].put(['set_setpoint',tmp[3]])
                pidclient[tmp[2]].send_string('set_setpoint\t%f' % tmp[3])
                pidclient[tmp[2]].recv_string()
                TCP.send_msg(conn, 'I set the setpoint for output {:.0f} to {: f}'.format(tmp[2],tmp[3]))
            elif tmp[1] == 'SET_KP':
                #out_queue[tmp[2]].put(['set_kp',tmp[3]])
                pidclient[tmp[2]].send_string('set_kp\t%f' % tmp[3])
                pidclient[tmp[2]].recv_string()
                TCP.send_msg(conn, 'I set kp for output {:.0f} to {: f}'.format(tmp[2],tmp[3]))
            elif tmp[1] == 'SET_KI':
                #out_queue[tmp[2]].put(['set_ki',tmp[3]])
                pidclient[tmp[2]].send_string('set_ki\t%f' % tmp[3])
                pidclient[tmp[2]].recv_string()
                TCP.send_msg(conn, 'I set ki for output {:.0f} to {: f}'.format(tmp[2],tmp[3]))
            elif tmp[1] == 'SET_KD':
                #out_queue[tmp[2]].put(['set_kd',tmp[3]])
                pidclient[tmp[2]].send_string('set_kd\t%f' % tmp[3])
                pidclient[tmp[2]].recv_string()
                TCP.send_msg(conn, 'I set kd for output {:.0f} to {: f}'.format(tmp[2],tmp[3]))
            elif tmp[1] == 'SET_OUTMAX':
                #out_queue[tmp[2]].put(['set_outmax',tmp[3]])
                pidclient[tmp[2]].send_string('set_outmax\t%f' % tmp[3])
                pidclient[tmp[2]].recv_string()
                TCP.send_msg(conn, 'I set maximal output voltage for output {:.0f} to {: f}'.format(tmp[2],tmp[3]))
            elif tmp[1] == 'GET_SETPOINT':
                #out_queue[tmp[2]].put(['get_setpoint'])
                pidclient[tmp[2]].send_string('get_setpoint')
                ans = float(pidclient[tmp[2]].recv_string())
                #ans = in_queue[tmp[2]].get()
                TCP.send_msg(conn, ans)
            elif tmp[1] == 'GET_ERROR':
                #out_queue[tmp[2]].put(['get_error'])
                pidclient[tmp[2]].send_string('get_error')
                ans = float(pidclient[tmp[2]].recv_string())
                #ans = in_queue[tmp[2]].get()
                TCP.send_msg(conn, ans)
            elif tmp[1] == 'GET_KP':
                #out_queue[tmp[2]].put(['get_kp'])
                pidclient[tmp[2]].send_string('get_kp')
                ans = float(pidclient[tmp[2]].recv_string())
                #ans = in_queue[tmp[2]].get()
                TCP.send_msg(conn, ans)
            elif tmp[1] == 'GET_KI':
                #out_queue[tmp[2]].put(['get_ki'])
                pidclient[tmp[2]].send_string('get_ki')
                ans = float(pidclient[tmp[2]].recv_string())
                #ans = in_queue[tmp[2]].get()
                TCP.send_msg(conn, ans)
            elif tmp[1] == 'GET_KD':
                #out_queue[tmp[2]].put(['get_kd'])
                pidclient[tmp[2]].send_string('get_kd')
                ans = float(pidclient[tmp[2]].recv_string())
                #ans = in_queue[tmp[2]].get()
                TCP.send_msg(conn, ans)
            elif tmp[1] == 'GET_PID_STATUS':
                ans = int(globalvars.pidrun[tmp[2]])
                TCP.send_msg(conn, ans)
            elif tmp[1] == 'GET_TEMP':
                if globalvars.pidrun[tmp[2]]:
                    #out_queue[tmp[2]].put(['get_temperatur'])
                    pidclient[tmp[2]].send_string('get_temperatur')
                    ans = float(pidclient[tmp[2]].recv_string())
                    #ans = in_queue[tmp[2]].get()
                    TCP.send_msg(conn, ans)
                else:
                    if tmp[2] == 0:
                        dev_chan = ['ADC', 0, 0, 4]


                    #SPIQueue.put(dev_chan)
                    SPIclient.send('\t'.join(str(x) for x in dev_chan).encode())
                    ans = SPIclient.recv().decode()
                    #ans = tcpqueue.get()
                    TCP.send_msg(conn, ans)

            elif tmp[1] == 'GET_RUN_VALUES':
                if globalvars.pidrun[tmp[2]]:
                    #out_queue[tmp[2]].put(['get_run_values'])
                    pidclient[tmp[2]].send_string('get_run_values')
                    ans = eval(pidclient[tmp[2]].recv_string())
                    #ans = in_queue[tmp[2]].get()
                    TCP.send_msg(conn, ans)
                else:
                    if tmp[2] == 0:
                        dev_chan = ['ADC', 0, 0, 4]


                    #SPIQueue.put(dev_chan)
                    #ans = tcpqueue.get()
                    SPIclient.send('\t'.join(str(x) for x in dev_chan).encode())
                    ans = float(SPIclient.recv().decode())
                    #adc value, 0, output voltage
                    TCP.send_msg(conn, [ans, 0, globalvars.output_volt[tmp[2]]])

            elif tmp[1] == 'GET_VOLTAGE':
                if globalvars.pidrun[tmp[2]]:
                    #out_queue[tmp[2]].put(['get_output'])
                    pidclient[tmp[2]].send_string('get_output')
                    ans = float(pidclient[tmp[2]].recv_string())
                    #ans = in_queue[tmp[2]].get()
                    TCP.send_msg(conn, ans)
                    globalvars.output_volt[tmp[2]] = ans
                else:
                    TCP.send_msg(conn, globalvars.output_volt[tmp[2]])

            elif tmp[1] == 'SET_VOLTAGE':
                if tmp[2] == 0:
                    dev_chan = ['DAC', 0, 0, 4, tmp[3]]
                elif tmp[2] == 1:
                    dev_chan = ['DAC', 0, 1, 4, tmp[3]]


                globalvars.output_volt[tmp[2]] = tmp[3]

                #SPIQueue.put(dev_chan)
                #ans = tcpqueue.get()
                SPIclient.send('\t'.join(str(x) for x in dev_chan).encode())
                ans = SPIclient.recv().decode()
                if ans == 'ACK':
                    TCP.send_msg(conn, 'I set voltage of output {:.0f} to {: f}'.format(tmp[2],tmp[3]))
                else:
                    print('Error!')
        else:
            print('Not a command!')
            #reply = reply + ' '*(1024 - len(reply))
            #conn.send(reply.encode())
        busy = False

    busy = False
    if not globalvars.stopall:
        print('close thread')
    else:
        print('close thread because of global stop')
    #reply = reply + ' '*(1024 - len(reply))
    #conn.sendall(reply.encode())
    conn.close()

#Function to convert byte string to int/float
def str2num(s):
    try:
        return int(s)
    except ValueError:
        try:
            return float(s)
        except ValueError:
            return 0
