"""
handles the connections to the IIC ports.

This module consists of the core iic functionality.
This can handle reading and writing to an iic port.

| author     = "Seth Miers"
| copyright  = "Copyright, Xilinx Inc. All rights reserved."
| credits    = ["Seth Miers"]
| license    = "GPL"
| version    = "3.0"
| maintainer = "Seth Miers"
| email      = "sethm@xilinx.com"
| status     = "Development"


"""

##########################################
#               Imports                  #
##########################################

# --------------Packages---------------- #
# pylint: disable=import-error
# periphery isnt available on windows
try:
    from periphery import I2C
    from periphery import I2CError
except ImportError:
    pass

# --------------Local------------------- #

##########################################
#              Main Code                 #
##########################################


class IicComm():
    '''
    Handles connections to the iic ports available on
    the machine. The library does not have rudamentary
    bit-banging functions, so only the standard iic
    functions have been implemented.

    Args:
        iic_port (int): The iic port to conenct to. If
            no iic port is specified then it defaults
            to 0

    Attributes:
        iic (I2C): The current iic connection

    TODO:
        * The iiccomm does not work on windows, add
          in aproprate handling for that
        * Add logger in
    '''
    def __init__(self, iic_port=0):
        self.set_iic_port(iic_port)

    def set_iic_port(self, iic_port):
        '''
        Setup the iic on the specified port
        '''
        self.iic = I2C("/dev/i2c-" + str(iic_port))

    def iic_recv(self, device_address, receive_count):
        '''
        Handles a basic iic receive command.

        Args:
            device_address (int): The address to use
                when sending a receive command
            receive_count (int): The number of bytes
                to receive

        Attributes:
            msg (I2C.Message): The receive message that
                was trasnferred.

        Returns
            list: A list of all the bytes received. This will
                be 'None' if a nack was found.
        '''
        msg = self.iic_msg(device_address, [], receive_count)
        if msg is not None:
            return msg[0].data
        return None

    def iic_send(self, device_address, message):
        '''
        Handles a basic iic send command.

        Args:
            device_address (int): The address to use
                when sending a send command
            message (list): A list of bytes that will
                be sent over the iic

        Attributes:
            msg (I2C.Message): The send message that
                was trasferred.

        Returns
            list: A list of all the bytes sent. This will
                be 'None' if a nack was found.
        '''
        msg = self.iic_msg(device_address, message, 0)
        if msg is not None:
            return msg[0].data
        return None

    def iic_ping(self, device_address):
        '''
        Handles a basic iic ping command. This is simply
        a iic send command but only sending one byte of
        0xff data.

        Args:
            device_address (int): The address to use
                when sending a send command

        Returns
            list: A list of all the bytes sent. This will
                be 'None' if a nack was found.
        '''
        return self.iic_send(device_address, [0xff])

    def iic_send_rs_recv(self, device_address, message, receive_count):
        '''
        Handles a basic iic repeated start command.

        Args:
            device_address (int): The address to use
                when sending a send command
            message (list): A list of bytes that will
                be sent over the iic
            receive_count (int): The number of bytes
                to receive

        Attributes:
            msg (I2C.Message): The repeated start message that
                was transferred.

        Returns
            list: A list of all the bytes received. This will
                be 'None' if a nack was found.
        '''
        msg = self.iic_msg(device_address, message, receive_count)
        if msg is not None:
            return msg[1].data
        return None

    def iic_msg(self, device_address, message, receive_count):
        '''
        Formats the i2c message depending on what needs to be
        sent and what needs to be received.

        Args:
            device_address (int): The address to use
                when sending data.
            message (list): A list of bytes that will
                be sent over the iic.
            receive_count (int): The number of bytes
                to receive.

        Attributes:
            msg (I2C.Message): The message that will be used to
                acomplish the task based on the args.

        Returns
            I2C.Message: A formatted iic message is returned. If
                the message was unable to be transferred (a nack
                is found) then a 'None' will be returned.
        '''
        if receive_count == 0:
            msg = [I2C.Message(message)]
        elif message == []:
            msg = [I2C.Message([0x00]*receive_count, read=True)]
        else:
            msg = [I2C.Message(message), I2C.Message([0x00]*receive_count,
                                                     read=True)]
        try:
            self.iic.transfer(device_address, msg)
            return msg
        except I2CError:
            return None
