#-*- coding:utf-8 -*-

"""
This file is part of OpenSesame.

OpenSesame is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

OpenSesame is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with OpenSesame.  If not, see <http://www.gnu.org/licenses/>.
"""

from libopensesame.py3compat import *

import os.path
import imp
from libopensesame import debug, misc
from libqtopensesame.misc import config
from qtpy import QtGui, QtWidgets, QtCore

available_themes = [u'default']

class theme:

	"""Handles the GUI theme"""

	default_icon_size = 32

	def __init__(self, main_window, theme=None):

		"""
		Constructor

		Arguments:
		main_window -- the main_window object

		Keyword arguments:
		theme -- the theme to be used or None to use config (default=None)
		"""

		self.main_window = main_window
		self.fallback_icon = QtGui.QIcon(os.path.join(misc.resource(u"theme"),
			u"fallback.png"))
		if theme is None:
			self.theme = config.get_config(u"theme")
		else:
			self.theme = theme
		self.theme_folder = misc.resource(os.path.join(u"theme", \
			self.theme))
		debug.msg(u"theme = '%s' (%s)" % (self.theme, self.theme_folder))
		# The theme folder must exist, and contain a file called __theme__.py,
		# if not, we fall back to the default theme, which is assumed to always
		# exist.
		if self.theme_folder is None or not os.path.exists(
			os.path.join(self.theme_folder, u'__theme__.py')):
			debug.msg(u"theme '%s' does not exist, using 'default'" % theme, \
				reason=u"warning")
			self.theme = u"default"
			self.theme_folder = misc.resource(os.path.join(u"theme", \
				self.theme))
		self.theme_info = os.path.join(self.theme_folder, u"__theme__.py")
		if os.path.exists(self.theme_info):
			info = imp.load_source(self.theme, self.theme_info)
			with open(os.path.join(self.theme_folder, info.qss)) as fd:
				self._qss = fd.read()
			self._icon_map = info.icon_map
			self._icon_theme = info.icon_theme
		self.load_icon_map()
		self.apply_theme(self.main_window)

	@property
	def experiment(self):
		return self.main_window.experiment

	def apply_theme(self, widget):

		"""
		Apply the theme to a QWidget, i.e. load the stylesheet and the icons

		Arguments:
		widget -- a QWidget
		"""

		if hasattr(widget, u'setStyleSheet'):
			widget.setStyleSheet(self._qss)
		if hasattr(widget, u"ui"):
			self.load_icons(widget.ui)

	def qfileicon(self, path):

		"""
		desc:
			Gets an filetype icon for a file.

		arguments:
			path:	The full path to the file. The file must exist.

		returns:
			type:	QIcon
		"""

		try:
			import fileinspector
		except ImportError:
			return QtWidgets.QFileIconProvider().icon(QtCore.QFileInfo(path))
		return QtGui.QIcon.fromTheme(
			fileinspector.translate_to_xdg(fileinspector.determine_type(path)),
			fallback=QtGui.QIcon.fromTheme(u'unknown'))

	def qicon(self, icon):

		"""
		desc:
			Gets an icon from the theme.

		arguments:
			icon:	One of the following:
					- A QIcon, which returned as is
					- The name of an image file
					- The name of a plug-in with a hardcoded icon
					- The name of an entry in the icon map
					- A theme icon name.

		returns:
			desc:	An icon, or a fallback icon if the specified wasn't found.
			type:	QIcon
		"""

		if isinstance(icon, QtGui.QIcon):
			return icon
		if os.path.isfile(icon):
			qicon = QtGui.QIcon()
			if icon.endswith(u'_large.png'):
				size = 32
			else:
				size = 16
			qicon.addFile(icon, size=QtCore.QSize(size, size))
			return qicon
		if hasattr(self, u'experiment') and (u'%s_large.png' % icon in \
			self.experiment.resources or '%s.png' in self.experiment.resources):
			qicon = QtGui.QIcon()
			if u'%s_large.png' % icon in self.experiment.resources:
				qicon.addFile(self.experiment.resource(
					u'%s_large.png' % icon), size=QtCore.QSize(32,32))
			if u'%s.png' % icon in self.experiment.resources:
				qicon.addFile(self.experiment.resource(u'%s.png' % icon),
					size=QtCore.QSize(16,16))
			return qicon
		if icon in self.icon_map:
			name = self.icon_map[icon][0]
		else:
			name = icon
		icon = QtGui.QIcon.fromTheme(name, self.fallback_icon)
		if icon.name() != name:
			debug.msg(u'missing icon %s' % name, reason=u'warning')
		return icon

	def qpixmap(self, icon, size=None):

		"""
		Get an icon from the theme

		Arguments:
		icon -- the icon name

		Keyword arguments:
		size -- the size of the icon or None for default (default=None)

		Returns:
		A QPixmap
		"""

		if isinstance(icon, basestring) and os.path.exists(icon):
			return QtGui.QPixmap(icon)
		if size is None:
			if icon in self.icon_map:
				size = self.icon_map[icon][1]
			else:
				size = self.default_icon_size
		size = QtCore.QSize(size, size)
		# Pixmap should never return a pixmap larger than the requested size.
		# However, this does happen on some versions of Qt4. Therefore, we check
		# the size, and scale if necessary.
		pixmap = self.qicon(icon).pixmap(size)
		if pixmap.size() != size:
			pixmap = pixmap.scaled(size)
		return pixmap

	def qlabel(self, icon, size=None):

		"""
		Get an icon from the theme

		Arguments:
		icon -- the icon name

		Keyword arguments:
		size -- the size of the icon or None for default (default=None)

		Returns:
		A QLabel
		"""

		l = QtWidgets.QLabel()
		l.setPixmap(self.qpixmap(icon, size=size))
		return l

	def load_icon_map(self):

		"""Load the icon map"""

		self.original_theme = QtGui.QIcon.themeName()
		if os.path.exists(os.path.join(self.theme_folder, self._icon_theme)):
			debug.msg(u"using custom icon theme")
			QtGui.QIcon.setThemeSearchPaths(QtGui.QIcon.themeSearchPaths() \
				+ [self.theme_folder])
			QtGui.QIcon.setThemeName(self._icon_theme)
		else:
			debug.msg(u"using default icon theme, icons may be missing", \
				reason=u"warning")
		self.icon_map = {}
		path = os.path.join(self.theme_folder, self._icon_map)
		debug.msg(path)
		with open(path) as fd:
			for l in fd:
				l = l.split(",")
				if len(l) == 3:
					try:
						size = int(l[2])
					except:
						size = 32
					alias = l[0].strip()
					name = l[1].strip()
					if alias in self.icon_map:
						debug.msg(u"alias '%s' already in icon map, overwriting" % \
							alias, reason=u"warning")
					self.icon_map[alias] = name, size

	def load_icons(self, ui):

		"""
		Add icons to all icon supporting widgets in a ui object

		Arguments:
		ui -- the ui object to load icons into
		"""

		for i in dir(ui):
			# Oddly enough, it can happend that getattr() fails on items that
			# have been returned by dir(). That's why we need a hasattr() as
			# well.
			if i in self.icon_map and hasattr(ui, i):
				a = getattr(ui, i)
				if hasattr(a, u"setIcon"):
					a.setIcon(self.qicon(i))
				elif hasattr(a, u"setPixmap"):
					a.setPixmap(self.qpixmap(i))

	def set_toolbar_size(self, size):

		"""
		Control the size of the icons in the toolbar

		Arguments:
		size -- a size in pixels
		"""

		self.main_window.ui.toolbar_main.setIconSize(QtCore.QSize(size, size))
		self.main_window.ui.toolbar_items.setIconSize(QtCore.QSize(size, size))
		self.main_window.ui.toolbar_items.build()

	def resource(self, fname):

		"""
		Retrieves the path to a resource within the theme folder.

		Arguments:
		fname	--	The resource filename.

		Returns:
		The full path to the resource file in the theme folder.
		"""

		return os.path.join(self.theme_folder, fname)
