#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
**notificationsManager.py**
**Platform:**
	Windows, Linux, Mac Os X.
**Description:**
	Defines the :class:`NotificationsManager` and :class:`Notification` classes.
**Others:**
"""
#**********************************************************************************************************************
#***	Future imports.
#**********************************************************************************************************************
from __future__ import unicode_literals
#**********************************************************************************************************************
#***	External imports.
#**********************************************************************************************************************
import time
from PyQt4.QtCore import Qt
from PyQt4.QtCore import QObject
from PyQt4.QtCore import pyqtSignal
from PyQt4.QtGui import QColor
#**********************************************************************************************************************
#***	Internal imports.
#**********************************************************************************************************************
import foundations.dataStructures
import foundations.exceptions
import foundations.verbose
from umbra.ui.widgets.notification_QLabel import Notification_QLabel
#**********************************************************************************************************************
#***	Module attributes.
#**********************************************************************************************************************
__author__ = "Thomas Mansencal"
__copyright__ = "Copyright (C) 2008 - 2014 - Thomas Mansencal"
__license__ = "GPL V3.0 - http://www.gnu.org/licenses/"
__maintainer__ = "Thomas Mansencal"
__email__ = "[email protected]"
__status__ = "Production"
__all__ = ["LOGGER", "Notification", "NotificationsManager"]
LOGGER = foundations.verbose.installLogger()
#**********************************************************************************************************************
#***	Module classes and definitions.
#**********************************************************************************************************************
[docs]class Notification(foundations.dataStructures.Structure):
	"""
	Defines a storage object for :class:`NotificationsManager` class notification.
	"""
	def __init__(self, **kwargs):
		"""
		Initializes the class.
		:param \*\*kwargs: message, time.
		:type \*\*kwargs: dict
		"""
		LOGGER.debug("> Initializing '{0}()' class.".format(self.__class__.__name__))
		foundations.dataStructures.Structure.__init__(self, **kwargs)
 
[docs]class NotificationsManager(QObject):
	"""
	Defines the Application notifications manager.
	"""
	# Custom signals definitions.
	notificationRegistered = pyqtSignal(Notification)
	"""
	This signal is emited by the :class:`NotificationsManager` class when a notification is registered. ( pyqtSignal )
	:return: Current registered notification.
	:rtype: Notification
	"""
	def __init__(self, parent=None):
		"""
		Initializes the class.
		"""
		LOGGER.debug("> Initializing '{0}()' class.".format(self.__class__.__name__))
		QObject.__init__(self, parent)
		# --- Setting class attributes. ---
		self.__container = parent
		self.__notifications = []
		self.__notifiers = []
		self.__notifiersStackPadding = 10
		self.__maximumNotifiers = 5
	#******************************************************************************************************************
	#***	Attributes properties.
	#******************************************************************************************************************
	@property
	def container(self):
		"""
		Property for **self.__container** attribute.
		:return: self.__container.
		:rtype: QObject
		"""
		return self.__container
	@container.setter
# Oncilla: Statement commented by auto-documentation process: 	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def container(self, value):
		"""
		Setter for **self.__container** attribute.
		:param value: Attribute value.
		:type value: QObject
		"""
		raise foundations.exceptions.ProgrammingError(
			"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "container"))
	@container.deleter
# Oncilla: Statement commented by auto-documentation process: 	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs]	def container(self):
		"""
		Deleter for **self.__container** attribute.
		"""
		raise foundations.exceptions.ProgrammingError(
			"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "container"))
 
	@property
	def notifications(self):
		"""
		Property for **self.__notifications** attribute.
		:return: self.__notifications.
		:rtype: list
		"""
		return self.__notifications
	@notifications.setter
# Oncilla: Statement commented by auto-documentation process: 	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def notifications(self, value):
		"""
		Setter for **self.__notifications** attribute.
		:param value: Attribute value.
		:type value: list
		"""
		raise foundations.exceptions.ProgrammingError(
			"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "notifications"))
	@notifications.deleter
# Oncilla: Statement commented by auto-documentation process: 	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs]	def notifications(self):
		"""
		Deleter for **self.__notifications** attribute.
		"""
		raise foundations.exceptions.ProgrammingError(
			"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "notifications"))
 
	@property
	def notifiers(self):
		"""
		Property for **self.__notifiers** attribute.
		:return: self.__notifiers.
		:rtype: list
		"""
		return self.__notifiers
	@notifiers.setter
# Oncilla: Statement commented by auto-documentation process: 	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def notifiers(self, value):
		"""
		Setter for **self.__notifiers** attribute.
		:param value: Attribute value.
		:type value: list
		"""
		raise foundations.exceptions.ProgrammingError(
			"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "notifiers"))
	@notifiers.deleter
# Oncilla: Statement commented by auto-documentation process: 	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs]	def notifiers(self):
		"""
		Deleter for **self.__notifiers** attribute.
		"""
		raise foundations.exceptions.ProgrammingError(
			"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "notifiers"))
 
	@property
	def notifiersStackPadding(self):
		"""
		Property for **self.__notifiersStackPadding** attribute.
		:return: self.__notifiersStackPadding.
		:rtype: int
		"""
		return self.__notifiersStackPadding
	@notifiersStackPadding.setter
# Oncilla: Statement commented by auto-documentation process: 	@foundations.exceptions.handleExceptions(AssertionError)
	def notifiersStackPadding(self, value):
		"""
		Setter for **self.__notifiersStackPadding** attribute.
		:param value: Attribute value.
		:type value: int
		"""
		if value is not None:
			assert type(value) is int, "'{0}' attribute: '{1}' type is not 'int'!".format("notifiersStackPadding",
																						  value)
			assert value >= 0, "'{0}' attribute: '{1}' need to be positive!".format("notifiersStackPadding", value)
		self.__notifiersStackPadding = value
	@notifiersStackPadding.deleter
# Oncilla: Statement commented by auto-documentation process: 	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	@property
	def maximumNotifiers(self):
		"""
		Property for **self.__maximumNotifiers** attribute.
		:return: self.__maximumNotifiers.
		:rtype: int
		"""
		return self.__maximumNotifiers
	@maximumNotifiers.setter
# Oncilla: Statement commented by auto-documentation process: 	@foundations.exceptions.handleExceptions(AssertionError)
	def maximumNotifiers(self, value):
		"""
		Setter for **self.__maximumNotifiers** attribute.
		:param value: Attribute value.
		:type value: int
		"""
		if value is not None:
			assert type(value) is int, "'{0}' attribute: '{1}' type is not 'int'!".format("maximumNotifiers",
																						  value)
			assert value > 0, "'{0}' attribute: '{1}' need to be exactly positive!".format("maximumNotifiers", value)
		self.__maximumNotifiers = value
	@maximumNotifiers.deleter
# Oncilla: Statement commented by auto-documentation process: 	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs]	def maximumNotifiers(self):
		"""
		Deleter for **self.__maximumNotifiers** attribute.
		"""
		raise foundations.exceptions.ProgrammingError(
			"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "maximumNotifiers"))
	#******************************************************************************************************************
	#***	Class methods.
	#****************************************************************************************************************** 
	def __iter__(self):
		"""
		Reimplements the :meth:`object.__iter__` method.
		:return: Notifications iterator.
		:rtype: object
		"""
		return iter(self.__notifications)
	def __len__(self):
		"""
		Reimplements the :meth:`object.__len__` method.
		:return: Notifications count.
		:rtype: int
		"""
		return len(self.__notifications)
	def __notifier__fadedOut(self):
		"""
		Defines the slot triggered by **Notification_QLabel** Widget when faded out.
		"""
		if self.sender() in self.__notifiers:
			self.__notifiers.pop(self.__notifiers.index(self.sender()))
	def __offsetNotifiers(self, offset):
		"""
		Offsets existing notifiers.
		:param offset: Offset.
		:type offset: int
		"""
		overallOffset = offset
		for notifier in self.__notifiers:
			notifier.verticalOffset = overallOffset
			notifier.refreshPosition()
			overallOffset += offset
[docs]	def listNotifications(self):
		"""
		Returns the registered notifications.
		:return: Notifications list.
		:rtype: list
		"""
		return [self.formatNotification(notification) for notification in self]
 
[docs]	def isNotificationRegistered(self, notification):
		"""
		Returns if the given notification is registered.
		:param notification: Notification.
		:type notification: unicode
		:return: Is notification registered.
		:rtype: bool
		"""
		return notification in self
 
[docs]	def registerNotification(self, notification):
		"""
		Registers given notification.
		:param notification: Notification to register.
		:type notification: Notification
		:return: Method success.
		:rtype: bool
		"""
		LOGGER.debug("> Registering notification: '{0}'.".format(notification))
		self.__notifications.append(notification)
		self.notificationRegistered.emit(notification)
		return True
 
[docs]	def notify(self, message, duration=3000, notificationClickedSlot=None, messageLevel="Information", **kwargs):
		"""
		Displays an Application notification.
		:param message: Notification message.
		:type message: unicode
		:param duration: Notification display duration.
		:type duration: int
		:param notificationClickedSlot: Notification clicked slot.
		:type notificationClickedSlot: object
		:param messageLevel: Message level ( "Information", "Warning", "Exception" ).
		:type messageLevel: unicode
		:param \*\*kwargs: Keywords arguments.
		:type \*\*kwargs: \*\*
		:return: Method success.
		:rtype: bool
		"""
		for notifier in self.__notifiers[self.__maximumNotifiers - 1:]:
			notifier.duration=150
			notifier.hideMessage()
		notification = Notification(message=message, time=time.time())
		self.registerNotification(notification)
		notifier = Notification_QLabel(self.__container, **kwargs)
		# Signals / Slots.
		notifier.fadedOut.connect(self.__notifier__fadedOut)
		self.__container.sizeChanged.connect(notifier.resizeEvent)
		if notificationClickedSlot:
			notifier.notificationClicked.connect(notificationClickedSlot)
		else:
			notifier.setAttribute(Qt.WA_TransparentForMouseEvents)
		notifier.showMessage(message, duration)
		self.__offsetNotifiers(-notifier.height() - self.__notifiersStackPadding)
		self.__notifiers.insert(0, notifier)
		if messageLevel == "Information":
			LOGGER.info("{0} | '{1}'.".format(self.__class__.__name__, self.formatNotification(notification)))
		elif messageLevel == "Warning":
			LOGGER.warning("!> {0} | '{1}'.".format(self.__class__.__name__, self.formatNotification(notification)))
		elif messageLevel == "Exception":
			LOGGER.error("!> {0} | '{1}'.".format(self.__class__.__name__, self.formatNotification(notification)))
		return True
 
[docs]	def warnify(self, message, duration=3000, notificationClickedSlot=None, **kwargs):
		"""
		Displays an Application notification warning.
		:param message: Notification message.
		:type message: unicode
		:param duration: Notification display duration.
		:type duration: int
		:param notificationClickedSlot: Notification clicked slot.
		:type notificationClickedSlot: object
		:param \*\*kwargs: Keywords arguments.
		:type \*\*kwargs: \*\*
		:return: Method success.
		:rtype: bool
		"""
		return self.notify(message,
						   duration,
						   notificationClickedSlot,
						   messageLevel="Warning",
						   color=QColor(220, 128, 64),
						   backgroundColor=QColor(32, 32, 32),
						   borderColor=QColor(220, 128, 64),
						   **kwargs)
 
[docs]	def exceptify(self, message, duration=3000, notificationClickedSlot=None, **kwargs):
		"""
		Displays an Application notification exception.
		:param message: Notification message.
		:type message: unicode
		:param duration: Notification display duration.
		:type duration: int
		:param notificationClickedSlot: Notification clicked slot.
		:type notificationClickedSlot: object
		:param \*\*kwargs: Keywords arguments.
		:type \*\*kwargs: \*\*
		:return: Method success.
		:rtype: bool
		"""
		return self.notify(message,
						   duration,
						   notificationClickedSlot,
						   messageLevel="Exception",
						   color=QColor(220, 64, 64),
						   backgroundColor=QColor(32, 32, 32),
						   borderColor=QColor(220, 64, 64),
						   **kwargs)