import sys
import telnetlib
import time
import re
import os
import argparse

ports = []

class Eth_Module(object):

    def __init__(self, *, numberRelays=4, numberGpios=8, deviceIP, username, password):
        self.numberRelays= numberRelays
        self.numberGpios= numberGpios
        self.stateRelays=[0]*numberRelays
        self.stateGpios=[0]*numberGpios
        self.deviceIP=deviceIP
        self.username = username
        self.password = password
        self.iodir = int('{0:x}'.format(2**numberGpios - 1),16)

        #Create a new TELNET object
        self.telnet_obj = telnetlib.Telnet(self.deviceIP)
        
        result = self.telnet_obj.read_until(b">",timeout=1)

        if b"successfully" not in result :
            #Wait for login prompt from device and enter user name when prompted
            cred = self.telnet_obj.read_until(b"login", timeout=5)
            self.telnet_obj.write(self.username.encode('ascii') + b"\r\n")


            #Wait for password prompt and enter password when prompted by device
            self.telnet_obj.read_until(b"Password: ", timeout=5)
            self.telnet_obj.write(self.password.encode('ascii') + b"\r\n")

            
            #Wait for device response
            log_result = self.telnet_obj.read_until(b"successfully\r\n",timeout=1)
            self.telnet_obj.read_until(b">>",timeout=1)

       
            #Check if login attempt was successful
            if b"successfully" in log_result:
                print("\nLogged in successfully... Connected to device", self.deviceIP, "\n")
            elif b"denied" in log_result:
                print("Login failed!!!! Please check login credentials or Device IP Address\n\n")
                self.telnet_obj.close()
                exit(0)
                
        elif b"successfully" in result :
            print("\nLogged in successfully... Connected to device", self.deviceIP, "\n")
        else:
            print(1)
            print("Login failed!!!! Please check login credentials or Device IP Address\n\n")
            self.telnet_obj.close()
            exit(0)
        
        version =self.send_cmd(cmd="ver\r\n")
        self.version=version[:8]
        Id = self.send_cmd(cmd="id get\r\n")
        self.id = Id[:8]


    def send_cmd(self, cmd):
        # Send command
        self.telnet_obj.write(cmd.encode())

        # Get response command
        ret = self.telnet_obj.read_until(b">")
        #print(ret)
        response = ret.decode('UTF-8')
        
        return response


    ##--------- Device Info Functions ---------##
    def ver(self):
        """Return the device's version number as string value. """
        cmd="ver\r\n"
        response = self.send_cmd(cmd=cmd)
        return response[:-3]
        

    def id_get(self):
        """ Return the device id as string value. """
        cmd="id get\r\n"
        response = self.send_cmd(cmd=cmd)
        return response[:-3]
        

    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\n".format(cmdId)
        response = self.send_cmd(cmd=cmd)


    def usr_get(self):
        """ Return the device username as string value. """
        cmd="usr get\r\n"
        response = self.send_cmd(cmd=cmd)
        return response[:-3]
        

    def usr_set(self, cmdUser="xxxxxxxx"):
        """ Re-program the User to the new username. """
        if(len(cmdUser)>8):
            print("Username must be 1-8 characters in length\n")
            return
        cmd="usr set {}\r\n".format(cmdUser)
        self.send_cmd(cmd=cmd)
        

    def pass_get(self):
        """ Return the device password as string value. """
        cmd="pass get\r\n"
        response = self.send_cmd(cmd=cmd)
        return response[:-3]
        

    def pass_set(self, cmdPass="xxxxxxxx"):
        """ Re-program the User to the new password. """
        if(len(cmdPass)>8):
            print("Password must be 1-8 characters in length\n")
            return
        cmd="pass set {}\r\n".format(cmdPass)
        self.send_cmd(cmd=cmd)

        
    def info(self):
        """ Return the Power-On Status """
        cmd="info\r\n"
        response = self.send_cmd(cmd=cmd)
        info = response[:-4]
        return info


    ##---------- Reset all Relays ---------##
    def reset(self):
        cmd="reset\r\n"
        self.send_cmd(cmd=cmd)


    #----- functions for single relay -----#
    def relay_on(self, indexRelay):
        cmd="relay on "+ str(indexRelay) + "\r\n"
        self.send_cmd(cmd=cmd)

    def relay_off(self, indexRelay):
        cmd="relay off "+ str(indexRelay) + "\r\n"
        self.send_cmd(cmd=cmd)

    def relay_read(self, indexRelay):
        cmd="relay read "+str(indexRelay)+"\r\n"
        ret = self.send_cmd(cmd=cmd)
        if(ret.find("on") == 0):
            return ("on")
        elif(ret.find("off") == 0):
            return ("off")


    ##--------- Functions for all Relay ---------##
    def relay_readall(self):
        cmd="relay readall\r\n"
        response = self.send_cmd(cmd=cmd)
        return response[:-3]


    def relay_writeall(self,cmdRelay= "xxxx"):
        if(len(cmdRelay) == (self.numberRelays/4) or (self.numberRelays==4 and len(cmdRelay)==2)):
            for i in range(0,len(cmdRelay)):
                int(cmdRelay[i],16)
            cmd="relay writeall {}\r\n".format(cmdRelay)
            self.send_cmd(cmd=cmd)
        
        
    def relay_update(self,cmdRelay= "xxxxxxxx"):
        if(len(cmdRelay) > self.numberRelays):
            return
        for idxRelay in range(len(cmdRelay)):
            num=idxRelay
            if(idxRelay>=10 and self.numberRelays>8):
                num=chr(idxRelay+55)
            if cmdRelay[idxRelay] == 'o':
                self.relay_on(num)
            elif cmdRelay[idxRelay] == 'c':
                self.relay_off(num)


            
    ##--------- functions for single gpio ---------##
    def gpio_set(self, indexGpio):
        cmd="gpio set "+ str(indexGpio) + "\r\n"
        self.send_cmd(cmd=cmd)

    def gpio_clear(self, indexGpio):
        cmd="gpio clear "+ str(indexGpio) + "\r\n"
        self.send_cmd(cmd=cmd)

    def gpio_read(self, indexGpio):
        cmd="gpio read "+str(indexGpio)+"\r\n"
        response = self.send_cmd(cmd=cmd)
        return response[0]

    def gpio_status(self, indexGpio):
        cmd="gpio status "+str(indexGpio)+"\r\n"
        response = self.send_cmd(cmd=cmd)
        return response[0]
    
    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\n"
        response = self.send_cmd(cmd=cmd)
        return response[:-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\n".format(cmdGpio)
            self.send_cmd(cmd=cmd)
            
    
    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\n".format(cmdGpio)
            self.iodir=int(cmdGpio,16)
            self.send_cmd(cmd=cmd)

        
    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\n".format(cmdGpio)
            self.send_cmd(cmd=cmd)


    def notify_get(self):
        cmd="gpio notify get\r\n"
        response=self.send_cmd(cmd=cmd)
        return response[:-3]

    def notify_set(self, value):
        if(value == 1):
            cmd="gpio notify on\r\n"
        if(value == 0):
            cmd="gpio notify off\r\n"
        response=self.send_cmd(cmd=cmd)
        return response[:-3]
    
    
    ##---------- Adc read Function ----------##
    def adc_read(self, indexAdc):
        cmd="adc read "+ str(indexAdc) + "\r\n"
        response = self.send_cmd(cmd=cmd)
        return response[:-3]


    ##--------- Device Information ---------##
    """Return human readable string of the device's curent state."""
    def dev_info(self):
        return(
            "DeviceIP: {} | Id: {} | ver: {} | ports: {} | eol: {} \n".format(
                self.deviceIP,
                self.id,
                self.version,
                (self.numberRelays if not (self.numberGpios>8) else self.numberGpios),
                repr("\r\n")
            )
        )

    ##-------- Configuration command ----------##
    def reboot(self):
        cmd="reboot\r\n"
        self.send_cmd(cmd=cmd)


        
def main(args=None):
    scriptname = os.path.basename(__file__)
    parser = argparse.ArgumentParser(scriptname)
    parser.add_argument('--relays', default=4, type=int)
    parser.add_argument('--gpios', default=8, type=int)
    parser.add_argument('--deviceIP', default="192.168.1.0", type=str)
    parser.add_argument('--username', default="admin", type=str)
    parser.add_argument('--password', default="admin", type=str)
    parser.add_argument('--cmd', default="x", type=str)

    options = parser.parse_args()

    #This code works with python3 and above
    #Check the python version
    if sys.version_info[0] < 3:
        raise Exception("Python version 3.x required")

    Module= Eth_Module(numberRelays=options.relays, numberGpios=options.gpios, deviceIP=options.deviceIP, username=options.username, password=options.password)
    if(options.cmd=="x"):
        print(Module.dev_info())
              
    else:
        Module.relay_update(options.cmd)
            
    return 0

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

