import sys
import time
import socket
import threading

import queue
import zmq

import pin as GPIO
GPIO.config('./config.json')

import MAX11270 as adc
import mcp4822 as dac
import PID as pid
import tcptools as TCP
import spithread as SPIThread
import clientthread as clientThread
import globalvars
######################################################################
#TCP/IP
HOST = '' #Symbolic name
PORT = 2000

######################################################################
# GPIO conf
GPIO.setmode(GPIO.BCM)
ADC_CLK_PIN  = 21
ADC_MOSI_PIN = 20
ADC_MISO_PIN = 19
ADC_CS_PIN   = 17       #ADC 0 -> input 0,1
ADC_SYNC_PIN = 27
ADC_RSTB_PIN = 22

DAC_CLK_PIN  = 11
DAC_MOSI_PIN = 10
DAC_CS_PIN   =  8       
DAC_LDAC_PIN =  5


#----nonglobals-------
# this is the array of threads
t = [] # this is the array of threads

######################################################################

#Workaround for Windows to handle KeyboardInterrupt
def workaround():
    try:
        while True: input()
    except (KeyboardInterrupt, EOFError):
        socket.socket().connect((HOST,PORT))
######################################################################
#start program

# Setting up server
lock = threading.Lock() #create lock functionalities
print('\nCreate socket')
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#create socket
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
print('   ...done')
print('\nBind socket...')
#bind socket
try:
    sock.bind((HOST,PORT))
except socket.error as msg:
    print('   ...Bind failed. Error Code : '+str(msg.errno)+' Message : '+ msg.strerror)
    sock.close()
    sys.exit()
print('   ...done')
print('\nOpen socket listener...')
sock.listen(10)
print('   ...done')

# Setting up I/O
adc0 = adc.MAX11270(ADC_CLK_PIN, ADC_MOSI_PIN, ADC_MISO_PIN, ADC_CS_PIN, ADC_SYNC_PIN, ADC_RSTB_PIN)


dac0 = dac.MCP4822(DAC_CLK_PIN, DAC_MOSI_PIN, DAC_CS_PIN, DAC_LDAC_PIN, nullVoltage=1.29)


pid_obj     = [pid.PID(0, 0, 0, 1, 0)]


#Start spithread
#new_t = threading.Thread(target=SPIThread.spithread,args=([adc0, adc1], [dac0, dac1], SPIQueue, pidqueue))
new_t = threading.Thread(target=SPIThread.spithread,args=([adc0], [dac0],))
new_t.deamon = True
new_t.start()
t.append(new_t) #append to array of threads

#Start pid thread
for i in range(len(pid_obj)):
    new_t = threading.Thread(target=pid.run,args=(pid_obj[i], 100,i))
    new_t.deamon = True
    new_t.start()
    t.append(new_t) #append to array of threads

#Start workaround only on Windows
if sys.platform.startswith('win'):
    new_t = threading.Thread(target=workaround,args=())
    new_t.deamon = True
    new_t.start()
    t.append(new_t) #append to array of threads

print('\nStart waiting for connections...')
while True:
    conn = None
    try:
        #wait to accept a connection - blocking call
        print('\nWait for connection...')
        conn, addr = sock.accept()
        print('Connected with ' + addr[0] + ':' + str(addr[1]))
        print('\nOpen client thread...')
        new_t = threading.Thread(target=clientThread.clientthread,args=(conn,))
        new_t.deamon = True
        new_t.start()
        t.append(new_t) #append to array of threads
        print('   ...done')
        print('\nCleanup threads...')
        t = [this_t for this_t in t if this_t.is_alive()]#cleanup t
        print('   ...done')

    except (KeyboardInterrupt,SystemExit):  #Ctrl + break for keyboard interrupt (windows)
        print('\nKeyboard interrupt...')
        print('\nCleanup threads...')
        t = [this_t for this_t in t if this_t.is_alive()]#cleanup t
        print('   ...done')
        print('\nClosing socket...')
        sock.close()
        print('   ...done')
        print('\nWaiting for threads to close...')
        globalvars.stopall = True

        #Send end command to all threads
        #SPIQueue.put(['END'])
        #Connect to SPI thread via zmq tcp socket
        context = zmq.Context()
        SPIclient = context.socket(zmq.REQ)
        SPIclient.connect("tcp://localhost:5555")
        SPIclient.send("END\t".encode())
        msg = SPIclient.recv()

        for this_t in t: #wait for threads to close
            this_t.join()
        print('   ...done')
        GPIO.cleanup()
        sys.exit()
