"""
Base class for establishing a connection to the equipment.
"""
from __future__ import unicode_literals

from .record_types import EquipmentRecord
from .exceptions import MSLConnectionError
from .utils import (
    logger,
    convert_to_enum
)


class Connection(object):

    def __init__(self, record):
        """Base class for establishing a connection to the equipment.

        Do not instantiate this class directly. Use the
        :meth:`~.EquipmentRecord.connect` method to connect to the equipment.

        Parameters
        ----------
        record : :class:`.EquipmentRecord`
            A record from an :ref:`equipment-database`.
        """
        if not isinstance(record, EquipmentRecord):
            raise TypeError('Must pass in an {} object'.format(EquipmentRecord.__name__))
        self._record = record
        self._exception_handler = MSLConnectionError
        self._repr = '{}<{}|{}|{} at {}>'.format(
            self.__class__.__name__,
            self.equipment_record.manufacturer,
            self.equipment_record.model,
            self.equipment_record.serial,
            self.equipment_record.connection.address if self.equipment_record.connection else 'None'
        )
        self._str = '{}<{}|{}|{}>'.format(
            self.__class__.__name__,
            self.equipment_record.manufacturer,
            self.equipment_record.model,
            self.equipment_record.serial
        )

    @property
    def equipment_record(self):
        """:class:`.EquipmentRecord`: The information about the equipment.
        """
        return self._record

    def disconnect(self):
        """Disconnect from the equipment.

        This method should be overridden in the subclass if the subclass must implement
        tasks that need to be performed in order to safely disconnect from the equipment.

        For example:

            * to clean up system resources from memory (e.g., if using a manufacturer's SDK)
            * to configure the equipment to be in a state that is safe for people
              working in the lab when the equipment is not in use

        Note
        ----
        This method gets called automatically when the :class:`.Connection` object
        gets garbage collected, which happens when the reference count is 0.
        """
        pass

    def __repr__(self):
        return self._repr

    def __str__(self):
        return self._str

    def __del__(self):
        self.disconnect()

    def raise_exception(self, msg):
        """Raise a :exc:`~.MSLConnectionError`.

        Parameters
        ----------
        msg : :class:`str`
            The message to display when the exception is raised.
        """
        self.log_error('{!r} {}'.format(self, msg))
        raise self._exception_handler('{!r}\n{}'.format(self, msg))

    @staticmethod
    def convert_to_enum(obj, enum, prefix=None, to_upper=False, strict=True):
        """Convert `obj` to an Enum.

        Parameters
        ----------
        obj : :class:`object`
            Any object to be converted to the specified `enum`. Can be a
            value of member of the specified `enum`.
        enum : :class:`~enum.Enum`
            The :class:`~enum.Enum` object that `obj` should be converted to.
        prefix : :class:`str`, optional
            If `obj` is a :class:`str`, then ensures that `prefix` is included at
            the beginning of `obj` before converting `obj` to the `enum`.
        to_upper : :class:`bool`, optional
            If `obj` is a :class:`str`, then whether to change `obj` to
            be upper case before converting `obj` to the `enum`.
        strict : :class:`bool`, optional
            Whether errors should be raised. If :data:`False` and `obj` cannot
            be converted to `enum` then `obj` is returned and the error is
            logged.

        Returns
        -------
        :class:`~enum.Enum`
            The `enum`.

        Raises
        ------
        ValueError
            If `obj` is not in `enum` and `strict` is :data:`True`.
        """
        return convert_to_enum(obj, enum, prefix=prefix, to_upper=to_upper, strict=strict)

    @staticmethod
    def log_debug(msg, *args, **kwargs):
        """Log a debug message.

        All input parameters are passed to :meth:`~logging.Logger.debug`.
        """
        logger.debug(msg, *args, **kwargs)

    @staticmethod
    def log_info(msg, *args, **kwargs):
        """Log an info message.

        All input parameters are passed to :meth:`~logging.Logger.info`.
        """
        logger.info(msg, *args, **kwargs)

    @staticmethod
    def log_warning(msg, *args, **kwargs):
        """Log a warning message.

        All input parameters are passed to :meth:`~logging.Logger.warning`.
        """
        logger.warning(msg, *args, **kwargs)

    @staticmethod
    def log_error(msg, *args, **kwargs):
        """Log an error message.

        All input parameters are passed to :meth:`~logging.Logger.error`.
        """
        logger.error(msg, *args, **kwargs)

    @staticmethod
    def log_critical(msg, *args, **kwargs):
        """Log a critical message.

        All input parameters are passed to :meth:`~logging.Logger.critical`.
        """
        logger.critical(msg, *args, **kwargs)

    def set_exception_class(self, handler):
        """Set the exception-handler class for this :class:`Connection`.

        Parameters
        ----------
        handler : :class:`~.MSLConnectionError`
            A subclass of :class:`~.MSLConnectionError`

        Raises
        ------
        TypeError
            If the `handler` is not a subclass of :class:`~.MSLConnectionError`
        """
        if issubclass(handler, MSLConnectionError):
            self._exception_handler = handler
        else:
            raise TypeError('The exception handler must be a subclass of {}'.format(MSLConnectionError))

@@ -3003,26 +3003,8 @@
  obj
- : :class:%60object%60
 %0A   
@@ -3142,30 +3142,8 @@
 enum
- : :class:%60~enum.Enum%60
 %0A   

remove the data type for 'obj' and 'enum' in the docstring