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

#	Copyright 2011-2013, Marten de Vries
#	Copyright 2011-2012, Cas Widdershoven
#	Copyright 2012, Milan Boers
#
#	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 datetime
import weakref
import contextlib
import collections

def installQtClasses():
	global TeachLessonWidget, TeachSettingsWidget, TeachWidget

	class TeachSettingsWidget(QtWidgets.QWidget):
		def __init__(self, settings, widgets, *args, **kwargs):
			super().__init__(*args, **kwargs)

			self._settings = settings

			self.startLessonButton = QtWidgets.QPushButton()
			self.formLayout = QtWidgets.QFormLayout()

			self._settingsKeys = ["lessonType", "listModifiers", "itemModifiers"]#, "dontShowAgain"]
			for key in self._settingsKeys:
				setting = settings[key]
				try:
					widget = widgets[setting["type"]](setting)
				except KeyError:
					#shouldn't happen, just in case...
					widget = QtWidgets.QLabel()
				self.formLayout.addRow("placeholder", widget)
			self.formLayout.addRow(self.startLessonButton)

			self.gb = QtWidgets.QGroupBox()
			self.gb.setLayout(self.formLayout)

			mainLayout = QtWidgets.QVBoxLayout()
			mainLayout.addWidget(self.gb)
			self.setLayout(mainLayout)

			self.retranslate()

		def retranslate(self):
			self.startLessonButton.setText(
				_("I'm ready, start the lesson!")
			)
			self.gb.setTitle(_("Lesson settings"))
			i = 0
			for key in self._settingsKeys:
				setting = self._settings[key]
				w = self.formLayout.itemAt(i, QtWidgets.QFormLayout.LabelRole).widget()
				w.setText(setting["name"])

				i += 1

	class TeachLessonWidget(QtWidgets.QSplitter):
		def __init__(self, keyboardWidget, *args, **kwargs):
			super().__init__(*args, **kwargs)

			self.changeSettingsButton = QtWidgets.QPushButton()
			self.wordLabel = QtWidgets.QLabel()
			self.questionLabel = QtWidgets.QLabel()
			font = QtGui.QFont()
			font.setPointSize(24)
			self.questionLabel.setFont(font)
			self.questionLabel.setWordWrap(True)
			self.commentLabel = QtWidgets.QLabel()
			self.commentLabel.setWordWrap(True)
			if keyboardWidget is not None:
				self.keyboardWidget = keyboardWidget
			self.commentAfterAnsweringLabel = QtWidgets.QLabel()
			self.commentAfterAnsweringLabel.setWordWrap(True)
			self.nextButton = QtWidgets.QPushButton()
			self.teachTabWidget = QtWidgets.QTabWidget()
			self.progressBar = QtWidgets.QProgressBar()

			self.sideWidget = QtWidgets.QSplitter(QtCore.Qt.Vertical)

			leftLayout = QtWidgets.QVBoxLayout()
			leftLayout.addWidget(self.wordLabel)
			leftLayout.addWidget(self.questionLabel)
			leftLayout.addWidget(self.commentLabel)
			leftLayout.addStretch()
			leftLayout.addWidget(self.teachTabWidget)
			leftLayout.addWidget(self.commentAfterAnsweringLabel)
			leftLayout.addWidget(self.nextButton)
			leftLayout.addWidget(self.progressBar)

			leftWidget = QtWidgets.QWidget()
			leftWidget.setLayout(leftLayout)

			rightLayout = QtWidgets.QVBoxLayout()
			with contextlib.suppress(AttributeError):
				rightLayout.addWidget(self.keyboardWidget)
			rightLayout.addWidget(self.changeSettingsButton)

			rightWidget = QtWidgets.QWidget()
			rightWidget.setLayout(rightLayout)

			self.addWidget(self.sideWidget)
			self.addWidget(leftWidget)
			self.addWidget(rightWidget)

			#set stretch factor for widget indexes
			self.setStretchFactor(0, 1)
			self.setStretchFactor(1, 255)
			self.setStretchFactor(2, 1)

		def retranslate(self):
			self.changeSettingsButton.setText(_("Change lesson settings"))
			self.wordLabel.setText(_("Word:"))
			self.nextButton.setText(_("Next"))

		def addSideWidget(self, widget):
			self.sideWidget.insertWidget(0, widget)

		def removeSideWidget(self, widget):
			widget.setParent(None)

	class TeachWidget(QtWidgets.QStackedWidget):
		tabChanged = QtCore.pyqtSignal([object])
		lessonDone = QtCore.pyqtSignal()
		listChanged = QtCore.pyqtSignal([object])

		def __init__(self, compose, showResults, lessonTypeCreators, listModifiers, itemModifiers, settings, widgets, keyboardWidget, applicationActivityChanged, *args, **kwargs):
			super().__init__(*args, **kwargs)

			self._compose = compose
			self._showResults = showResults
			self._lessonTypeCreators = lessonTypeCreators
			self._listModifiers = listModifiers
			self._itemModifiers = itemModifiers

			self._settings = settings
			self._applicationActivityChanged = applicationActivityChanged

			self._buildUi(keyboardWidget, widgets)
			self._connectSignals()

			self.inLesson = False

		def addSideWidget(self, widget):
			self._lessonWidget.addSideWidget(widget)

		def removeSideWidget(self, widget):
			self._lessonWidget.removeSideWidget(widget)

		def setTeachTypeWidgets(self, teachTypeWidgets):
			"""Should only be called one. Hope it can be refactored out
			   sometime. Should be called before startLesson() too.

			"""
			self._teachTypeWidgets = teachTypeWidgets
			for wid, name in self._teachTypeWidgets:
				self._lessonWidget.teachTabWidget.addTab(wid, name)

		def updateLesson(self, lesson):
			self.lesson = lesson

		def _showSettings(self):
			self.setCurrentWidget(self._settingsWidget)

		def _startLesson(self):
			self.setCurrentWidget(self._lessonWidget)
			self._applicationActivityChanged.handle(self._activityChanged)

			path = self._settings["lessonType"]["value"]
			try:
				createLessonType = self._lessonTypeCreators[path]
			except KeyError:
				createLessonType = next(iter(self._lessonTypeCreators.values()))

			indexes = list(range(len(self.lesson.list["items"])))
			for path in self._settings["listModifiers"]["value"]:
				try:
					modifyList = self._listModifiers[path]
				except KeyError:
					continue
				indexes = modifyList(indexes, self.lesson.list)

			selectedItemModifiers = []
			for path in self._settings["itemModifiers"]["value"]:
				with contextlib.suppress(KeyError):
					selectedItemModifiers.append(self._itemModifiers[path])

			def modifyItem(item):
				#function that applies all item modifiers on an item and
				#returns the result at the end of the chain. Before that is
				#done, it makes a copy of the item since the item modifiers
				#may modify the item in place.
				result = item.copy()
				for modify in selectedItemModifiers:
					result = modify(result)
				return result

			self._lessonType = createLessonType(self.lesson.list, indexes, modifyItem)

			self._lessonType.newItem.handle(self._newItem)
			self._lessonType.lessonDone.handle(self.stopLesson)

			for widget, name in self._teachTypeWidgets:
				widget.updateLessonType(self._lessonType)

			self._lessonType.start()

			self.inLesson = True

		def _showCommentAfterAnswering(self, show):
			self._lessonWidget.commentAfterAnsweringLabel.setVisible(show)
			self._lessonWidget.nextButton.setVisible(show)
			self._lessonWidget.teachTabWidget.setVisible(not show)

		def _activityChanged(self, activity):
			if activity == "inactive":
				self._pauseStart = datetime.datetime.now()
			elif activity == "active":
				self._lessonType.addPause({
					"start": self._pauseStart,
					"end": datetime.datetime.now()
				})

		def _tellListAndLessonChange(self):
			"""Updates the GUI after some modifications have been made."""

			self.listChanged.emit(self.lesson.list)
			self.lesson.changed = True

		def _nextClicked(self):
			self._nextCallback()

		def _showAfterAnsweringCommentIfNecessary(self, nextCallback):
			"""Returns True if showing an answering comment, otherwise
			   False.

			"""
			try:
				commentAfterAnswering = self._currentItem["commentAfterAnswering"]
			except (AttributeError, KeyError):
				pass
			else:
				#next time don't show the after answering comment again.
				del self._currentItem

				self._nextCallback = nextCallback
				self._showCommentAfterAnswering(True)
				self._lessonWidget.commentAfterAnsweringLabel.setText(commentAfterAnswering)
				return True

			self._showCommentAfterAnswering(False)
			self._lessonWidget.commentAfterAnsweringLabel.clear()
			return False

		def _newItem(self, item):
			if self._showAfterAnsweringCommentIfNecessary(lambda: self._newItem(item)):
				return

			self._currentItem = item

			#update other parts of the GUI to changes made since the
			#last time _newItem was called.
			self._tellListAndLessonChange()

			self._lessonWidget.questionLabel.setText(self._compose(item["questions"]))
			try:
				self._lessonWidget.commentLabel.setText(item["comment"])
			except KeyError:
				self._lessonWidget.commentLabel.clear()
			self._updateProgress()

		def _updateProgress(self):
			self._lessonWidget.progressBar.setMaximum(self._lessonType.totalItems)
			self._lessonWidget.progressBar.setValue(self._lessonType.askedItems)

		def stopLesson(self, showResults=True):
			if self._showAfterAnsweringCommentIfNecessary(lambda: self.stopLesson(showResults)):
				return

			#first update the GUI since modifications have been made.
			self._tellListAndLessonChange()

			self._applicationActivityChanged.unhandle(self._activityChanged)
			self._updateProgress()

			self._showSettings()

			if showResults and self._showResults is not None:
				self._showResults(self.lesson.list, "words", self.lesson.list["tests"][-1])

			self.inLesson = False

			self.lessonDone.emit()

		def _buildUi(self, keyboardWidget, widgets):
			self._settingsWidget = TeachSettingsWidget(self._settings, widgets)
			self._lessonWidget = TeachLessonWidget(keyboardWidget)

			self.addWidget(self._settingsWidget)
			self.addWidget(self._lessonWidget)

		def retranslate(self):
			self._settingsWidget.retranslate()
			self._lessonWidget.retranslate()

		def _connectSignals(self):
			#tab changed
			self._lessonWidget.teachTabWidget.currentChanged.connect(lambda i: self.tabChanged.emit(self._lessonWidget.teachTabWidget.currentWidget()))

			#start lesson button
			self._settingsWidget.startLessonButton.clicked.connect(self._startLesson)

			#change lesson settings button
			self._lessonWidget.changeSettingsButton.clicked.connect(self._showSettings)

			#next button
			self._lessonWidget.nextButton.clicked.connect(self._nextClicked)

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

		self.type = "wordsTeacher"
		self.priorities = {
			"default": 506,
		}

		self.uses = (
			self._mm.mods(type="translator"),
			self._mm.mods(type="itemModifier"),
			self._mm.mods(type="listModifier"),
			self._mm.mods(type="resultsDialog"),
			self._mm.mods(type="settings"),
		)
		self.requires = (
			self._mm.mods(type="ui"),
			self._mm.mods(type="wordsStringComposer"),
			self._mm.mods(type="lessonType"),
			self._mm.mods(type="teachType"),
			self._mm.mods(type="settingsWidgets"),
		)
		self.filesWithTranslations = ("words.py",)

	_compose = property(lambda self: self._modules.default("active", type="wordsStringComposer").compose)

	@property
	def _showResults(self):
		with contextlib.suppress(IndexError):
			return self._modules.default("active", type="resultsDialog").showResults
		#explicit is better than implicit.
		return None

	def createWordsTeacher(self):
		lessonTypeCreators = collections.OrderedDict(
			(mod.__class__.__file__, mod.createLessonType)
			for mod in self._modules.sort("active", type="lessonType")
		)
		listModifiers = dict(
			(mod.__class__.__file__, mod.modifyList)
			for mod in self._modules.sort("active", type="listModifier")
		)
		itemModifiers = dict(
			(mod.__class__.__file__, mod.modifyItem)
			for mod in self._modules.sort("active", type="itemModifier")
		)

		keyboardWidget = self._charsKeyboard
		try:
			letterChosen = keyboardWidget.letterChosen
		except AttributeError:
			#replacement that does nothing
			letterChosen = self._modules.default(type="event").createEvent()

		tw = TeachWidget(
			self._compose,
			self._showResults,
			lessonTypeCreators,
			listModifiers,
			itemModifiers,
			self._settings,
			self._widgets,
			keyboardWidget,
			self._applicationActivityChanged
		)
		self._activeWidgets.add(weakref.ref(tw))
		self._retranslate()

		teachTypeWidgets = [
			(
				module.createWidget(tw.tabChanged, letterChosen, tw.addSideWidget, tw.removeSideWidget),
				module.name
			)
			for module in self._modules.sort("active", type="teachType")
			if module.dataType in ("all", "words")
		]
		tw.setTeachTypeWidgets(teachTypeWidgets)

		return tw

	@property
	def _widgets(self):
		return self._modules.default("active", type="settingsWidgets").widgets

	@property
	def _charsKeyboard(self):
		try:
			return self._modules.default(
				"active",
				type="charsKeyboard"
			).createWidget()
		except IndexError:
			return

	@property
	def _applicationActivityChanged(self):
		uiModule = set(self._mm.mods("active", type="ui")).pop()
		return uiModule.applicationActivityChanged

	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._activeWidgets = set()

		try:
			registerSetting = self._modules.default(type="settings").registerSetting
		except IndexError:
			def registerSetting(name, **kwargs):
				kwargs["value"] = kwargs.pop("defaultValue")
				return kwargs

		modToOption = lambda mod: (mod.name, mod.__class__.__file__)
		lessonTypeOptions = list(map(modToOption, self._modules.sort("active", type="lessonType")))
		listModifierOptions = list(map(modToOption, self._modules.sort("active", type="listModifier")))
		itemModifierOptions = list(map(modToOption, self._modules.sort("active", type="itemModifier")))
		self._settings = {
			"lessonType": registerSetting(**{
				"internal_name": "org.openteacher.teachers.words.lessonType",
				"type": "option",
				"options": lessonTypeOptions,
				"defaultValue": lessonTypeOptions[0][1] if len(lessonTypeOptions) > 0 else None,
			}),
			"listModifiers": registerSetting(**{
				"internal_name": "org.openteacher.teachers.words.listModifiers",
				"type": "multiOption",
				"options": listModifierOptions,
				"defaultValue": listModifierOptions[0][1] if len(listModifierOptions) > 0 else None,
			}),
			"itemModifiers": registerSetting(**{
				"internal_name": "org.openteacher.teachers.words.itemModifiers",
				"type": "multiOption",
				"options": itemModifierOptions,
				"defaultValue": itemModifierOptions[0][1] if len(itemModifierOptions) > 0 else None,
			}),
#FIXME: make this actually work... :S
#			"dontShowAgain": registerSetting(**{
#				"internal_name": "org.openteacher.teachers.words.dontShowAgain",
#				"type": "boolean",
#				"defaultValue": False,
#			}),
		}

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

		self.active = True

	def _retranslate(self):
		#Translations
		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")
			)

		self._settings["lessonType"]["name"] = _("Lesson type")
		self._settings["listModifiers"]["name"] = _("Word list order and filters")
		self._settings["itemModifiers"]["name"] = _("Word modifications")
#		self._settings["dontShowAgain"]["name"] = _("Don't show this screen again when I start a lesson.")

		for setting in self._settings.values():
			setting.update({
				"category": _("Words lesson"),
				"subcategory": _("Lesson settings")
			})

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

	def disable(self):
		self.active = False

		del self._modules
		del self._activeWidgets
		del self._settings

def init(moduleManager):
	return WordsTeacherModule(moduleManager)
