#Distributed under the MIT licesnse.
#Copyright (c) 2014 Dave McCoy (dave.mccoy@cospandesign.com)

#Permission is hereby granted, free of charge, to any person obtaining a copy of
#this software and associated documentation files (the "Software"), to deal in
#the Software without restriction, including without limitation the rights to
#use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
#of the Software, and to permit persons to whom the Software is furnished to do
#so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE.

""" UART

Facilitates communication with the UART core independent of communication
medium

For more details see:

http://wiki.cospandesign.com/index.php?title=Wb_uart

"""

__author__ = 'dave.mccoy@cospandesign.com (Dave McCoy)'

import sys
import os
import time

from array import array as Array

import driver

COSPAN_DESIGN_UART_MODULE = 0x01

#Register Constants
CONTROL             = 0
STATUS              = 1
PRESCALER           = 2
CLOCK_DIVIDER       = 3
WRITE_AVAILABLE     = 4
WRITE_DATA          = 5
READ_COUNT          = 6
READ_DATA           = 7

#Control Bit values
CONTROL_RESET       = 0
CONTROL_RTS_CTS_FC  = 1
CONTROL_DTS_DSR_FC  = 2
CONTROL_INT_READ    = 3
CONTROL_INT_WRITE   = 4

#Status Bit values
STATUS_OVFL_TX      = 0
STATUS_OVFL_RX      = 1
STATUS_UFL_RX       = 2
STATUS_INT_READ     = 3
STATUS_INT_WRITE    = 4


class UARTError (Exception):
    """UART Error:

    Errors associated with UART
        UART Bus Busy
        Incorrect Settings
    """
    pass


class UART(driver.Driver):
    """UART
    """

    @staticmethod
    def get_abi_class():
        return 0

    @staticmethod
    def get_abi_major():
        return driver.get_device_id_from_name("uart")

    @staticmethod
    def get_abi_minor():
        return COSPAN_DESIGN_UART_MODULE

    def __init__(self, nysa, urn, debug = False):
        super(UART, self).__init__(nysa, urn, debug)

    def get_control(self):
        """get_control

        reads the control register

        Args:
          Nothing

        Return:
          32-bit control register value

        Raises:
          NysaCommError: Error in communication
        """
        return self.read_register(CONTROL)

    def set_control(self, control):
        """set_control

        write the control register

        Args:
          control: 32-bit control value

        Return:
          Nothing

        Raises:
          NysaCommError: Error in communication
        """
        self.write_register(CONTROL, control)

    def get_status(self):
        """get_status

        get the status of the UART

        *** NOTE:
        *** because the status is reset by the core
        *** the status should only be read once
        *** Read the status with get_status
        *** then perform the tests on the status

        Args:
          Nothing

        Return:
          32-bit status value

        Raises:
          NysaCommError: Error in communication
        """
        self.status =  self.read_register(STATUS)
        return self.status

    def reset(self):
        self.get_status()
        self.set_control(0)
        self.set_register_bit(CONTROL, CONTROL_RESET)

    def is_read_overflow(self):
        """is_read_overflow

        if read overfow

        *** NOTE:
        *** because the status is reset by the core
        *** the status should only be read once
        *** Read the status with get_status
        *** then perform the tests on the status

        Args:
          Nothing

        Return:
          True: empty
          False: not empty

        Raises:
          NysaCommError
        """
        #status = self.get_status()
        if ((self.status & STATUS_OVFL_RX) > 0):
            return True
        return False

    def is_write_overflow(self):
        """is_write_overflow

        if write is buffer overflowed

        *** NOTE:
        *** because the status is reset by the core
        *** the status should only be read once
        *** Read the status with get_status
        *** then perform the tests on the status

        Args:
          Nothing

        Return:
          True: Overflow
          False: No Overflow

        Raises:
          NysaCommError
        """
        #status = self.get_status()
        if ((self.status & STATUS_OVFL_TX) > 0):
            return True
        return False

    def is_read_underflow(self):
        """is_read_underflow

        Read too many bytes from the read

        *** NOTE:
        *** because the status is reset by the core
        *** the status should only be read once
        *** Read the status with get_status
        *** then perform the tests on the status

        Args:
          Nothing

        Return:
          True: read underflow
          False: not read underflow

        Raises:
          NysaCommError
        """
        #status = self.get_status()
        if ((self.status & STATUS_UFL_RX) > 0):
            return True
        return False

    def is_read_interrupt(self):
        """is_read_interrupt

        test if a read interrupt has occured

        *** NOTE:
        *** because the status is reset by the core
        *** the status should only be read once
        *** Read the status with get_status
        *** then perform the tests on the status

        Args:
          Nothing

        Return:
          True: read interrupt
          False: read interrupt did not occure

        Raises:
          NysaCommError

        """
        #status = self.get_status()
        if ((self.status & STATUS_INT_READ) > 0):
            return True
        return False

    def is_write_interrupt(self):
        """is_write_interrupt

        test if a write interrupt has occured

        *** NOTE:
        *** because the status is reset by the core
        *** the status should only be read once
        *** Read the status with get_status
        *** then perform the tests on the status

        Args:
          Nothing

        Return:
          True: write interrupt
          False: not write interrupt

        Raises: