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

#	Copyright 2009-2013, 2017, Marten de Vries
#	Copyright 2008-2011, 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 weakref
import contextlib

def installQtClasses():
	global DetailsWidget, NotesWidget, TestViewerWidget, TestsModel, TestsViewer, TestsViewerWidget

	class TestsModel(QtCore.QAbstractTableModel):
		DATE, NOTE, COMPLETED = range(3)

		def __init__(self, calculateNote, *args, **kwargs):
			super().__init__(*args, **kwargs)

			self._calculateNote = calculateNote
			self._list = {
				"tests": [],
				"items": [],
			}
			self._headers = ["", "", ""]

		def headerData(self, section, orientation, role):
			if role != QtCore.Qt.DisplayRole:
				return
			if orientation == QtCore.Qt.Horizontal:
				return self._headers[section]
			else:
				return section + 1

		def retranslate(self):
			self._headers = [
				_("Date"),
				_("Note"),
				_("Completed"),
			]

		def data(self, index, role):
			if not index.isValid():
				return
			test = self._list["tests"][index.row()]
			if role == QtCore.Qt.DisplayRole:
				if index.column() == self.DATE:
					try:
						return test["results"][0]["active"]["start"].date().isoformat()
					except (IndexError, KeyError):
						return u""
				elif index.column() == self.NOTE:
					return self._calculateNote(test)
			elif role == QtCore.Qt.CheckStateRole and index.column() == self.COMPLETED:
				#guess it's there.
				try:
					return test["finished"]
				except KeyError:
					return

		def testFor(self, index):
			if index.isValid():
				return self._list["tests"][index.row()]

		def rowCount(self, parent):
			try:
				return len(self._list["tests"])
			except KeyError:
				return 0

		def columnCount(self, parent):
			return 3

		def _getList(self):
			return self._list

		def _setList(self, list):
			self.beginResetModel()
			self._list = list
			self.endResetModel()

		list = property(_getList, _setList)

	class NotesWidget(QtWidgets.QWidget):
		def __init__(self, noteCalculator, calculatePercents, *args, **kwargs):
			super().__init__(*args, **kwargs)

			self._noteCalculator = noteCalculator
			self._calculatePercents = calculatePercents

			self.highestLabel = QtWidgets.QLabel()
			self.averageLabel = QtWidgets.QLabel()
			self.lowestLabel = QtWidgets.QLabel()

			self.layout = QtWidgets.QFormLayout()
			self.layout.addRow(QtWidgets.QLabel(), self.highestLabel)
			self.layout.addRow(QtWidgets.QLabel(), self.averageLabel)
			self.layout.addRow(QtWidgets.QLabel(), self.lowestLabel)

			self.setLayout(self.layout)

		def retranslate(self):
			self.layout.labelForField(self.highestLabel).setText(
				_("Highest note:")
			)
			self.layout.labelForField(self.averageLabel).setText(
				_("Average note:")
			)
			self.layout.labelForField(self.lowestLabel).setText(
				_("Lowest note:")
			)

		def updateList(self, list):
			try:
				percents = [self._calculatePercents(t) for t in list["tests"]]
			except KeyError:
				percents = []
			try:
				maxTest = list["tests"][percents.index(max(percents))]
				self.highestLabel.setText(self._noteCalculator.calculateNote(maxTest))
			except (KeyError, ValueError):
				#TRANSLATORS: This is meant as 'here would normally
				#stand a note, but not today.' If '-' isn't
				#appropriate in you language for that, please replace.
				#Otherwise, just copy the original.
				self.highestLabel.setText(_("-"))
			try:
				minTest = list["tests"][percents.index(min(percents))]
				self.lowestLabel.setText(self._noteCalculator.calculateNote(minTest))
			except (KeyError, ValueError):
				self.lowestLabel.setText(_("-"))
			try:
				average = self._noteCalculator.calculateAverageNote(list["tests"])
			except (ZeroDivisionError, KeyError):
				average = _("-")
			self.averageLabel.setText(average)

	class DetailsWidget(QtWidgets.QWidget):
		def __init__(self, testTypes, *args, **kwargs):
			super().__init__(*args, **kwargs)

			self._testTypes = testTypes

			self.labels = []
			self.layout = QtWidgets.QFormLayout()

			self.setLayout(self.layout)

		def updateList(self, list, dataType):
			for module in self._testTypes:
				if module.dataType == dataType and hasattr(module, "properties"):
					# Only if there are any properties in this module
					# If the labels were not made yet, make them
					if len(self.labels) == 0:
						for property in module.properties:
							label = QtWidgets.QLabel(property[0])
							label.setWordWrap(True)
							self.layout.addRow(property[0], label)
							label.setText(list.get(property[1], _("-")))
							self.labels.append(label)
					# Else, update them
					else:
						i = 0
						for property in module.properties:
							self.labels[i].setText(list.get(property[1], _("-")))
							i += 1
					break

	class TestsViewerWidget(QtWidgets.QSplitter):
		testActivated = QtCore.pyqtSignal([object, object, object])

		def __init__(self, testTypes, noteCalculator, calculatePercents, createPercentNoteViewer=None, *args, **kwargs):
			super().__init__(QtCore.Qt.Vertical, *args, **kwargs)

			self._createPercentNotesViewer = createPercentNoteViewer

			self.testsModel = TestsModel(noteCalculator.calculateNote)
			testsView = QtWidgets.QTableView()
			testsView.setModel(self.testsModel)
			testsView.doubleClicked.connect(self.showTest)
			self.notesWidget = NotesWidget(noteCalculator, calculatePercents)
			self.detailsWidget = DetailsWidget(testTypes)

			horSplitter = QtWidgets.QSplitter()
			horSplitter.addWidget(testsView)
			horSplitter.addWidget(self.notesWidget)

			self.addWidget(self.detailsWidget)
			self.addWidget(horSplitter)

		def showTest(self, index):
			list = self.testsModel.list
			dataType = self.testsModel.dataType
			test = self.testsModel.testFor(index)

			self.testActivated.emit(list, dataType, test)

		def updateList(self, list, dataType):
			self.testsModel.list = list
			self.testsModel.dataType = dataType
			self.notesWidget.updateList(list)
			self.detailsWidget.updateList(list, dataType)
			with contextlib.suppress(AttributeError):
				self.percentNotesViewer.hide()
			try:
				self.percentNotesViewer = self._createPercentNotesViewer(list["tests"])
			except (TypeError, KeyError):
				pass
			else:
				self.addWidget(self.percentNotesViewer)

		def retranslate(self):
			self.notesWidget.retranslate()
			self.testsModel.retranslate()

	class TestViewerWidget(QtWidgets.QWidget):
		backActivated = QtCore.pyqtSignal()

		def __init__(self, createTestViewer, list, dataType, test, *args, **kwargs):
			super().__init__(*args, **kwargs)

			self.backButton = QtWidgets.QPushButton("")
			self.backButton.clicked.connect(self.backActivated.emit)

			testViewer = createTestViewer(list, dataType, test)

			layout = QtWidgets.QVBoxLayout()
			layout.addWidget(testViewer)
			layout.addWidget(self.backButton)
			self.setLayout(layout)

		def retranslate(self):
			self.backButton.setText(_("Back"))

	class TestsViewer(QtWidgets.QStackedWidget):
		def __init__(self, testTypes, noteCalculator, calculatePercents, createTestViewer, createPercentNotesViewer=None,  *args, **kwargs):
			super().__init__(*args, **kwargs)

			self._createTestViewer = createTestViewer

			self.testsViewerWidget = TestsViewerWidget(testTypes, noteCalculator, calculatePercents, createPercentNotesViewer)
			self.testsViewerWidget.testActivated.connect(self.showList)
			self.addWidget(self.testsViewerWidget)

		def showList(self, list, dataType, test):
			testViewer = TestViewerWidget(self._createTestViewer, list, dataType, test)
			testViewer.backActivated.connect(self.showTests)
			self.addWidget(testViewer)
			self.setCurrentWidget(testViewer)
			self.retranslate()

		def showTests(self):
			self.setCurrentWidget(self.testsViewerWidget)

		def updateList(self, list, dataType):
			self.testsViewerWidget.updateList(list, dataType)

		def retranslate(self):
			for i in range(self.count()):
				self.widget(i).retranslate()

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

		self.type = "testsViewer"
		self.requires = (
			self._mm.mods(type="ui"),
			self._mm.mods(type="noteCalculatorChooser"),
			self._mm.mods(type="percentsCalculator"),
			self._mm.mods(type="testViewer"),
		)
		self.uses = (
			self._mm.mods(type="percentNoteViewer"),
			self._mm.mods(type="translator"),
		)
		self.filesWithTranslations = ("testsViewer.py",)

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

		self._modules = set(self._mm.mods(type="modules")).pop()

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

	def _retranslate(self):
		global _, ngettext
		try:
			translator = self._modules.default("active", type="translator")
		except IndexError:
			_, ngettext = str, lambda x, y, n: x if n == 1 else y
		else:
			_, ngettext = translator.gettextFunctions(
				self._mm.resourcePath("translations")
			)
		for tv in self._testsViewers:
			r = tv() #check weak reference
			if r is not None:
				r.retranslate()

	def createTestsViewer(self):
		testTypes = self._mm.mods("active", type="testType")
		noteCalculator = self._modules.default(
			"active",
			type="noteCalculatorChooser"
		).noteCalculator
		calculatePercents = self._modules.default(
			"active",
			type="percentsCalculator"
		).calculatePercents
		createTestViewer = self._modules.default(
			"active",
			type="testViewer"
		).createTestViewer
		try:
			createPercentNotesViewer = self._modules.default(
				"active",
				type="percentNotesViewer"
			).createPercentNotesViewer
		except IndexError:
			createPercentNotesViewer = None

		tv = TestsViewer(testTypes, noteCalculator, calculatePercents, createTestViewer, createPercentNotesViewer)
		#Weak reference so gc can still get into action
		self._testsViewers.add(weakref.ref(tv))
		self._retranslate()
		return tv

def init(moduleManager):
	return TestsViewerModule(moduleManager)
