# encoding=utf-8
"""
File containing the CotationPseudoMotsWindow for the ModulePseudoMots cotation
"""

__author__ = "Roland Trouville"
__copyright__ = "Copyright 2015+, Consortium MonPaGe"
__license__ = "Creative Commons 4.0 By-Nc-Sa"
__maintainer__ = "Roland Trouville"
__email__ = "contact.monpage@gmail.com"
__status__ = "Production"

import logging
import os
from math import pow
from sqlite3 import Row
from typing import List, Optional, Tuple, Union

try:
	from typing import override
except ImportError:
	from typing_extensions import override  # noqa: F401

from PySide6 import QtCore
from PySide6.QtCore import QSize, Qt
from PySide6.QtWidgets import (
	QAbstractItemView,
	QApplication,
	QCheckBox,
	QGroupBox,
	QLabel,
	QPlainTextEdit,
	QProgressBar,
	QPushButton,
	QRadioButton,
	QTabWidget,
	QTreeWidget,
	QTreeWidgetItem,
)

from cotation.cotation_window import CotationWindow
from tools.audio_manager import AudioManager
from tools.display_tools import DisplayTools
from tools.general_tools import GeneralTools

logger = logging.getLogger(__name__)


class CotationPseudoMotsWindow(CotationWindow):
	"""
	Cotation window for a given module
	"""

	current_file_index = 0

	form: QGroupBox
	form_global: QGroupBox

	judge_code: str
	session_path: str
	advanced_mode: bool

	file_list: list[str]
	file_state_list: dict[str, bool]

	file_navigation_list: QTreeWidget
	file_navigation_list_group: QGroupBox

	audio_pbar: QProgressBar
	audio_pbar_label: QLabel

	values: Optional[List[Tuple[str, ...]]]

	mot_label: QLabel

	column_headers: List[QLabel] = []
	column_group: List[QGroupBox] = []
	sound_problem_buttons: List[QRadioButton] = []
	correct_buttons: List[QRadioButton] = []
	incorrect_buttons: List[QRadioButton] = []
	inint_buttons: List[QRadioButton] = []
	effort_buttons: List[QCheckBox] = []
	inversion_buttons: List[QCheckBox] = []
	ajout_buttons: List[QCheckBox] = []
	error_type: List[List[QRadioButton]] = []
	error_nature = []
	error_verbatim: List[QPlainTextEdit] = []

	advanced_tabs: QTabWidget
	advanced_tabs_save: QPushButton

	# Do we display navigation as a hideable panel or on the side because we have width > 800 ?
	can_display_navigation_on_side: bool = False

	def __init__(
		self,
		parent,
		participant_code,
		module_name,
		judge_code,
		session_path,
		advanced_mode=False,
	):
		super().__init__(participant_code, parent)
		self.module_name = module_name
		self.judge_code = judge_code
		self.session_path = session_path
		self.directory_path = (
			"./data/participant/" + participant_code + "/" + session_path
		)
		self.advanced_mode = advanced_mode
		self.column_headers = []
		self.column_group = []
		self.correct_buttons = []
		self.incorrect_buttons = []
		self.sound_problem_buttons = []
		self.inint_buttons = []
		self.effort_buttons = []
		self.inversion_buttons = []
		self.ajout_buttons = []

		self.error_type = []
		self.error_nature = []
		self.error_verbatim = []
		AudioManager.init()
		self.init_cotation()
		self.init_participant_result_db()
		self.init_ui()
		self.display_current_item_cotation()

	def init_ui(self):
		"""
		Initialize the UI
		:return: None
		"""

		super().init_ui()
		try:
			h = min(DisplayTools.window_max_height, 600)
			w = min(DisplayTools.window_max_width, 1000)
			self.setFixedSize(QSize(w, h))

			self.can_display_navigation_on_side = (
				DisplayTools.get_window_size().width() >= 1000
			)

			self.form_global = QGroupBox("", self)
			self.form_global.resize(780, 310)
			self.form_global.move(10, 10)
			qbtn = QPushButton("Ecouter le fichier", self.form_global)
			qbtn.clicked.connect(self.listen_to_file)
			qbtn.resize(150, 20)
			qbtn.move(10, 5)
			DisplayTools.set_font_size(qbtn, "btn_ctn_ecouter")

			self.audio_pbar = QProgressBar(self.form_global)
			self.audio_pbar.setMinimum(0)
			self.audio_pbar.setMaximum(20)
			self.audio_pbar.setValue(0)
			self.audio_pbar.resize(400, 20)
			self.audio_pbar.setTextVisible(False)
			self.audio_pbar.raise_()

			self.audio_pbar_label = QLabel("", self.form_global)
			self.audio_pbar_label.resize(60, 20)
			DisplayTools.set_font_size(self.audio_pbar_label, "lab_ctn_progress_bar")

			self.audio_pbar.move(170, 5)
			self.audio_pbar_label.move(680, 5)

			self.form = QGroupBox("", self.form_global)
			self.form.resize(780, 290)
			self.form.move(0, 25)

			tmp = QPushButton("Problème son", self.form)
			tmp.move(10, 50)
			tmp.resize(70, 20)
			DisplayTools.set_font_size(tmp, "lab_ctn_pbsnd_btn")
			tmp.clicked.connect(self.sound_problem)

			self.mot_label = QLabel("pseudo mot", self.form)
			self.mot_label.resize(400, 20)
			self.mot_label.move(90, 50)
			DisplayTools.set_font_size(self.mot_label, "lab_ctn_mot")

			tmp = QLabel("problème son", self.form)
			tmp.resize(110, 20)
			tmp.move(5, 110)
			tmp.setAlignment(Qt.AlignmentFlag.AlignRight)
			DisplayTools.set_font_size(tmp, "lab_cot_probsnd")

			tmp = QLabel("correct", self.form)
			tmp.resize(110, 20)
			tmp.move(5, 135)
			tmp.setAlignment(Qt.AlignmentFlag.AlignRight)
			DisplayTools.set_font_size(tmp, "lab_cot_corr")

			tmp = QLabel("incorrect", self.form)
			tmp.resize(110, 20)
			tmp.move(5, 160)
			tmp.setAlignment(Qt.AlignmentFlag.AlignRight)
			DisplayTools.set_font_size(tmp, "lab_cot_incorr")

			tmp = QLabel("produit avec effort", self.form)
			tmp.resize(110, 20)
			tmp.move(5, 185)
			tmp.setAlignment(Qt.AlignmentFlag.AlignRight)
			DisplayTools.set_font_size(tmp, "lab_cot_eff")

			tmp = QLabel("avec inversion", self.form)
			tmp.resize(110, 20)
			tmp.move(5, 210)
			tmp.setAlignment(Qt.AlignmentFlag.AlignRight)
			DisplayTools.set_font_size(tmp, "lab_cot_invers")

			tmp = QLabel("avec insertion", self.form)
			tmp.resize(110, 20)
			tmp.move(5, 235)
			tmp.setAlignment(Qt.AlignmentFlag.AlignRight)
			DisplayTools.set_font_size(tmp, "lab_cot_ajout")

			for i in range(0, 10):
				tmpgr = QGroupBox("", self.form)
				tmpgr.resize(50, 180)
				tmpgr.move(120 + (55 * i), 80)
				self.column_group.append(tmpgr)

				tmp = QLabel(str(i + 1), tmpgr)
				tmp.resize(45, 20)
				tmp.move(5, 5)
				tmp.setAlignment(Qt.AlignmentFlag.AlignCenter)
				self.column_headers.append(tmp)

				tmp = QRadioButton("", tmpgr)
				tmp.resize(20, 20)
				tmp.move(15, 30)
				tmp.setChecked(False)
				self.sound_problem_buttons.append(tmp)

				tmp = QRadioButton("", tmpgr)
				tmp.resize(20, 20)
				tmp.move(15, 55)
				tmp.setChecked(True)
				self.correct_buttons.append(tmp)

				tmp = QRadioButton("", tmpgr)
				tmp.resize(20, 20)
				tmp.move(15, 80)
				tmp.setChecked(False)
				self.incorrect_buttons.append(tmp)

				tmp = QCheckBox("", tmpgr)
				tmp.resize(20, 20)
				tmp.move(15, 105)
				tmp.setChecked(False)
				self.effort_buttons.append(tmp)

				tmp = QCheckBox("", tmpgr)
				tmp.resize(20, 20)
				tmp.move(15, 130)
				tmp.setChecked(False)
				self.inversion_buttons.append(tmp)

				tmp = QCheckBox("", tmpgr)
				tmp.resize(20, 20)
				tmp.move(5, 155)
				tmp.setChecked(False)
				self.ajout_buttons.append(tmp)

				tmpgr.hide()

			tmp = QPushButton("Enregistrer et continuer", self.form)
			tmp.resize(150, 20)
			tmp.move(5, 265)
			DisplayTools.set_font_size(tmp, "lab_ctn_save_and_next")
			tmp.clicked.connect(self.save_and_next)

			self.advanced_tabs = QTabWidget(self)
			self.advanced_tabs.resize(780, 260)
			self.advanced_tabs.move(10, 290)

			self.advanced_tabs_save = QPushButton("Enregistrer et continuer", self)
			self.advanced_tabs_save.resize(150, 20)
			self.advanced_tabs_save.move(630, 530)
			DisplayTools.set_font_size(
				self.advanced_tabs_save, "lab_ctn_save_advanced_and_next"
			)
			self.advanced_tabs_save.hide()
			self.advanced_tabs_save.clicked.connect(self.save_advanced_and_next)

			# File navigation list
			x_offset = 0
			if self.can_display_navigation_on_side:
				x_offset = 210
			else:
				tmp = QPushButton("Navigation ↓", self.form_global)
				tmp.move(585, 5)
				tmp.resize(180, 20)
				DisplayTools.set_font_size(tmp, "lab_ctn_navigation")
				tmp.clicked.connect(self.file_navigation_toggle)

			self.file_navigation_list_group = QGroupBox(self)
			self.file_navigation_list_group.move(x_offset + 580, 40)
			self.file_navigation_list_group.resize(200, 550)

			self.file_navigation_list = QTreeWidget(self.file_navigation_list_group)
			self.file_navigation_list.setSelectionMode(
				QAbstractItemView.SelectionMode.SingleSelection
			)
			self.file_navigation_list.setColumnCount(2)
			self.file_navigation_list.setColumnWidth(0, 40)
			self.file_navigation_list.setHeaderLabels(["", "Fichier"])
			self.file_navigation_list.move(5, 5)
			self.file_navigation_list.resize(180, 500)
			self.file_navigation_list.itemDoubleClicked.connect(
				self.file_navigation_list_dblclick
			)
			tmp = QPushButton("Coter le fichier", self.file_navigation_list_group)
			tmp.move(5, 520)
			tmp.resize(180, 20)
			DisplayTools.set_font_size(tmp, "lab_ctn_goto_file")
			tmp.clicked.connect(self.file_navigation_list_goto)
			self.init_file_navigation_list()

		except Exception as e:
			logger.error(f"Initializing UI error: {e}")

	def file_navigation_toggle(self):
		"""
		Toggle, hide or show list file'.wav' to cotation.
		:return: None
		"""
		if self.can_display_navigation_on_side:
			self.file_navigation_list_group.show()
		else:
			if self.file_navigation_list_group.isVisible():
				self.file_navigation_list_group.hide()
			else:
				self.file_navigation_list_group.show()

	def file_navigation_list_dblclick(self, item):
		"""
		Load parameter to the selected file'.wav' without saving previous selected file
		:param item: file you want make cotation
		:return: None
		"""
		self.file_navigation_list_select(item.text(1))

	def file_navigation_list_goto(self):
		"""
		Action called when user selects a file in the file navigation and press the button. Changes to the selected
		file WITHOUT SAVING current changes
		:return:
		"""
		items = self.file_navigation_list.selectedItems()
		if len(items) == 1:
			self.file_navigation_list_select(items[0].text(1))

	def file_navigation_list_select(self, filename):
		"""
		Load parameter to the selected file'.wav' without saving previous selected file
		:param filename: file you want make cotation
		:return: None
		"""
		for i in range(0, len(self.file_list)):
			if self.file_list[i].endswith(filename):
				self.current_file_index = i
				self.display_current_item_cotation()
				return

	def init_file_navigation_list(self):
		"""
		Initialise the navigation list with elements based on the list of files in file_list/ Marks elements for
		which we have results stored with an X in the first column to differentiate them
		:return:
		"""
		self.file_navigation_list.clear()
		for f in self.file_list:
			f = f.split("_")
			name = f[len(f) - 1]
			current_dir_treeitem = QTreeWidgetItem(0)
			current_dir_treeitem.setText(1, name)
			if name in self.file_state_list:
				current_dir_treeitem.setText(0, "X")
			self.file_navigation_list.addTopLevelItem(current_dir_treeitem)

	def sound_problem(self):
		"""
		check all buttons of sound problem in the cotation of selected file'.wav'
		:return None:
		"""
		for chb in self.sound_problem_buttons:
			chb.setChecked(True)

	def init_cotation(self):
		"""
		Initialise cotation. First it links to the cotation main database, then it populates the file_list and
		initialize the file_state_list
		:return:
		"""
		super().init_cotation()
		self.file_list = []
		self.file_state_list = {}
		pseudo_words = self.get_pseudo_words_with_structure()

		if os.path.isdir(self.directory_path):
			for filename in os.listdir(self.directory_path):
				if (
					not filename.endswith("TextGrid")
					and filename.split("_")[-1] in pseudo_words
				):  # We only take files we found for which we have data in
					# the cotation main database. Others are rejected. Made to excluse .TextGrid and such
					self.file_list.append(filename)

	def init_participant_result_db(self):
		"""
		Initialise the participant specific database, and then populates the file_state_list based on data stored there
		:return:
		"""
		super().init_participant_result_db()

		results = self.get_available_results(
			self.session_path, self.participant_code, self.judge_code
		)
		for r in results:
			tmp = r[2] + "-" + r[1]
			self.file_state_list[tmp] = True

	def verif_state_effort(self, i, index):
		"""
		Verify and change state of check_box_buttons
		:return None:
		"""

		assert self.values is not None

		saved = self.get_stored_result(
			self.session_path,
			self.participant_code,
			self.judge_code,
			int(self.values[i][0]),
			int(self.values[i][3]),
			int(self.values[i][4]),
		)

		if saved["state"] == 1:
			self.incorrect_buttons[index].setChecked(True)
		elif saved["state"] == -1:
			self.sound_problem_buttons[index].setChecked(True)
		else:
			self.correct_buttons[index].setChecked(True)
		if saved["effort"] == 1:
			self.effort_buttons[index].setChecked(True)
		else:
			self.effort_buttons[index].setChecked(False)

		self.inversion_buttons[index].setChecked(saved["inversion"] == 1)
		self.ajout_buttons[index].setChecked(saved["ajout"] == 1)

	def display_current_item_cotation(self):
		"""
		Display cotation for the current item
		:return:
		"""
		self.form.setDisabled(False)
		self.advanced_tabs.hide()
		self.advanced_tabs_save.hide()
		for i in range(
			0, self.advanced_tabs.count()
		):  # Removing advanced tabs made for previous item
			try:
				self.advanced_tabs.removeTab(0)
			except IndexError:
				break
		self.values = self.get_pseudo_word_structure(
			self.file_list[self.current_file_index]
		)

		# updating the filelist with selection
		tmp = self.file_list[self.current_file_index].split("_")
		w = self.file_navigation_list.findItems(
			tmp[len(tmp) - 1], QtCore.Qt.MatchFlag.MatchContains, 1
		)
		if len(w) == 1:
			w[0].setSelected(True)

		for i in range(
			0, len(self.column_group)
		):  # hiding all the columns that were updated and made visible for
			# previous item
			self.column_group[i].hide()

		if self.values is not None and len(self.values) > 0:
			self.form.setTitle(self.file_list[self.current_file_index])
			label = (
				"Cotation "
				+ self.values[0][7]
				+ " "
				+ self.values[0][8]
				+ " ("
				+ self.values[0][1]
				+ ") - "
				+ self.values[0][2]
			)
			self.mot_label.setText(label)
			if (
				self.values[0][1] in ("a", "b", "c", "d", "f")
			):  # type b and c, we do everything, ignore is managed by DB (pseudo_word_phonem_special)
				index = 0
				for i in range(0, len(self.values)):
					self.get_stored_result(
						self.session_path,
						self.participant_code,
						self.judge_code,
						int(self.values[i][0]),
						int(self.values[i][3]),
						int(self.values[i][4]),
					)

					self.verif_state_effort(i, index)

					self.column_group[index].show()
					self.column_headers[index].setText("/" + self.values[i][6] + "/")
					index += 1

			elif self.values[0][1] in (
				"e",
				"g",
			):  # e and g, we manage syllable, not phonems
				index = 0

				last = -1
				last_title = ""
				for i in range(0, len(self.values)):
					if (
						int(self.values[i][3]) != last
					):  # checking syll pos to stay in same syll
						last_title = self.values[i][6]
						self.get_stored_result(
							self.session_path,
							self.participant_code,
							self.judge_code,
							int(self.values[i][0]),
							int(self.values[i][3]),
							int(self.values[i][4]),
						)

						self.verif_state_effort(i, index)

						self.column_group[index].show()
						last = self.values[i][3]  # syll pos
						self.column_headers[index].setText("/" + last_title + "/")
						index += 1
					else:
						# same syllable position, we extend last column isntead of enabling a new one
						last_title += self.values[i][6]
						self.column_headers[index - 1].setText("/" + last_title + "/")

			else:
				self.values = None

		else:
			self.form.setTitle(self.file_list[self.current_file_index])
			self.mot_label.setText("Pseudo mot introuvable dans la base de donnée")
			for i in range(0, len(self.column_group)):
				self.column_group[i].hide()

		self.update()
		QApplication.processEvents()

	def listen_to_file(self):
		"""
		Plays the audio file that is the currently selected item (the one we are doing cotation on)
		:return:
		"""
		filename = self.directory_path + self.file_list[self.current_file_index]
		AudioManager.stop()
		AudioManager.play_wave(filename, self.audio_pbar, self.audio_pbar_label)

	def research_sound_problem(self, index, state, effort, inversion, ajout):
		"""
		Research if there are sound problem
		"""
		if self.sound_problem_buttons[index].isChecked():
			state = -1
		if self.effort_buttons[index].isChecked():
			effort = 1
		if self.inversion_buttons[index].isChecked():
			inversion = 1
		if self.ajout_buttons[index].isChecked():
			ajout = 1
		return state, effort, inversion, ajout

	def save_and_next(self):
		"""
		Saves the judge input for the pseudoword. Also determines if the program needs to display advanced grids
		based on wether the advanced mode is enabled and if the errors noted by the judge warrants any advanced cotation
		:return:
		"""
		self.error_type = []
		self.error_nature = []
		self.error_verbatim = []
		grids = []
		if self.values is not None:
			if self.values[0][1] == "a":  # type a has no advanced mode
				index = 0
				for i in range(0, len(self.values)):
					if int(self.values[i][5]) == 0:  # Vowel (consonnant = 0)
						state = 0
						effort = 0
						inversion = 0
						ajout = 0

						if self.incorrect_buttons[index].isChecked():
							state = 1
						state, effort, inversion, ajout = self.research_sound_problem(
							index, state, effort, inversion, ajout
						)
						try:
							self.set_stored_result(
								self.session_path,
								self.participant_code,
								self.judge_code,
								int(self.values[i][0]),
								int(self.values[i][3]),
								int(self.values[i][4]),
								0,
								state,
								effort,
								inversion,
								ajout,
								self.values[i][6],
							)
							tmp = self.values[i][1] + "-" + self.values[i][9]
							self.file_state_list[tmp] = True
						except Exception as e:
							repr(e)
						index += 1

			elif self.values[0][1] in (
				"b",
				"c",
				"d",
				"f",
			):  # b and c have advanced mode for "incorrect" consonnant or
				# incorrect vowels after the first one (for b)
				index = 0
				nb_v = 0
				nb_c = 0
				for i in range(0, len(self.values)):
					pos = int(self.values[i][3]) * 10 + int(self.values[i][4])
					if int(self.values[i][5]) == 0:  # Vowel
						p_type = "v"
						nb_v += 1
					else:
						p_type = "c"
						nb_c += 1
					state = 0
					effort = 0
					inversion = 0
					ajout = 0
					if self.incorrect_buttons[index].isChecked():
						state = 1
						if p_type == "c":
							if (
								int(self.values[i][5]) == 1
							):  # We do not waznt advanced clusters (consonant > 1)
								grids.append(
									pos
								)  # consonant, incorrect -> we want an advanced grid for this phonemic
						# position
						else:
							# NOw done by not displaying vowels
							# if nb_v > 1 or self.values[0][1] in ("d", "f"):  # vowel, incorrect, and not the first vowel
							#  -> we want and advanced grid for this phonem
							grids.append(pos)
					state, effort, inversion, ajout = self.research_sound_problem(
						index, state, effort, inversion, ajout
					)
					try:
						self.set_stored_result(
							self.session_path,
							self.participant_code,
							self.judge_code,
							int(self.values[i][0]),
							int(self.values[i][3]),
							int(self.values[i][4]),
							0,
							state,
							effort,
							inversion,
							ajout,
						)
						tmp = self.values[i][1] + "-" + self.values[i][9]
						self.file_state_list[tmp] = True
					except Exception as e:
						repr(e)
					index += 1

			# elif self.values[0][1] in ("d", "f"):
			#     index = 0
			#     nb_v = 0
			#     nb_c = 0
			#     last = -1
			#     for i in range(0, len(self.values)):
			#         if last != self.values[i][5]:
			#             last = self.values[i][5]
			#             if i < len(self.values) - 1 and self.values[i + 1][5] == self.values[i][5]:
			#                 is_cluster = 0
			#                 for j in range(i, len(self.values)):
			#                     if self.values[i][5] == self.values[j][5]:
			#                         is_cluster += 1
			#                     else:
			#                         break
			#             else:
			#                 is_cluster = 0
			#             is_cluster = 0
			#             pos = int(self.values[i][3]) * 10 + int(self.values[i][4])
			#             if int(self.values[i][5]) == 0:  # Vowel
			#                 p_type = "v"
			#                 nb_v += 1
			#             else:
			#                 p_type = "c"
			#                 nb_c += 1
			#             state = 0
			#             effort = 0
			#             if self.incorrect_buttons[index].isChecked():
			#                 state = 1
			#                 if p_type == "c":  # incorrect and consonant, we want an advanced grid
			#                     grids.append(pos)
			#             if self.inint_buttons[index].isChecked():
			#                 state = 2
			#
			#             if self.effort_buttons[index].isChecked():
			#                 effort = 1
			#             try:
			#                 self.set_stored_result(self.session_path, self.participant_code, self.judge_code,
			#                                        int(self.values[i][0]), int(self.values[i][3]),
			#                                        int(self.values[i][4]), is_cluster, state, effort)
			#                 tmp = self.values[i][1] + "-" + self.values[i][9]
			#                 self.file_state_list[tmp] = True
			#             except Exception as e:
			#                 repr(e)
			#             index += 1

			elif self.values[0][1] in ("e", "g"):  # e has no advanced mode
				index = 0
				last = -1
				for i in range(0, len(self.values)):
					if last != self.values[i][3]:
						last = self.values[i][3]
						is_cluster = 0
						for j in range(i, len(self.values)):
							if self.values[i][5] == self.values[j][5]:
								is_cluster += 1
							else:
								break
						state = 0
						effort = 0
						inversion = 0
						ajout = 0
						pos = int(self.values[i][3]) * 10 + int(self.values[i][4])
						if self.incorrect_buttons[index].isChecked():
							state = 1
						# 16/02/2017
						# if self.values[0][1] == "g":  # we are incorrect and on a type g pseudo word, we want an
						#     # advanced grid
						#     grids.append(pos)
						state, effort, inversion, ajout = self.research_sound_problem(
							index, state, effort, inversion, ajout
						)
						try:
							self.set_stored_result(
								self.session_path,
								self.participant_code,
								self.judge_code,
								int(self.values[i][0]),
								int(self.values[i][3]),
								int(self.values[i][4]),
								is_cluster,
								state,
								effort,
								inversion,
								ajout,
							)
							tmp = self.values[i][1] + "-" + self.values[i][9]
							self.file_state_list[tmp] = True
						except Exception as e:
							repr(e)
						index += 1

			else:
				# print("autre")
				pass

		if (
			len(grids) > 0 and self.advanced_mode
		):  # if we want advanced grid and we are in advanced mode, we display
			# advanced grids
			self.form.setDisabled(True)
			for i in range(0, len(grids)):
				self.make_advanced_grids(grids[i])
			if not self.can_display_navigation_on_side:
				self.file_navigation_list_group.hide()
			self.advanced_tabs.show()
			self.advanced_tabs_save.show()
		else:  # no advanced grid required, we are finished with this pseudo word, we go to the next one
			self.current_file_index += 1
			if self.current_file_index >= len(self.file_list):
				super().end_cotation()
			else:
				self.display_current_item_cotation()

	def make_advanced_grids(self, phonem_pos):
		"""
		Will make an advanced cotation grid for the specified phonemic position. Note, in case we want want an
		advanced grid for a cluster or a syllable, it is always considered to be on the first phonem of the
		cluster/syllable
		:param phonem_pos: the position of phonem we want an advanced grid for
		"""
		if self.values is not None:
			if self.values[0][1] in ("b", "c", "d", "f"):
				for i in range(0, len(self.values)):
					pos = int(self.values[i][3]) * 10 + int(self.values[i][4])
					if pos == phonem_pos:
						if int(self.values[i][5]) == 0:  # Vowel
							tmp = QGroupBox(
								"Erreur Voyelle /" + self.values[i][6] + "/", self
							)
						else:
							tmp = QGroupBox(
								"Erreur Consonne /" + self.values[i][6] + "/", self
							)
						tmp.setObjectName(
							str(self.values[i][3]) + ";" + str(self.values[i][4])
						)

						saved = self.get_stored_result(
							self.session_path,
							self.participant_code,
							self.judge_code,
							int(self.values[i][0]),
							int(self.values[i][3]),
							int(self.values[i][4]),
						)

						tmpradios = self.__make_advanced_grid_type__(tmp)
						self.error_type.append(tmpradios)

						tmplab = QLabel("Commentaire", tmp)
						tmplab.move(500, 5)
						tmplab.resize(100, 20)
						tmptext = QPlainTextEdit(tmp)
						if saved["error_verbatim"] is not None:
							tmptext.setPlainText(saved["error_verbatim"])
						tmptext.move(500, 25)
						tmptext.resize(200, 200)
						self.error_verbatim.append(tmptext)

						if int(self.values[i][5]) == 0:  # Vowel
							tmpradios2 = self.__make_advanced_grid_vowel_nature__(tmp)
							self.error_nature.append(tmpradios2)
							tmp.resize(700, 210)
							self.advanced_tabs.addTab(
								tmp, " Voyelle /" + self.values[i][6] + "/"
							)
						else:
							tmpradios2 = self.__make_advanced_grid_consonant_nature__(
								tmp
							)
							self.error_nature.append(tmpradios2)
							tmp.resize(700, 210)
							self.advanced_tabs.addTab(
								tmp, " Consonne /" + self.values[i][6] + "/"
							)

						for t in tmpradios:
							if (
								int(t.objectName()) == saved["error_type"]
							):  # the object name of the radios are equal
								# to their value. That way we can know which one to check when we display the grid
								t.setChecked(True)
						if (
							saved["error_type"] != 3
						):  # Pas Distorsion -> we disable the radios that allow to
							# pinpoint the kind of distortion
							for t in tmpradios2:
								t.setEnabled(False)
						else:
							bitval_base = "{0:0" + str(len(tmpradios2)) + "b}"
							bitval = bitval_base.format(
								saved["error_nature"]
							)  # we use a binary representation of
							# the stored value to know which checkbox to check
							for k in range(0, len(tmpradios2)):
								tmpradios2[k].setEnabled(True)
								tmpradios2[k].setChecked(bitval[k] == "1")

			# elif self.values[0][1] in ("d", "f"):
			#
			#     for i in range(0, len(self.values)):
			#         pos = int(self.values[i][3]) * 10 + int(self.values[i][4])
			#         if pos == phonem_pos:
			#             saved = self.get_stored_result(self.session_path, self.participant_code, self.judge_code,
			#                                            int(self.values[i][0]), int(self.values[i][3]),
			#                                            int(self.values[i][4]))
			#             # check if cluster
			#             if self.values[i][5] == 1 and i < len(self.values) - 1 and self.values[i + 1][5] == 1:
			#                 cluster_members = []
			#                 cluster_title = ""
			#                 for j in range(i, len(self.values) - 1):
			#                     cluster_members.append(self.values[j][6])
			#                     cluster_title += self.values[j][6]
			#                 # cluster
			#                 tmp = QGroupBox("Erreur cluster /" + cluster_title + "/", self)
			#                 tmp.setObjectName(str(self.values[i][3]) + ";" + str(self.values[i][4]))
			#                 tmpradiosarray = self.__make_advanced_grid_cluster__(tmp, cluster_members)
			#                 self.error_type.append(tmpradiosarray[0])
			#                 self.error_nature.append(tmpradiosarray[1])
			#                 tmp.resize(700, 200)
			#                 self.advanced_tabs.addTab(tmp, " Cluster /" + cluster_title + "/")
			#
			#                 for t in tmpradiosarray[0]:
			#                     if int(t.objectName()) == saved["error_type"]:  # the object name of the radios are
			#                         # equal to their value. That way we can know which one to check when we display
			#                         # the grid
			#                         t.setChecked(True)
			#                 bitval_base = "{0:0" + str(len(cluster_members)) + "b}"
			#                 if saved["error_type"] == 3:
			#                     bitval_base = "{0:0" + str(len(cluster_members) + 1) + "b}"
			#
			#                 bitval = bitval_base.format(saved["error_nature"])  # we use a binary representation of
			#                 # the stored value to know which checkbox to check
			#                 for k in range(0, len(tmpradiosarray[1][saved["error_type"] - 1])):
			#                     tmpradiosarray[1][saved["error_type"] - 1][k].setEnabled(True)
			#                     tmpradiosarray[1][saved["error_type"] - 1][k].setChecked(bitval[k] == "1")
			#
			#             else:
			#                 # not cluster
			#                 tmp = QGroupBox("Erreur consonne /" + self.values[i][6] + "/")
			#                 tmp.setObjectName(str(self.values[i][3]) + ";" + str(self.values[i][4]))
			#                 tmpradios = self.__make_advanced_grid_type__(tmp)
			#                 self.error_type.append(tmpradios)
			#                 tmpradios2 = self.__make_advanced_grid_consonant_nature__(tmp)
			#                 self.error_nature.append(tmpradios2)
			#                 tmp.resize(700, 200)
			#                 self.advanced_tabs.addTab(tmp, " Consonne /" + self.values[i][6] + "/")
			#                 for t in tmpradios:
			#                     if int(t.objectName()) == saved["error_type"]:
			#                         t.setChecked(True)
			#                 if saved["error_type"] != 3:  # Pas Distorsion
			#                     for t in tmpradios2:
			#                         t.setEnabled(False)
			#                 else:
			#                     for t in tmpradios2:
			#                         if int(t.objectName()) == saved["error_nature"]:
			#                             t.setChecked(True)

			elif self.values[0][1] == "g":
				for i in range(0, len(self.values)):
					pos = int(self.values[i][3]) * 10 + int(self.values[i][4])
					if pos == phonem_pos:
						saved = self.get_stored_result(
							self.session_path,
							self.participant_code,
							self.judge_code,
							int(self.values[i][0]),
							int(self.values[i][3]),
							int(self.values[i][4]),
						)
						syllable_label = ""
						syllable_members = []
						for j in range(i, len(self.values)):
							if self.values[i][3] == self.values[j][3]:
								syllable_label += self.values[j][6]
								syllable_members.append(self.values[j][6])

						tmp = QGroupBox(
							"Erreur syllabe /"
							+ syllable_label
							+ "/ "
							+ str(self.values[i][3])
						)
						tmp.setObjectName(
							str(self.values[i][3]) + ";" + str(self.values[i][4])
						)
						tmpradiosarray = self.__make_advanced_grid_syllable__(
							tmp, syllable_members
						)
						self.error_type.append(tmpradiosarray[0])
						self.error_nature.append(tmpradiosarray[1])
						tmplab = QLabel("Commentaire", tmp)
						tmplab.move(550, 5)
						tmplab.resize(100, 20)
						tmptext = QPlainTextEdit(tmp)
						tmptext.move(550, 25)
						tmptext.resize(200, 200)
						if saved["error_verbatim"] is not None:
							tmptext.setPlainText(saved["error_verbatim"])
						self.error_verbatim.append(tmptext)

						tmp.resize(700, 200)

						self.advanced_tabs.addTab(
							tmp,
							" Syllabe /"
							+ syllable_label
							+ "/ "
							+ str(self.values[i][3]),
						)
						for t in tmpradiosarray[0]:
							if int(t.objectName()) == saved["error_type"]:
								t.setChecked(True)
							bitval_base = "{0:0" + str(len(syllable_members)) + "b}"
							if saved["error_type"] == 3:
								bitval_base = (
									"{0:0" + str(len(syllable_members) + 1) + "b}"
								)

							bitval = bitval_base.format(
								saved["error_nature"]
							)  # we use a binary representation of
							# the stored value to know which checkbox to check
							for k in range(
								0, len(tmpradiosarray[1][saved["error_type"] - 1])
							):
								tmpradiosarray[1][saved["error_type"] - 1][
									k
								].setEnabled(True)
								tmpradiosarray[1][saved["error_type"] - 1][
									k
								].setChecked(bitval[k] == "1")

			self.update()
			QApplication.processEvents()

	def __make_advanced_grid_type__(
		self, target_groupbox: QGroupBox
	) -> List[QRadioButton]:
		"""
		Make an advanced grid for "type d'erreur"
		:param target_groupbox: the parent groupbox of this advanced grid
		:return: array containing the created QRadioButton
		"""
		tmpg = QGroupBox("", target_groupbox)
		tmpradios = []
		tmpr = QRadioButton("Omission", tmpg)
		tmpr.move(15, 25)
		tmpr.resize(250, 20)
		tmpr.setObjectName("1")
		tmpradios.append(tmpr)
		tmpr = QRadioButton("Inintelligible", tmpg)
		tmpr.move(15, 50)
		tmpr.resize(250, 20)
		tmpr.setObjectName("2")
		tmpradios.append(tmpr)
		tmpr = QRadioButton("Distorsion/substitution", tmpg)
		tmpr.move(15, 75)
		tmpr.resize(250, 20)
		tmpr.toggled.connect(self.enable_error_nature_grid)
		tmpr.setObjectName("3")
		tmpradios.append(tmpr)

		tmpg.move(5, 25)
		tmpg.resize(200, 200)
		return tmpradios

	@staticmethod
	def __make_advanced_grid_consonant_nature__(target_groupbox):
		"""
		Creates an advanced grid for "Grille Nature d'erreur consonne"
		:param target_groupbox: the parent groupbox of this advanced grid
		:return: array containing the created QRadioButton
		"""
		tmp_nature = QGroupBox("Nature d'erreur", target_groupbox)
		tmpradios = []
		tmpr = QCheckBox("Lieu d'articulation", tmp_nature)
		tmpr.setObjectName("1")
		tmpr.move(15, 25)
		tmpr.resize(150, 20)
		tmpradios.append(tmpr)
		tmpr = QCheckBox("Mode d'articulation", tmp_nature)
		tmpr.setObjectName("2")
		tmpr.move(15, 50)
		tmpr.resize(150, 20)
		tmpradios.append(tmpr)
		tmpr = QCheckBox("Voisement", tmp_nature)
		tmpr.setObjectName("3")
		tmpr.move(15, 75)
		tmpr.resize(150, 20)
		tmpradios.append(tmpr)
		tmpr = QCheckBox("Nasalité", tmp_nature)
		tmpr.setObjectName("4")
		tmpr.move(15, 100)
		tmpr.resize(150, 20)
		tmpradios.append(tmpr)
		tmpr = QCheckBox("Autre", tmp_nature)
		tmpr.setObjectName("5")
		tmpr.move(15, 125)
		tmpr.resize(150, 20)
		tmpradios.append(tmpr)
		tmp_nature.move(260, 20)
		tmp_nature.resize(200, 200)

		return tmpradios

	@staticmethod
	def __make_advanced_grid_vowel_nature__(
		target_groupbox: QGroupBox,
	) -> List[QCheckBox]:
		"""
		Makes the advanced grid "Nature d'erreur voyelle"
		:param target_groupbox: the parent groupbox of this advanced grid
		:return: array containing the created QRadioButton
		"""
		tmp_nature = QGroupBox("Nature d'erreur", target_groupbox)
		tmpradios = []
		tmpr = QCheckBox("Aperture", tmp_nature)
		tmpr.setObjectName("1")
		tmpr.move(15, 25)
		tmpr.resize(150, 20)
		tmpradios.append(tmpr)
		tmpr = QCheckBox("Lieu d'articulation", tmp_nature)
		tmpr.setObjectName("2")
		tmpr.move(15, 50)
		tmpr.resize(150, 20)
		tmpradios.append(tmpr)
		tmpr = QCheckBox("Arrondissement", tmp_nature)
		tmpr.setObjectName("3")
		tmpr.move(15, 75)
		tmpr.resize(150, 20)
		tmpradios.append(tmpr)
		tmpr = QCheckBox("Nasalité", tmp_nature)
		tmpr.setObjectName("4")
		tmpr.move(15, 100)
		tmpr.resize(150, 20)
		tmpradios.append(tmpr)
		tmpr = QCheckBox("Voisement", tmp_nature)
		tmpr.setObjectName("5")
		tmpr.move(15, 125)
		tmpr.resize(150, 20)
		tmpradios.append(tmpr)
		tmpr = QCheckBox("Autre", tmp_nature)
		tmpr.setObjectName("6")
		tmpr.move(15, 150)
		tmpr.resize(150, 20)
		tmpradios.append(tmpr)

		tmp_nature.move(260, 20)
		tmp_nature.resize(200, 200)

		return tmpradios

	def __make_advanced_grid_cluster__(self, target_groupbox, cluster_members):
		"""
		Makes and advanced grid for "Type d'erreur cluster"
		:param target_groupbox: the parent groupbox of this advanced grid
		:param cluster_members: a list of members of the cluster. Each phonem, here as a string
		:return: array of arrays containing the created QRadioButton (both basic and advanced)
		"""
		logger.critical("Deprecated")

		tmpg = QGroupBox("", target_groupbox)
		tmpradios = []
		tmpr = QRadioButton("Omission", tmpg)
		tmpr.move(15, 55)
		tmpr.resize(250, 20)
		tmpr.toggled.connect(self.enable_cluster_nature_grid)
		tmpr.setObjectName("1")
		tmpradios.append(tmpr)
		tmpr = QRadioButton("Distortion/erreur articulatoire", tmpg)
		tmpr.move(15, 80)
		tmpr.resize(250, 20)
		tmpr.toggled.connect(self.enable_cluster_nature_grid)
		tmpr.setObjectName("2")
		tmpradios.append(tmpr)
		tmpr = QRadioButton("insertion voyelle dans le cluster", tmpg)
		tmpr.move(15, 105)
		tmpr.resize(250, 20)
		tmpr.toggled.connect(self.enable_cluster_nature_grid)
		tmpr.setObjectName("3")
		tmpradios.append(tmpr)
		tmpr = QRadioButton("non identifiable", tmpg)
		tmpr.move(15, 130)
		tmpr.resize(250, 20)
		tmpr.toggled.connect(self.enable_cluster_nature_grid)
		tmpr.setObjectName("4")
		tmpradios.append(tmpr)

		tmpg.move(5, 25)
		tmpg.resize(200, 200)

		tmpradios2 = [[]]
		tmpg2 = QGroupBox("", target_groupbox)

		for i in range(
			0, 5
		):  # We loop for each line: 1 for header, 4 for the actual values defined above
			for j in range(0, len(cluster_members)):
				if i == 0:
					qtmp = QLabel("/" + cluster_members[j] + "/", tmpg2)
					qtmp.move(35 + (j * 50), 25)
					qtmp.resize(20, 20)
				elif i in (1, 2):
					tmpradios2.append([])
					ctmp = QCheckBox("", tmpg2)
					ctmp.move(35 + (j * 50), 55 + ((i - 1) * 25))
					ctmp.resize(20, 20)
					ctmp.setEnabled(False)
					# noinspection PyTypeChecker
					tmpradios2[i - 1].append(ctmp)
				elif i == 3:
					tmpradios2.append([])
					ctmp = QCheckBox("", tmpg2)
					ctmp.move(15 + (j * 50), 55 + ((i - 1) * 25))
					ctmp.resize(20, 20)
					ctmp.setEnabled(False)
					# noinspection PyTypeChecker
					tmpradios2[i - 1].append(ctmp)
				elif i == 4:
					tmpradios2.append([])
			# extra checkbox for insertion at the end
			if i == 3:
				ctmp = QCheckBox("", tmpg2)
				ctmp.move(15 + (len(cluster_members) * 50), 55 + ((i - 1) * 25))
				ctmp.resize(20, 20)
				ctmp.setEnabled(False)
				# noinspection PyTypeChecker
				tmpradios2[i - 1].append(ctmp)

		tmpg2.move(210, 25)
		tmpg2.resize(300, 200)

		return [tmpradios, tmpradios2]

	def __make_advanced_grid_syllable__(
		self, target_groupbox, syllable_members
	) -> Tuple[list[QRadioButton], list[list[QCheckBox]]]:
		"""
		 Makes and advanced grid for "Type d'erreur cluster"
		:param target_groupbox: the parent groupbox of this advanced grid
		:param syllable_members: a list of members of the syllable. Each phonem, here as a string
		:return: array of arrays containing the created QRadioButton (both basic and advanced)
		"""
		tmpg = QGroupBox("", target_groupbox)
		tmpradios: list[QRadioButton] = []
		tmpr = QRadioButton("Omission", tmpg)
		tmpr.move(15, 55)
		tmpr.resize(250, 20)
		tmpr.toggled.connect(self.enable_cluster_nature_grid)
		tmpr.setObjectName("1")
		tmpradios.append(tmpr)
		tmpr = QRadioButton("Distortion/erreur articulatoire", tmpg)
		tmpr.move(15, 80)
		tmpr.resize(250, 20)
		tmpr.toggled.connect(self.enable_cluster_nature_grid)
		tmpr.setObjectName("2")
		tmpradios.append(tmpr)
		tmpr = QRadioButton("non identifiable", tmpg)
		tmpr.move(15, 105)
		tmpr.resize(250, 20)
		tmpr.toggled.connect(self.enable_cluster_nature_grid)
		tmpr.setObjectName("4")
		tmpradios.append(tmpr)

		tmpg.move(5, 25)
		tmpg.resize(200, 200)

		tmpradios2: list[list[QCheckBox]] = [[]]
		tmpg2 = QGroupBox("", target_groupbox)

		for i in range(
			0, 4
		):  # We loop for each line: 1 for header, 3 for the actual values defined above
			for j in range(0, len(syllable_members)):
				if i == 0:
					qtmp = QLabel("/" + syllable_members[j] + "/", tmpg2)
					qtmp.move(35 + (j * 50), 25)
					qtmp.resize(20, 20)
				elif i in (1, 2):
					tmpradios2.append([])
					ctmp = QCheckBox("", tmpg2)
					ctmp.move(35 + (j * 50), 55 + ((i - 1) * 25))
					ctmp.resize(20, 20)
					ctmp.setEnabled(False)
					tmpradios2[i - 1].append(ctmp)

		tmpg2.move(210, 25)
		tmpg2.resize(300, 200)

		return tmpradios, tmpradios2

	def save_advanced_and_next(self):
		"""
		Saves the judge input for all the advanced grids
		:return:
		"""
		for i in range(0, self.advanced_tabs.count()):
			pos = self.advanced_tabs.widget(i).objectName()
			syll_pos = pos[0]
			phon_pos = pos[2]

			error_type = 0
			error_nature = 0

			error_verbatim = self.error_verbatim[i].toPlainText().strip()

			if error_verbatim == "":
				error_verbatim = None

			error_type_found = False
			for j in range(0, len(self.error_type[i])):
				if self.error_type[i][j].isChecked():
					error_type = int(self.error_type[i][j].objectName())
					error_type_found = True
					# we get the error nature too
					if isinstance(self.error_nature[i][j], list):
						error_nature = 0
						for k in range(0, len(self.error_nature[i][j])):
							if self.error_nature[i][j][k].isChecked():
								error_nature += pow(
									2, len(self.error_nature[i][j]) - k - 1
								)
					else:
						for k in range(0, len(self.error_nature[i])):
							if self.error_nature[i][k].isChecked():
								error_nature += pow(
									2, len(self.error_nature[i]) - k - 1
								)

			if not error_type_found:
				GeneralTools.alert_box(
					self, "Vous devez préciser l'erreur dans l'onglet n° " + str(i + 1)
				)
				return
			else:
				assert self.values is not None

				try:
					self.set_advanced_stored_result(
						self.session_path,
						self.participant_code,
						self.judge_code,
						int(self.values[0][0]),
						int(syll_pos),
						int(phon_pos),
						error_type,
						error_nature,
						error_verbatim,
					)
				except Exception as e:
					repr(e)

		self.current_file_index += 1
		if self.current_file_index >= len(
			self.file_list
		):  # going to next item or ending
			super().end_cotation()
		else:
			self.file_navigation_list_group.show()
			self.display_current_item_cotation()

	def enable_error_nature_grid(self):
		"""
		Event handler that will enable/disable the advanced "erreur nature" grid based on the value of the basic one
		:return:
		"""
		current_id = self.advanced_tabs.currentIndex()
		enable = self.error_type[current_id][2].isChecked()
		for i in range(0, len(self.error_nature[current_id])):
			self.error_nature[current_id][i].setEnabled(enable)
			self.error_nature[current_id][i].setAutoExclusive(False)
			self.error_nature[current_id][i].setChecked(False)

	# self.error_nature[current_id][i].setAutoExclusive(True)

	def enable_cluster_nature_grid(self):
		"""
		Event handler that will enable/disable the advanced "erreur cluster" grid based on the value of the basic one
		:return:
		"""
		current_id = self.advanced_tabs.currentIndex()
		for i in range(0, len(self.error_type[current_id])):
			if isinstance(self.error_nature[current_id][i], list):
				for j in range(0, len(self.error_nature[current_id][i])):
					self.error_nature[current_id][i][j].setChecked(False)
					self.error_nature[current_id][i][j].setEnabled(
						self.error_type[current_id][i].isChecked()
					)

	@override
	def get_stored_result(
		self,
		session_path: str,
		participant_code: str,
		judge: str,
		pw_id: int,
		syll_pos: int,
		phon_pos: int,
	) -> Union[Row, dict[str, Optional[int]]]:
		"""
		Get the stored result in the participant cotation database for a specific phonem in a specific  syllable
		:param session_path: the path where files are located for this cotation
		:param participant_code: the code of the participant
		:param judge: the code of the judge
		:param pw_id: the id of the pseudoword we are judging
		:param syll_pos: position of the syllable in the pseudoword
		:param phon_pos: position of then phonem in the syllable
		:return: the value that exists in the participant database OR a default value
		"""
		vals = super().get_stored_result(
			session_path, participant_code, judge, pw_id, syll_pos, phon_pos
		)
		if vals is not None:
			return vals
		else:
			return {
				"state": 0,
				"effort": 0,
				"inversion": 0,
				"ajout": 0,
				"error_type": 0,
				"error_nature": 0,
				"error_verbatim": None,
			}

	def set_stored_result(
		self,
		session_path: str,
		participant_code: str,
		judge: str,
		pw_id: int,
		syll_pos: int,
		phon_pos: int,
		is_cluster: int,
		state: int,
		effort: int,
		inversion: int,
		ajout: int,
		phonem: str = "",
	) -> bool:
		"""
		Set the stored result in the participant cotation database for a specific phonem in a specific  syllable
		:param session_path: the path where files are located for this cotation
		:param participant_code: the code of the participant
		:param judge: the code of the judge
		:param pw_id: the id of the pseudoword we are judging
		:param syll_pos: position of the syllable in the pseudoword
		:param phon_pos: position of then phonem in the syllable
		:param is_cluster: the number of element in the cluster (0 or 1 = no cluster)
		:param state: the state value
		:param effort: the effort value
		:param inversion: the inversion value
		:param ajout: the ajout value
		:param phonem: the phonem label for history reasons
		:return: True
		"""
		super().set_stored_result(
			session_path,
			participant_code,
			judge,
			pw_id,
			syll_pos,
			phon_pos,
			is_cluster,
			state,
			effort,
			inversion,
			ajout,
			phonem,
		)
		self.init_file_navigation_list()
		return True
