#! /usr/bin/env python3
# -*- coding: utf-8 -*-

#	Copyright 2011-2013, Marten de Vries
#
#	This file is part of OpenTeacher.
#
#	OpenTeacher 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.
#
#	OpenTeacher 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 OpenTeacher.  If not, see <http://www.gnu.org/licenses/>.

import weakref
import contextlib

def installQtClasses():
	global ButtonsGroupBox, StartWidget, LargeStartWidgetButton

	class LargeStartWidgetButton(QtWidgets.QToolButton):
		def __init__(self, *args, **kwargs):
			super().__init__(*args, **kwargs)

			#the label that handles the word wrapping
			self._label = QtWidgets.QLabel(self)
			self._label.setWordWrap(True)
			self._label.setIndent(32)
			self._label.setMargin(10)
			self._label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)

			#setup the button sizes etc. correctly
			self.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
			self.setIconSize(QtCore.QSize(32, 32))
			self.setSizePolicy(
				QtWidgets.QSizePolicy.MinimumExpanding,
				QtWidgets.QSizePolicy.Fixed
			)
			self.setMinimumSize(70, 70)
			#width: unlimited (Qt default). Height: fixed
			self.setMaximumSize(16777215, 70)

		def resizeEvent(self, *args, **kwargs):
			#resize the label when the button resizes
			result = super().resizeEvent(*args, **kwargs)
			self._label.resize(self.size())
			return result

		def setText(self, text):
			#set the text on the label instead of the button
			self._label.setText(text)

	class SmallStartWidgetButton(QtWidgets.QToolButton):
		def __init__(self, *args, **kwargs):
			super().__init__(*args, **kwargs)

			self.setAutoRaise(True)
			self.setSizePolicy(
				QtWidgets.QSizePolicy.MinimumExpanding,
				QtWidgets.QSizePolicy.MinimumExpanding,
			)
			self.setIcon(QtWidgets.QCommandLinkButton().icon())
			self.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)

	class ButtonsGroupBox(QtWidgets.QGroupBox):
		def __init__(self, customButtons=None, *args, **kwargs):
			super().__init__(*args, **kwargs)

			self._buttons = {}

			self._smallLayout = QtWidgets.QVBoxLayout()
			self._largeLayout = QtWidgets.QGridLayout()

			if not customButtons:
				customButtons = []
			self._customButtons = customButtons

			layout = QtWidgets.QVBoxLayout()
			layout.addLayout(self._largeLayout)
			layout.addLayout(self._smallLayout)
			self.setLayout(layout)

		@staticmethod
		def _emptyLayout(layout):
			while True:
				item = layout.takeAt(0)
				if not item:
					break
				if item.widget():
					item.widget().setParent(None)

		def _updateLayout(self):
			self._emptyLayout(self._largeLayout)
			self._emptyLayout(self._smallLayout)

			i = 0
			j = 0
			for button, desc in sorted(self._buttons.items(), key=lambda data: data[1]["priority"]):
				if desc["size"] == "large":
					#make button
					qtButton = LargeStartWidgetButton()
					qtButton.setIcon(QtGui.QIcon(desc["icon"]))
					#insert into layout
					self._largeLayout.addWidget(qtButton, i, j)
					j += 1
					if j > 1:
						j = 0
						i += 1
				else:
					#make button
					qtButton = SmallStartWidgetButton()
					#insert into layout
					self._smallLayout.addWidget(qtButton)

				#do stuff common to buttons of both sizes
				qtButton.setText(desc["text"])
				#lambda to remove some qt argument. The second lambda so it
				#works as expected in a for-loop.
				qtButton.clicked.connect(
					(lambda button: lambda: button.clicked.send())(button)
				)

			for button in self._customButtons:
				self._smallLayout.addWidget(button)

		def addButton(self, button):
			self._buttons[button] = {
				"text": "",
				"icon": "",
				"priority": 0,
				"size": "large",
			}
			button.changeText.handle(lambda t: self._updateText(button, t))
			button.changeIcon.handle(lambda i: self._updateIcon(button, i))
			button.changePriority.handle(lambda p: self._updatePriority(button, p))
			button.changeSize.handle(lambda s: self._updateSize(button, s))
			self._updateLayout()

		def removeButton(self, button):
			del self._buttons[button]
			self._updateLayout()

		def _updateText(self, button, text):
			self._buttons[button]["text"] = text
			self._updateLayout()

		def _updateIcon(self, button, icon):
			self._buttons[button]["icon"] = icon
			self._updateLayout()

		def _updatePriority(self, button, priority):
			self._buttons[button]["priority"] = priority
			self._updateLayout()

		def _updateSize(self, button, size):
			self._buttons[button]["size"] = size
			self._updateLayout()

	class LoadFromInternetButton(SmallStartWidgetButton):
		def __init__(self, *args, **kwargs):
			super().__init__(*args, **kwargs)

			self.setPopupMode(QtWidgets.QToolButton.InstantPopup)

			self._buttons = {}

		def addButton(self, button):
			self._buttons[button] = {
				"text": "",
				"priority": 0,
			}
			button.changeText.handle(lambda t: self._updateText(button, t))
			button.changePriority.handle(lambda p: self._updatePriority(button, p))
			self._updateMenu()

		def removeButton(self, button):
			del self._buttons[button]
			self._updateMenu()

		def _updateText(self, button, text):
			self._buttons[button]["text"] = text
			self._updateMenu()

		def _updatePriority(self, button, priority):
			self._buttons[button]["priority"] = priority
			self._updateMenu()

		def _updateMenu(self):
			menu = QtWidgets.QMenu(self)
			sortedButtons = sorted(
				self._buttons.items(),
				key=lambda buttonAndDesc: buttonAndDesc[1]["priority"]
			)
			for button, desc in sortedButtons:
				action = menu.addAction(desc["text"])
				#first lambda to remove some qt argument. The second
				#lambda so it works as expected in the for loop.
				action.triggered.connect(
					(lambda button: lambda: button.clicked.send())(button)
				)
			self.setMenu(menu)

	class StartWidget(QtWidgets.QSplitter):
		def __init__(self, recentlyOpenedViewer, *args, **kwargs):
			super().__init__(*args, **kwargs)

			self.loadFromInternetButton = LoadFromInternetButton()

			self.createLessonGroupBox = ButtonsGroupBox()
			self.loadLessonGroupBox = ButtonsGroupBox([self.loadFromInternetButton])
			self.helpGroupBox = ButtonsGroupBox()

			openLayout = QtWidgets.QVBoxLayout()
			openLayout.addWidget(self.createLessonGroupBox)
			openLayout.addWidget(self.loadLessonGroupBox)
			openLayout.addWidget(self.helpGroupBox)
			openLayout.addStretch()

			left = self.style().pixelMetric(QtWidgets.QStyle.PM_LayoutLeftMargin)
			openLayout.setContentsMargins(left, 0, 0, 0)

			openWidget = QtWidgets.QWidget(self)
			openWidget.setLayout(openLayout)

			self.addWidget(openWidget)

			self.setStretchFactor(0, 7)

			if recentlyOpenedViewer:
				recentlyOpenedLayout = QtWidgets.QVBoxLayout()
				recentlyOpenedLayout.addWidget(recentlyOpenedViewer)

				self.recentlyOpenedGroupBox = QtWidgets.QGroupBox()
				self.recentlyOpenedGroupBox.setLayout(recentlyOpenedLayout)

				self.addWidget(self.recentlyOpenedGroupBox)
				self.setStretchFactor(1, 2)

			self.retranslate()

		def retranslate(self):
			self.createLessonGroupBox.setTitle(_("Create lesson:"))
			self.loadLessonGroupBox.setTitle(_("Load lesson:"))
			self.helpGroupBox.setTitle(_("Help"))
			self.loadFromInternetButton.setText(_("Load from the internet"))
			with contextlib.suppress(AttributeError):
				self.recentlyOpenedGroupBox.setTitle(_("Recently opened:"))

		def _widgetForButton(self, button):
			return {
				"create": self.createLessonGroupBox,
				"load": self.loadLessonGroupBox,
				"help": self.helpGroupBox,
				"load-from-internet": self.loadFromInternetButton,
			}.get(button.category)

		def addButton(self, button):
			widget = self._widgetForButton(button)
			if widget:
				widget.addButton(button)

		def removeButton(self, button):
			widget = self._widgetForButton(button)
			if widget:
				widget.removeButton(button)

class StartWidgetModule:
	def __init__(self, moduleManager, *args, **kwargs):
		super().__init__(*args, **kwargs)
		self._mm = moduleManager

		self.type = "startWidget"

		self.requires = (
			self._mm.mods(type="buttonRegister"),
		)
		self.uses = (
			self._mm.mods(type="recentlyOpenedViewer"),
			self._mm.mods(type="translator"),
		)
		self.filesWithTranslations = ("startWidget.py",)

	def createStartWidget(self):
		"""By calling this method, you need to be able to guarantee that
		   there's already a QApplication active. E.g. by depending on
		   'ui', or by being the module that manages the QApplication...

		"""
		try:
			recentlyOpenedViewer = self._modules.default(
				"active",
				type="recentlyOpenedViewer"
			).createViewer()
		except IndexError:
			recentlyOpenedViewer = None
		widget = StartWidget(recentlyOpenedViewer)

		self._register.addButton.handle(widget.addButton)
		self._register.removeButton.handle(widget.removeButton)

		self._activeWidgets.add(weakref.ref(widget))
		return widget

	def _retranslate(self):
		global _
		global ngettext

		try:
			translator = self._modules.default("active", type="translator")
		except IndexError:
			_, ngettext = str, lambda a, b, n: a if n == 1 else b
		else:
			_, ngettext = translator.gettextFunctions(
				self._mm.resourcePath("translations")
			)

		for ref in self._activeWidgets:
			widget = ref()
			if widget is not None:
				widget.retranslate()

	def enable(self):
		global QtCore, QtGui, QtWidgets
		try:
			from PyQt5 import QtCore, QtGui, QtWidgets
		except ImportError:
			return
		installQtClasses()

		self._modules = set(self._mm.mods(type="modules")).pop()
		self._register = self._modules.default("active", type="buttonRegister")

		self._activeWidgets = set()

		#load translator
		try:
			translator = self._modules.default("active", type="translator")
		except IndexError:
			pass
		else:
			translator.languageChanged.handle(self._retranslate)
		self._retranslate()

		self.active = True

	def disable(self):
		self.active = False

		del self._modules
		del self._register
		del self._activeWidgets

def init(moduleManager):
	return StartWidgetModule(moduleManager)
