import sys
import serial
import os
import argparse
import itertools
import serial.tools.list_ports

ports = []

def devices():
    while True:
        foundPorts = serial.tools.list_ports.comports()
        ComPort = []
        ComName = []
        Ch = []
        
        for port in foundPorts:
            if "2A19" == port.hwid[12:16]:
                ComPort.append(port[0])
                Ch.append(int(port[1].split('-')[3].split(' ')[0])) if(port[1].find('-')>0) else Ch.append(int(port[1].split(' ')[2]))
                ComName.append(port[1][:port[1].find('(')-1]) if(port[1].find('(')) else ComName.append(port[1].split('-')[1])
        return ComPort,Ch,ComName
            

class Gpio(object):
    def __init__(self, *, numberGpios=8, port="COM1", baudrate=921600, timeout=1, name):
        self.numberGpios= numberGpios
        self.stateGpios=[0]*numberGpios
        self.port=port
        self.name = name
        self.baudrate=baudrate
        self.timeout=timeout
        self.iodir = int('{0:x}'.format(2**numberGpios - 1),16)
        if(self.port == '/dev/ttyUSB1'):
            print("Please Enter Port argument....")
            exit(0)
        response =self.send_cmd(cmd="ver\r",noResponse= False, sizeResponse=25)
        self.version = response[5:-3]
        response =self.send_cmd(cmd="id get\r",noResponse= False, sizeResponse=25)
        self.id = response[8:-3]
        response =self.send_cmd(cmd="gpio readall\r",noResponse= False, sizeResponse=50)
        self.state = response[14:-3]
        

    def send_cmd(self, cmd, *, noResponse=True, sizeResponse=0):
        #Open port for communication
        serPort = serial.Serial(port=self.port, baudrate=self.baudrate, timeout=self.timeout)

        # Send command
        serPort.write(cmd.encode())

        # Get response command
        if not(noResponse):
            ret = serPort.read(sizeResponse)
            response = ret.decode('UTF-8')

        #Close the port
        serPort.close()

        if not(noResponse):
            return response


    ##--------- Device Info Functions ---------##
    def ver(self):
        """ Return the device's version number as string value. """
        cmd="ver\r"
        response = self.send_cmd(cmd=cmd, noResponse= False, sizeResponse=25)
        version = response[len(cmd)+1:-3]
        return version
        

    def id_get(self):
        """ Return the device id as string value. """
        cmd="id get\r"
        response = self.send_cmd(cmd=cmd, noResponse= False, sizeResponse=25)
        Id = response[len(cmd)+1:-3]
        return Id
        

    def id_set(self, cmdId="xxxxxxxx"):
        """ Re-program the device id to the value in new_id. """
        if(len(cmdId)!=8):
            print("Device ID must be 8 characters in length\n")
            return
        cmd="id set {}\r".format(cmdId)
        response = self.send_cmd(cmd=cmd, noResponse= False, sizeResponse=25)
        
        
    def info(self):
        """ Return the Power-On Status """
        cmd="info\r"
        response = self.send_cmd(cmd=cmd, noResponse= False, sizeResponse=115)
        info = response[len(cmd)+1:-3]
        return info


    ##--------- functions for single gpio ---------##
    def gpio_set(self, indexGpio):
        if(self.name=="USBG" and (self.numberGpios==8 and (indexGpio[0]!="0" or indexGpio[1]!="0"))):
           return
        elif(self.name=="USBG" and (self.numberGpios==16 and (indexGpio[0]!="0" or indexGpio[1]>"1"))):
            return
        elif(self.name=="USBG" and (self.numberGpios==32 and (indexGpio[0]!="0" or indexGpio[1]>"3"))):
           return
        cmd="gpio set "+ str(indexGpio) + "\r"
        self.send_cmd(cmd=cmd)

    def gpio_clear(self, indexGpio):
        if(self.name=="USBG" and (self.numberGpios==8 and (indexGpio[0]!="0" or indexGpio[1]!="0"))):
           return
        elif(self.name=="USBG" and (self.numberGpios==16 and (indexGpio[0]!="0" or indexGpio[1]>"1"))):
            return
        elif(self.name=="USBG" and (self.numberGpios==32 and (indexGpio[0]!="0" or indexGpio[1]>"3"))):
           return
        cmd="gpio clear "+ str(indexGpio) + "\r"
        self.send_cmd(cmd=cmd)

    def gpio_read(self, indexGpio):
        if(self.name=="USBG" and (self.numberGpios==8 and (indexGpio[0]!="0" or indexGpio[1]!="0"))):
           return
        elif(self.name=="USBG" and (self.numberGpios==16 and (indexGpio[0]!="0" or indexGpio[1]>"1"))):
            return
        elif(self.name=="USBG" and (self.numberGpios==32 and (indexGpio[0]!="0" or indexGpio[1]>"3"))):
           return
        cmd="gpio read "+str(indexGpio)+"\r"
        response = self.send_cmd(cmd=cmd, noResponse= False, sizeResponse=25)
        post = response.find("\n\r")
        post1 = response.find(">")
        if(post1>0):
            return response[post+2:post1-2]
        
        
    def gpio_status(self, indexGpio):
        if(self.name=="USBR" and (indexGpio[0]!="0" or indexGpio[1]!="0")):
            return
        elif(self.name=="USBG" and (self.numberGpios==8 and (indexGpio[0]!="0" or indexGpio[1]!="0"))):
           return
        elif(self.name=="USBG" and (self.numberGpios==16 and (indexGpio[0]!="0" or indexGpio[1]>"1"))):
           return
        elif(self.name=="USBG" and (self.numberGpios==32 and (indexGpio[0]!="0" or indexGpio[1]>"3"))):
           return
        cmd="gpio status "+str(indexGpio)+"\r"
        response = self.send_cmd(cmd=cmd, noResponse= False, sizeResponse=25)
        post = response.find("\n\r")
        post1 = response.find(">")
        if(post1>0):
            return response[post+2:post1-2]
        
        
    def gpio_setup(self, ports, direction):
        """Set up a single port as input or output port."""
        if(direction == "OUT"):
            direction = 0
        elif(direction == "IN"):
            direction = 1
        else:
            return
        
        for i in range(len(ports)):
            if(direction == 0):
                self.iodir = (self.iodir & (~(1<<ports[i])))
            else:
                self.iodir = (self.iodir | (1<<ports[i]))
            
        self.iodir='{0:x}'.format(self.iodir)
        if(len(self.iodir) != int(self.numberGpios/4)):
            self.iodir=self.iodir.zfill(int(self.numberGpios/4))
        self.gpio_iodir(self.iodir)

    
        
    ##--------- Functions for all gpios ---------##
 
    def gpio_readall(self):
        cmd="gpio readall\r"
        response = self.send_cmd(cmd=cmd, noResponse= False, sizeResponse=50)
        self.state = response[len(cmd)+1:-3]
        return response[len(cmd)+1:-3]
        

    def gpio_writeall(self,cmdGpio= "xxxxxxxx"):
        if(len(cmdGpio) == (self.numberGpios/4)):
            for i in range(0,len(cmdGpio)):
                int(cmdGpio[i],16)
            cmd="gpio writeall {}\r".format(cmdGpio)
            self.send_cmd(cmd=cmd, noResponse= False, sizeResponse=50)
            
    
    def gpio_iodir(self, cmdGpio= "xxxxxxxx"):
        if(len(cmdGpio) == (self.numberGpios/4)):
            for i in range(0,len(cmdGpio)):
                int(cmdGpio[i],16)
            cmd="gpio iodir {}\r".format(cmdGpio)
            self.iodir=int(cmdGpio,16)
            self.send_cmd(cmd=cmd, noResponse= False, sizeResponse=50)

        
    def gpio_iomask(self, cmdGpio= "xxxxxxxx"):
        if(len(cmdGpio) == (self.numberGpios/4)):
            for i in range(0,len(cmdGpio)):
                int(cmdGpio[i],16)
            cmd="gpio iomask {}\r".format(cmdGpio)
            self.send_cmd(cmd=cmd, noResponse= False, sizeResponse=50)
                
    def notify_get(self):
        cmd="gpio notify get\r"
        ret=self.send_cmd(cmd=cmd,noResponse= False, sizeResponse=50)
        return ret[17:-3]

    def notify_set(self, value):
        if(value == 1):
            cmd="gpio notify on\r"
        if(value == 0):
            cmd="gpio notify off\r"
        response=self.send_cmd(cmd=cmd,noResponse= False, sizeResponse=50)
        return response[16:-3]


    ##---------- Adc read Function ----------##
    def adc_read(self, indexAdc):
        if(self.name=="USBG" and (self.numberGpios==8 and (indexAdc[0]!="0" or indexAdc[1]!="0" or indexAdc[2]>"5"))):
            return
        elif(self.name=="USBG" and ((self.numberGpios==16 or self.numberGpios==32) and (indexAdc[1]!="0" or indexAdc[1]!="0" or indexAdc[2]>"6"))):
            return
        cmd="adc read "+ str(indexAdc) + "\r"
        response= self.send_cmd(cmd=cmd,noResponse= False, sizeResponse=25)
        post = response.find("\n\r")
        post1 = response.find(">")
        if(post1>0):
            return response[post+2:post1-2]


    ##---------- Power-On Functions ---------##
    def gpio_poweron(self, cmdGpioDir="xxxxxxxx", cmdGpioVal="xxxxxxxx"):
        if((len(cmdGpioDir) == (self.numberGpios/4)) and (len(cmdGpioDir)==(self.numberGpios/4))):
            for i in range(0,len(cmdGpioDir)):
                int(cmdGpioDir[i],16)
                int(cmdGpioVal[i],16)
            cmd="gpio poweron {} {}\r".format(cmdGpioDir, cmdGpioVal)
            self.send_cmd(cmd=cmd, noResponse= False, sizeResponse=50)
    

    ##--------- Device Information ---------##
    def dev_info(self):
        """Return human readable string of the device's curent state."""
        return (
            "dev: {} | id: {} | ver: {} | ports: {} | eol: {} | state: 0x{}".format(
                self.port,
                self.id,
                self.version,
                self.numberGpios,
                repr("\r"),
                self.state
            )
        )
        

def main(args=None):
    scriptname = os.path.basename(__file__)
    parser = argparse.ArgumentParser(scriptname)
    parser.add_argument('--gpios', default=8, type=int)
    parser.add_argument('--port', default="/dev/ttyUSB1", type=str)
    parser.add_argument('--timeout', default=1, type=int)
    parser.add_argument('--baudrate', default=921600, type=int)
    parser.add_argument('--name', default="USB", type=str)
    
    options = parser.parse_args()
    (port, ch, name)= devices()

    print("\nDiscovered devices:\n")
    for (port, ch, name) in itertools.zip_longest(port,ch,name):
        options.gpios=ch
        options.port=port
        options.name = name
        numatoGpio= Gpio(numberGpios=options.gpios, port=options.port, baudrate=options.baudrate, timeout=options.timeout, name=options.name)
        print(numatoGpio.dev_info())
        
    return 0

if __name__=="__main__":
    ret=main()
    os._exit(ret)
