#!/usr/bin/python3
import datetime
import os
import shutil
import subprocess
import sys
import xml.etree.ElementTree as ET
from zipfile import ZipFile

from PyQt6.QtCore import Qt, QLockFile, QTimer, QTime
from PyQt6.QtGui import QIcon, QKeySequence, QColor, QShortcut, QCloseEvent, QAction
from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QMainWindow, QPushButton, QHBoxLayout, QVBoxLayout, \
    QGroupBox, QTableWidget, QFileDialog, QTableWidgetItem, QAbstractItemView, QComboBox, QMenu
from python3_mos_pyqt_dialogs.logging_functions import reset_log_to_last_n_days
from python3_mos_pyqt_dialogs.pyqt6_dialogs_functions import alert, question

from school_interview_system_record_modules.audio_threads import AudioRecord, TimeThread
from school_interview_system_record_modules.config import pdf_export_name, \
    md5_export_name, xml_export_name, icon_file, version, home_folder, root_config_folder, possible_statuses, log_file
from school_interview_system_record_modules.design import RepeatParticipantRecording
from school_interview_system_record_modules.export_functions import export_current_exam_to_pdf, create_md5_sums
from school_interview_system_record_modules.prepare_to_exam_widget import PrepareToExamWidget
from school_interview_system_record_modules.recording_widget import RecordingWidget
from school_interview_system_record_modules.system_functions import add_value_to_xml_tree, \
    enter_password, get_config_folder_for_current_exam, get_audio_records_folder, get_testing_records_folder, \
    get_config_file
import logging


class MyWindow(QMainWindow):

    def keyPressEvent(self, a0):
        if a0.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return):
            self.update_auditory_number()

    def change_auditory_filter(self):
        """
        Включение/выключение фильтра для аудитории
        """
        self.auditory_filter_enabled = not self.auditory_filter_enabled
        logging.info(f'Фильтр по аудитории в{"ы" if not self.auditory_filter_enabled else ""}ключен')
        self.update_students_table()

    def update_auditory_number(self):
        """
        Обновление номера аудитории и списка работ, записанных в этой аудитории
        """
        try:
            self.auditory_number = f'{int(self.auditory_number_combobox.currentText()):04}'
            self.update_students_table()
            for row in range(self.info_table.rowCount()):
                self.info_table.setItem(row, 1, QTableWidgetItem(self.auditory_number))
            subprocess.run(['py-ini-config', 'set', self.config_file, '-n', 'auditory_number', self.auditory_number])
        except Exception as e:
            logging.error(f'Некорректный номер аудитории. Введите число от 1 до 9999. Исключение: {e}')

    def delete_exam(self):
        """
        Удаление экзамена
        :return: -
        """
        if not enter_password():
            return
        if self.info_table.rowCount() > 0 and self.files:
            index = self.info_table.currentRow()
            if index == -1:
                index = 0
            stored_exam = subprocess.run(f'py-ini-config get {self.config_file} -n exam_file', shell=True,
                                         capture_output=True).stdout.decode().strip()
            if stored_exam == self.files[index]:
                alert(message='Нельзя удалить текущий экзамен до его полного завершения!')
                return
            if question(
                    message=f'Вы действительно хотите удалить экзамен с кодом предмета {self.info_table.item(index, 2).text()} от {self.info_table.item(index, 3).text()}?'):
                file_to_delete = self.files.pop(index)
                if os.path.isfile(file_to_delete):
                    os.remove(file_to_delete)
                else:
                    alert(message=f'Файл {file_to_delete} не найден!')
                self.update_exams_table()
                if self.info_table.rowCount() > 0:
                    if self.info_table.currentRow() >= 0:
                        subprocess.run(['py-ini-config', 'set', self.config_file, '-n', 'exam_file',
                                        self.files[self.info_table.currentRow()]])
                    else:
                        subprocess.run(['py-ini-config', 'set', self.config_file, '-n', 'exam_file',
                                        self.files[0]])
                else:
                    subprocess.run(['py-ini-config', 'del', self.config_file, '-n', 'exam_file'])

    def update_exams_table(self):
        """
        Обновление таблицы экзаменов
        """
        self.info_table.clear()
        self.info_table.setRowCount(0)
        self.info_table.setHorizontalHeaderLabels(
            ['Код ОО', 'Код аудитории', 'Код предмета', 'Дата экзамена', 'Время начала', 'Время завершения'])
        corrupted_files = []
        auditories = set()
        for file in self.files:
            try:
                if self.info_table.rowCount() == 0 or self.info_table.item(self.info_table.rowCount() - 1, 0):
                    self.info_table.setRowCount(self.info_table.rowCount() + 1)
                self.tree = ET.parse(file)
                root = self.tree.getroot()
                exam_params = (
                    'Код_ППЭ', 'Аудитория (вводится вручную!)', 'Код_предмета', 'Дата', 'Начало', 'Окончание')
                # Если вводится, то куда потом пишется, в ведомость?
                for col in range(len(exam_params)):
                    for i in root.iter('Block'):
                        if exam_params[col] in i.attrib['BlockName']:
                            self.info_table.setItem(self.info_table.rowCount() - 1, col, QTableWidgetItem(i.text))
                        if 'Аудитория' in i.attrib['BlockName']:
                            auditories.add(i.text)
                # self.update_students_table()
            except Exception as e:
                logging.warning(f'Выбранный файл {file} имеет неверный формат. Исключение: {e}')
                corrupted_files.append(file)
                self.info_table.removeRow(self.info_table.rowCount() - 1)
        for corrupted_file in corrupted_files:
            self.files.remove(corrupted_file)
        if self.info_table.rowCount() > 0:
            self.info_table.setCurrentCell(0, 0)
        auditories = list(auditories)
        auditories.sort()
        self.auditory_number_combobox.blockSignals(True)
        self.auditory_number_combobox.clear()
        self.auditory_number_combobox.addItems(auditories)
        self.auditory_number_combobox.blockSignals(False)
        auditory_from_file = subprocess.run(['py-ini-config', 'get', self.config_file, '-n', 'auditory_number'],
                                            capture_output=True).stdout.decode().strip()
        if auditory_from_file in auditories:
            self.auditory_number_combobox.setCurrentText(auditory_from_file)
            auditory_to_set_in_table = auditory_from_file
        else:
            auditory_to_set_in_table = self.auditory_number_combobox.currentText()
        if self.info_table.item(self.info_table.rowCount() - 1, 1):
            self.info_table.item(self.info_table.rowCount() - 1, 1).setText(auditory_to_set_in_table)
        else:
            self.info_table.setItem(self.info_table.rowCount() - 1, 1, QTableWidgetItem(auditory_to_set_in_table))

        self.update_students_table()
        # if self.auditory_number_input_field.text().strip():
        #     self.update_auditory_number()
        # else:
        #     alert(message='Необходимо ввести номер аудитории.')

    def update_students_table(self):
        """
        Обновление таблицы учеников
        :return: -
        """
        recorded_files = os.listdir(self.audio_records_folder)
        self.participants_table.clear()
        self.participants_table.setHorizontalHeaderLabels(
            ['№', 'Фамилия', 'Имя', 'Отчество', 'Класс', 'Код участника', 'ОВЗ', 'Статус'])
        self.participants_table.setRowCount(0)
        if self.info_table.rowCount() == 0 or not self.files:
            self.start_exam_button.setEnabled(False)
            self.close_exam_button.setEnabled(False)
            self.export_exam_button.setEnabled(False)
            self.xml_with_current_exam = ''
            return
        index = self.info_table.currentRow()
        file = self.files[index]
        self.xml_with_current_exam = file
        try:
            # self.tree = ET.parse(file)
            self.participants_table.setSortingEnabled(False)
            root = self.tree.getroot()
            # ОВЗ = РезервI, Статус = Не_закончил
            students_params = ('Фамилия', 'Имя', 'Отчество', 'Класс', 'Код_участника', 'РезервI', 'Не_закончил')
            params_cnt = 0
            row = 0
            auditory = ''
            students_params_values = [''] * len(students_params)
            for i in root.iter('Block'):
                # Новый ученик в xml-файле начинается с фамилии
                if 'Фамилия' in i.attrib['BlockName']:
                    # Найден один параметр для ученика - фамилия (счётчик изменится в цикле ниже).
                    # Когда найдутся все 6, можно добавлять строку в таблицу.
                    params_cnt = 0
                    students_params_values = [''] * len(students_params)
                    auditory = ''
                for col in range(len(students_params)):
                    if students_params[col] in i.attrib['BlockName']:
                        students_params_values[col] = i.text
                        params_cnt += 1
                    if 'Аудитория' in i.attrib['BlockName']:
                        auditory = i.text
                # Строка добавляется, если у ученика есть фамилия, просмотрены все его нужные параметры,
                # и он распределён в нужную аудиторию - или если фильтр по аудиториям отключён
                if params_cnt == len(students_params) and students_params_values[0] and (
                        auditory == self.auditory_number or not self.auditory_filter_enabled):
                    self.participants_table.setRowCount(row + 1)
                    self.participants_table.setItem(row, 0, QTableWidgetItem(str(row + 1)))
                    for col in range(len(students_params_values) - 1):
                        self.participants_table.setItem(row, col + 1, QTableWidgetItem(students_params_values[col]))
                    # ОВЗ
                    if students_params_values[-2]:
                        self.participants_table.setItem(row, 6, QTableWidgetItem('Да'))
                    status_combobox = QComboBox()
                    status_combobox.addItems(possible_statuses)
                    self.participants_table.setItem(row, 7, QTableWidgetItem())
                    self.participants_table.setCellWidget(row, 7, status_combobox)
                    self.participants_table.cellWidget(row, 7).currentIndexChanged.connect(self.change_status)
                    # Статус
                    if students_params_values[-1] in possible_statuses:
                        self.participants_table.cellWidget(row, 7).setCurrentIndex(
                            possible_statuses.index(students_params_values[-1]))
                    self.participants_table.item(row, 7).setData(0, self.participants_table.cellWidget(row,
                                                                                                       7).currentText())
                    self.participants_table.setItem(row, 0, QTableWidgetItem(str(row + 1)))
                    # если запись уже есть, выделить строку зелёным цветом
                    if f'{students_params_values[4]}.ogg' in recorded_files:
                        for col in range(len(students_params_values)):
                            self.participants_table.item(row, col).setBackground(QColor('darkgreen'))
                            self.participants_table.item(row, col).setForeground(QColor('white'))
                    row += 1
                    # после внесения текущего ученика в таблицу и до появления следующего ничего делать не нужно
                    students_params_values = [''] * len(students_params)
                    auditory = ''
            # Если есть экзамен и проведена тестовая запись, предоставить возможность начать экзамен
            if self.participants_table.rowCount() > 1 and subprocess.run(['py-ini-config', 'get', self.config_file, '-n', 'last_prepare_date'], capture_output=True).stdout.decode().strip():
                self.start_exam_button.setEnabled(True)

            self.participants_table.setSortingEnabled(True)

        except Exception as e:
            logging.warning(f'Выбранный файл {file} имеет неверный формат. Исключение: {e}')
            if file in self.files:
                self.files.remove(file)
                self.update_exams_table()

    def change_status(self):
        """
        Изменение статуса текущего ученика
        """
        try:
            row = self.participants_table.indexAt(self.sender().pos()).row()
            self.participants_table.item(row, 7).setData(0, self.participants_table.cellWidget(row, 7).currentText())
            students_code = self.participants_table.item(row, 5).text()
            self.tree = add_value_to_xml_tree(
                tree=self.tree,
                students_code=students_code,
                tag_name='Не_закончил',
                added_value=self.sender().currentText())
            self.tree.write(self.xml_with_current_exam, encoding='utf-8')

        except Exception as e:
            logging.warning(f'Во время изменения статуса возникла ошибка: {e}')

    def check_imported_xml(self, filename):
        """
        Проверка импортированного xml на корректность
        :param filename: имя файла с xml
        :return: True/False
        """
        try:
            tree = ET.fromstring(open(filename).read())
            header_dict = {i: 0 for i in
                           ('Регион', 'Код_ППЭ', 'Код_предмета', 'Код_МСУ', 'Название_предмета', 'Дата')
                           }
            students_dict = {i: 0 for i in
                             ('Фамилия', 'Имя', 'Отчество', 'ФИО_участника', 'Класс', 'Аудитория',
                              'Документ_серия', 'Документ_номер', 'Номер_варианта', 'Итоговый_Балл', 'Зачёт',
                              'ФИО Эксперта', 'Не_закончил', 'Код_в_базе', 'Локальный_ID', 'Штрих_код',
                              'Код_участника', 'РезервI', '5баллов')
                             }
            for i in tree.iter('Block'):
                for key in header_dict:
                    if key in i.attrib['BlockName']:
                        header_dict[key] += 1
                for key in students_dict:
                    if key in i.attrib['BlockName']:
                        students_dict[key] += 1
            # Если каждый параметр из заголовка не встречается одинаковое количество раз (и больше 0), файл не подходит
            cnt = header_dict['Регион']
            if cnt == 0:
                return False
            for key in header_dict:
                if header_dict[key] != cnt:
                    return False
            # Если все параметры учеников не встречаются одинаковое количество раз, файл также не подходит
            cnt = students_dict['Фамилия']
            for key in students_dict:
                if students_dict[key] != cnt:
                    return False
            return True
        except Exception as e:
            logging.warning(f'Во время проверки XML возникло исключение: {e}')
            return False

    def add_exam_from_xml(self):
        """
        Добавление экзамена из xml-файла
        :return: -
        """
        file, _ = QFileDialog.getOpenFileName(
            parent=None, caption='Выберите файл', directory=os.getenv('HOME') + '/Загрузки', filter='XML Files (*.xml)'
        )
        if file:
            if not self.check_imported_xml(file):
                alert(
                    message=f'Файл {file} повреждён или не является корректным файлом со станции подготовки. Импорт невозможен.')
                return
            if file not in self.files:
                shutil.copyfile(file, os.path.join(self.config_folder, os.path.basename(file)))
                self.files.append(os.path.join(self.config_folder, os.path.basename(file)))
                self.update_exams_table()
                self.update_auditory_number()
                subprocess.run(['mkdir', '-p', self.audio_records_folder])
            subprocess.run(f'py-ini-config set {self.config_file} -n exam_file '
                           f'{os.path.join(self.config_folder, os.path.basename(file))}', shell=True)

    def start_examen_function(self):
        """
        Начало экзамена
        :return: -
        """
        stored_exam = subprocess.run(f'py-ini-config get {self.config_file} -n exam_file', shell=True,
                                     capture_output=True).stdout.decode().strip()
        if stored_exam and stored_exam != self.xml_with_current_exam:
            alert(message=f'На этом устройстве уже запущен другой экзамен!\nИспользуемая база данных: {stored_exam}')
            return
        # выделено больше или меньше одной строки
        if len(self.participants_table.selectedIndexes()) != self.participants_table.columnCount():
            alert(message=f'Выберите ровно одного участника для начала экзамена')
            return
        if self.participants_table.rowCount() == 0 or not self.participants_table.item(
                self.participants_table.currentRow(), 0):
            alert(message='Не выбрано ни одного участника!')
            return
        current_participant_row = self.participants_table.currentRow()
        if self.participants_table.cellWidget(current_participant_row, 7).currentIndex() != 0:
            alert(message='Невозможно начать экзамен для участника с выставленным статусом.')
            return
        current_info_row = self.info_table.currentRow()
        if current_info_row == -1:
            current_info_row = 0
        self.current_student_barcode = self.participants_table.item(current_participant_row, 5).text()
        self.current_student = (f'{self.participants_table.item(current_participant_row, 1).text()} '
                                f'{self.participants_table.item(current_participant_row, 2).text()} '
                                f'{self.participants_table.item(current_participant_row, 3).text()}')
        self.current_student_number = self.participants_table.item(current_participant_row, 0).text()
        for file in os.listdir(self.audio_records_folder):
            if self.current_student_barcode in file:
                question_window = RepeatParticipantRecording(current_student=self.current_student,
                                                             current_student_recording=os.path.join(self.audio_records_folder, f'{self.current_student_barcode}.ogg'))
                if not question_window.exec():
                    return
                else:
                    os.remove(os.path.join(self.audio_records_folder, f'{self.current_student_barcode}.ogg'))

        start_examen_question = 'Подтвердить начало экзамена и потоковой аудиозаписи?'
        if self.whole_current_examen_recording_pid != -1:
            start_examen_question = f'Внимание! Начать экзамен для участника {self.current_student}?'
        if not question(message=start_examen_question):
            return
        self.current_student_class = self.participants_table.item(current_participant_row, 4).text()
        exam_data = self.info_table.item(current_info_row, 3).text()
        exam_auditory = self.info_table.item(current_info_row, 1).text()
        self.current_student_barcode = self.participants_table.item(current_participant_row, 5).text()
        self.tree = add_value_to_xml_tree(
            tree=self.tree,
            students_code=self.current_student_barcode,
            tag_name="Аудитория",
            added_value=exam_auditory
        )
        self.recording_window = RecordingWidget(
            audio_records_folder=self.audio_records_folder,
            student=self.current_student,
            students_class=self.current_student_class,
            exam_data=exam_data,
            exam_auditory=exam_auditory,
            students_barcode=self.current_student_barcode
        )
        self.recording_window.start_recording_button.clicked.connect(self.start_recording_from_main_window)
        self.recording_window.stop_recording_button.clicked.connect(self.stop_recording_from_main_window)
        subprocess.run(f'py-ini-config set {self.config_file} -n exam_file {self.xml_with_current_exam}', shell=True)
        self.recording_window.setWindowModality(Qt.WindowModality.ApplicationModal)
        self.recording_window.show()

        if self.whole_current_examen_recording_pid == -1:

            self.whole_examen_timer_thread = TimeThread()
            self.whole_examen_timer_thread.setTerminationEnabled(True)
            self.whole_examen_timer_thread.time_updated.connect(self.update_time_from_examen_start)
            self.whole_examen_timer_thread.start()

            self.whole_examen_recording_thread = AudioRecord()
            self.whole_examen_recording_thread.setTerminationEnabled(True)
            self.all_examen_filename = f"{datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}-rus-exam-auditory-{self.auditory_number}"
            self.whole_examen_recording_thread.filename = os.path.join(
                self.audio_records_folder,
                f"{self.all_examen_filename}.ogg"
            )
            self.whole_examen_recording_thread.process_id_signal.connect(self.update_current_exam_recording_pid)
            self.whole_examen_recording_thread.start()
            if self.info_table.item(current_info_row, 4):
                self.info_table.item(current_info_row, 4).setText(datetime.datetime.now().strftime('%H:%M'))
            else:
                self.info_table.setItem(current_info_row, 4,
                                        QTableWidgetItem(datetime.datetime.now().strftime('%H:%M')))

    def update_time_from_examen_start(self, received_time):
        self.time_from_examen_start = received_time

    def update_current_exam_recording_pid(self, pid):
        self.whole_current_examen_recording_pid = pid

    def start_recording_from_main_window(self):
        """
        Начало записи
        """
        self.last_start_time_of_recording_in_whole_exam = self.time_from_examen_start

    def stop_recording_from_main_window(self):
        """
        Конец записи
        """
        self.close_exam_button.setEnabled(True)
        self.passed_students.append(
            {
                'number': self.current_student_number,
                'fio': self.current_student,
                'class': self.current_student_class,
                'filename': f'{self.current_student_barcode}.ogg',
                'start_time_in_whole': self.last_start_time_of_recording_in_whole_exam,
                'end_time_in_whole': self.time_from_examen_start
            }
        )
        try:
            current_row_number = [self.participants_table.item(row, 5).text() for row in
                                  range(self.participants_table.rowCount())].index(self.current_student_barcode)
            for col in range(self.participants_table.columnCount()):
                if self.participants_table.item(current_row_number, col):
                    self.participants_table.item(current_row_number, col).setBackground(QColor('darkgreen'))
                    self.participants_table.item(current_row_number, col).setForeground(QColor('white'))
        except ValueError:
            logging.warning(f'Ошибка: не найдена запись для {self.current_student}')

    def close_exam_function(self):
        """
        Завершение экзамена
        :return: -
        """
        if question(message='Подтвердите завершение текущего экзамена'):
            if not enter_password():
                return
            self.whole_examen_recording_thread.terminate()
            subprocess.run(['kill', f'{self.whole_current_examen_recording_pid}'])
            logging.info(f'Stopped process pid={self.whole_current_examen_recording_pid}')
            self.start_exam_button.setEnabled(False)
            self.close_exam_button.setEnabled(False)
            self.export_exam_button.setEnabled(True)
            self.export_whole_examen_record_btn.setEnabled(True)
            current_info_row = self.info_table.currentRow()
            if current_info_row == -1:
                current_info_row = 0
            if self.info_table.item(current_info_row, 5):
                self.info_table.item(current_info_row, 5).setText(datetime.datetime.now().strftime('%H:%M'))
            else:
                self.info_table.setItem(current_info_row, 5,
                                        QTableWidgetItem(datetime.datetime.now().strftime('%H:%M')))

    def export_exam_function(self):
        """
        Экспорт всего экзамена
        :return: -
        """
        # удаление параметра, отвечающего за то, что запущен экзамен
        subprocess.run(f'py-ini-config del {self.config_file} -n exam_file', shell=True)

        # экспорт pdf
        export_current_exam_to_pdf(passed_students=self.passed_students,
                                   auditory_number=self.auditory_number,
                                   audio_records_folder=self.audio_records_folder)

        # экспорт md5
        create_md5_sums(auditory_number=self.auditory_number, audio_records_folder=self.audio_records_folder)

        # экспорт xml со статусами
        try:
            for row in range(self.participants_table.rowCount()):
                if self.participants_table.cellWidget(row, 7).currentIndex() != 0:
                    self.tree = add_value_to_xml_tree(
                        tree=self.tree,
                        students_code=self.participants_table.item(row, 5).text(),
                        tag_name="Не_закончил",
                        added_value=self.participants_table.cellWidget(row, 7).currentText()
                    )
            self.tree.write(f'{self.audio_records_folder}/{xml_export_name}-{self.auditory_number}.xml',
                            encoding='utf-8')
        except Exception as e:
            logging.error(f'Не удалось экспортировать xml со статусами. Ошибка: {e}')

        output_folder = QFileDialog.getExistingDirectory(
            parent=None, caption='Выберите папку для выгрузки', directory=os.getcwd()
        )
        if not output_folder:
            return
        files_to_pack = [i for i in os.listdir(self.audio_records_folder) if i.endswith('.ogg') and 'rus-exam' not in i]
        files_to_pack.append(f'{pdf_export_name}-auditory-{self.auditory_number}.pdf')
        files_to_pack.append(f'{md5_export_name}-auditory-{self.auditory_number}.txt')
        files_to_pack.append(f'{xml_export_name}-{self.auditory_number}.xml')
        with ZipFile(os.path.join(output_folder, f'{self.all_examen_filename}.zip'), 'w') as archive:
            for file in files_to_pack:
                if os.path.isfile(os.path.join(self.audio_records_folder, file)):
                    archive.write(os.path.join(self.audio_records_folder, file), file)
                else:
                    logging.error(f'Файл {os.path.join(self.audio_records_folder, file)} не существует.')
        # self.export_exam_button.setEnabled(False)
        self.close_exam_button.setEnabled(False)
        self.start_exam_button.setEnabled(True)
        alert(message=f'Экзамен закрыт и выгружен в {output_folder}/{self.all_examen_filename}.zip')

    def prepare_to_examen_function(self):
        """
        Проведение подготовки ПК к экзамену
        """
        self.prepare_to_exam_widget = PrepareToExamWidget(
            testing_records_folder=self.testing_records_folder,
            config_file=self.config_file
        )
        self.prepare_to_exam_widget.export_testing_record_btn = self.export_testing_record_btn
        self.prepare_to_exam_widget.parent_last_prepare_date_label = self.last_prepare_date_label
        self.prepare_to_exam_widget.parent_start_examen_button = self.start_exam_button
        self.prepare_to_exam_widget.show()

    def last_prepare_date_update(self):
        """
        Получение даты последней проведённой подготовки к экзамену
        """
        read_last_prepare_date = subprocess.run(['py-ini-config', 'get', self.config_file, '-n', 'last_prepare_date'],
                                                capture_output=True).stdout.decode().strip()
        if read_last_prepare_date:
            self.last_prepare_date_label.setText(f'Последняя проведённая подготовка: {read_last_prepare_date}')
            self.start_exam_button.setEnabled(True)
            self.start_exam_button.setToolTip('')
        else:
            self.last_prepare_date_label.setText(f'Последняя проведённая подготовка: -')
            self.start_exam_button.setEnabled(False)
            self.start_exam_button.setToolTip('Сначала необходимо провести подготовку')
        self.last_prepare_date_label.setWordWrap(True)

    def export_testing_record(self):
        """
        Экспорт тестовой аудиозаписи
        :return: -
        """
        try:
            testing_records = os.listdir(self.testing_records_folder)
        except Exception:
            alert(message='Вы не сделали ни одной тестовой записи.')
            return
        if not testing_records:
            alert(message='Вы не сделали ни одной тестовой записи.')
            return
        if len(testing_records) > 1:
            alert(message='Вы сделали больше одной тестовой записи. Выберите запись для выгрузки.')
            filename, _ = QFileDialog.getOpenFileName(
                parent=None, caption='Выберите тестовую запись', directory=self.testing_records_folder,
                filter="OGG files (*.ogg)"
            )
            if not filename:
                return
        else:
            filename = os.listdir(self.testing_records_folder)[0]

        output_folder = QFileDialog.getExistingDirectory(
            parent=None, caption='Выберите папку для выгрузки', directory=home_folder
        )
        if output_folder:
            shutil.copyfile(os.path.join(self.testing_records_folder, os.path.basename(filename)),
                            os.path.join(output_folder, os.path.basename(filename)))
            alert(message=f'Тестовая аудиозапись выгружена в {output_folder}/{filename}.')

    def export_whole_examen_record(self):
        """
        Экспорт аудиозаписей с текущего экзамена
        :return: -
        """
        try:
            examen_records = [i for i in os.listdir(self.audio_records_folder) if
                              'rus-exam' in i and i.endswith('.ogg')]
        except Exception:
            alert(message='Потоковая запись не найдена.')
            return
        if not examen_records:
            alert(message='Потоковая запись не найдена.')
            return
        if len(examen_records) > 1:
            alert(message='Найдено более одной потоковой записи. Выберите запись для выгрузки.')
            filename, _ = QFileDialog.getOpenFileName(
                parent=None, caption='Выберите потоковую запись', directory=self.audio_records_folder,
                filter="OGG files (*.ogg)"
            )
            if not filename:
                return
        else:
            filename = examen_records[0]

        output_folder = QFileDialog.getExistingDirectory(
            parent=None, caption='Выберите папку для выгрузки', directory=home_folder
        )
        if output_folder:
            shutil.copyfile(os.path.join(self.audio_records_folder, os.path.basename(filename)),
                            os.path.join(output_folder, os.path.basename(filename)))
            alert(message=f'Потоковая аудиозапись выгружена в {output_folder}/{filename}.')

    def closeEvent(self, a0: QCloseEvent):
        if not question(message='Вы действительно хотите выйти из программы?'):
            a0.ignore()
            return
        if not enter_password():
            a0.ignore()
            return
        a0.accept()

    def choose_examen(self):
        """
        Выбор экзамена
        :return: -
        """
        old_config_folder = self.config_folder
        self.config_folder = get_config_folder_for_current_exam()
        if not self.config_folder and not old_config_folder:
            alert(message='Не выбрано ни одного экзамена. Программа завершит работу.')
            sys.exit(0)
        elif not self.config_folder and old_config_folder:
            self.config_folder = old_config_folder
            return
        self.config_folder = os.path.join(root_config_folder, self.config_folder)
        self.audio_records_folder = get_audio_records_folder(self.config_folder)
        self.testing_records_folder = get_testing_records_folder(self.config_folder)
        self.config_file = get_config_file(self.config_folder)

        subprocess.run(['mkdir', '-p', self.audio_records_folder])
        subprocess.run(['mkdir', '-p', self.testing_records_folder])
        subprocess.run(['touch', self.config_file])
        self.files = [os.path.join(self.config_folder, i) for i in os.listdir(self.config_folder) if i.endswith('.xml')]
        self.xml_with_current_exam = subprocess.run(['py-ini-config', 'get', self.config_file, '-n', 'exam_file'])
        if os.path.isdir(self.audio_records_folder) and [i for i in os.listdir(self.audio_records_folder) if
                                                         not i.startswith('.')]:
            if not question(message=f'Папка для хранения записей не пустая! '
                                    f'Если Вы запустили программу в первый раз, очистите папку {self.audio_records_folder} '
                                    f'и перезапустите программу. Продолжить проведение текущего экзамена?'):
                sys.exit(0)
        # Сработает, если это не первый вызов. Если первый, то сработает в initUI
        try:
            self.last_prepare_date_update()
            self.update_exams_table()
            self.update_students_table()
        except Exception as e:
            logging.warning(f'Во время выбора экзамена возникла ошибка: {e}')

    def __init__(self):
        self.xml_with_current_exam = None
        self.files = None
        self.config_file = None
        self.testing_records_folder = None
        self.audio_records_folder = None
        self.config_folder = None
        if not enter_password():
            sys.exit(0)
        self.last_start_time_of_recording_in_whole_exam = '00:00'
        self.time_from_examen_start = '00:00'
        self.whole_examen_timer_thread = None
        self.whole_examen_recording_thread = None
        self.tree = None
        self.all_examen_filename = ''
        self.passed_students = []
        self.auditory_number = ''
        self.auditory_filter_enabled = True
        self.whole_current_examen_recording_pid = -1
        QMainWindow.__init__(self)

        subprocess.run(['mkdir', '-p', root_config_folder])

        self.choose_examen()

        for necessary_program in ('vorbis-tools', 'pavucontrol-gtk', 'wkhtmltopdf'):
            if subprocess.run(['rpm', '-q', necessary_program]).returncode != 0:
                alert(message=f'Установите {necessary_program} и перезапустите программу')
                sys.exit(0)
        self.initUI()

    def initUI(self):
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QHBoxLayout()
        central_widget.setLayout(layout)

        left_layout = QVBoxLayout()

        prepare_to_exam_groupbox = QGroupBox('Подготовка к экзамену')
        prepare_to_exam_groupbox_layout = QVBoxLayout()
        prepare_to_exam_groupbox.setLayout(prepare_to_exam_groupbox_layout)
        self.load_exam_button = QPushButton('Загрузить экзамен')
        self.load_exam_button.clicked.connect(self.add_exam_from_xml)
        prepare_to_exam_groupbox_layout.addWidget(self.load_exam_button)
        self.prepare_button = QPushButton('Провести подготовку')
        self.prepare_button.clicked.connect(self.prepare_to_examen_function)
        prepare_to_exam_groupbox_layout.addWidget(self.prepare_button)
        self.last_prepare_date_label = QLabel()

        prepare_to_exam_groupbox_layout.addWidget(self.last_prepare_date_label)

        left_layout.addWidget(prepare_to_exam_groupbox)

        organize_exam_groupbox = QGroupBox('Проведение экзамена')
        organize_exam_groupbox_layout = QVBoxLayout()
        organize_exam_groupbox.setLayout(organize_exam_groupbox_layout)
        self.start_exam_button = QPushButton('Начать экзамен')
        self.start_exam_button.setEnabled(False)
        self.start_exam_button.clicked.connect(self.start_examen_function)
        organize_exam_groupbox_layout.addWidget(self.start_exam_button)
        self.close_exam_button = QPushButton('Закрыть экзамен')
        self.close_exam_button.setEnabled(False)
        self.close_exam_button.clicked.connect(self.close_exam_function)
        organize_exam_groupbox_layout.addWidget(self.close_exam_button)
        self.export_exam_button = QPushButton('Выгрузить экзамен')
        self.export_exam_button.setEnabled(False)
        self.export_exam_button.clicked.connect(self.export_exam_function)
        organize_exam_groupbox_layout.addWidget(self.export_exam_button)

        self.last_prepare_date_update()
        left_layout.addWidget(organize_exam_groupbox)

        self.export_testing_record_btn = QPushButton('Выгрузить тестовую запись')
        self.export_testing_record_btn.clicked.connect(self.export_testing_record)
        if not [i for i in os.listdir(self.testing_records_folder) if i.endswith('ogg')]:
            self.export_testing_record_btn.setEnabled(False)

        left_layout.addWidget(self.export_testing_record_btn)

        self.export_whole_examen_record_btn = QPushButton('Выгрузить потоковую запись')
        self.export_whole_examen_record_btn.clicked.connect(self.export_whole_examen_record)
        self.export_whole_examen_record_btn.setEnabled(False)
        left_layout.addWidget(self.export_whole_examen_record_btn)

        left_layout.addStretch()

        self.current_system_time_label = QLabel(QTime.currentTime().toString("HH:mm:ss"))
        self.current_system_time_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.current_system_time_label.setStyleSheet('font-size: 30pt;')
        left_layout.addWidget(self.current_system_time_label)
        self.timer = QTimer(self)
        self.timer.timeout.connect(
            lambda: self.current_system_time_label.setText(QTime.currentTime().toString("HH:mm:ss")))
        self.timer.start(1000)

        right_layout = QVBoxLayout()

        right_upper_panel_layout = QHBoxLayout()
        right_upper_panel_layout.addWidget(QLabel('Код аудитории'))
        # self.auditory_number_input_field = QLineEdit()
        # right_upper_panel_layout.addWidget(self.auditory_number_input_field)
        self.auditory_number_combobox = QComboBox()
        self.auditory_number_combobox.currentIndexChanged.connect(self.update_auditory_number)
        right_upper_panel_layout.addWidget(self.auditory_number_combobox)

        # self.enter_auditory_number_button = QPushButton('Ввести')
        # self.enter_auditory_number_button.clicked.connect(self.update_auditory_number)
        # right_upper_panel_layout.addWidget(self.enter_auditory_number_button)
        # self.delete_selected_exam_button = QPushButton('Удалить выбранный экзамен')
        # self.delete_selected_exam_button.setIcon(QIcon(QIcon.fromTheme('data-error')))
        # self.delete_selected_exam_button.clicked.connect(self.delete_exam)
        # right_upper_panel_layout.addWidget(self.delete_selected_exam_button)

        right_layout.addLayout(right_upper_panel_layout)

        self.info_table = QTableWidget()
        self.info_table.setRowCount(0)
        self.info_table.setColumnCount(6)
        self.info_table.setHorizontalHeaderLabels(
            ['Код ОО', 'Код аудитории', 'Код предмета', 'Дата экзамена', 'Время начала', 'Время завершения'])
        self.info_table.horizontalHeader().setStretchLastSection(True)
        self.info_table.cellClicked.connect(self.update_students_table)

        self.info_table.setMaximumHeight(150)
        self.info_table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
        right_layout.addWidget(self.info_table)

        self.participants_table = QTableWidget()
        horizontal_headers = ['№', 'Фамилия', 'Имя', 'Отчество', 'Класс', 'Код участника', 'ОВЗ', 'Статус']
        self.participants_table.setColumnCount(len(horizontal_headers))
        self.participants_table.setRowCount(0)
        self.participants_table.setHorizontalHeaderLabels(horizontal_headers)
        self.participants_table.horizontalHeader().setStretchLastSection(True)
        self.participants_table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
        self.participants_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
        self.participants_table.setSortingEnabled(True)

        right_layout.addWidget(self.participants_table)

        if self.files:
            self.update_exams_table()
            # self.update_students_table()
            self.update_auditory_number()

        layout.addLayout(left_layout)
        layout.addLayout(right_layout)

        # Ctrl + Alt + A - переключение фильтра по аудитории
        self.shortcut = QShortcut(QKeySequence("Ctrl+Alt+A"), self)
        self.shortcut.activated.connect(self.change_auditory_filter)

        menubar = self.menuBar()
        self.window_menu = QMenu('Файл')
        menubar.addMenu(self.window_menu)
        self.switch_examen_action = QAction('Перейти к другому экзамену')
        self.switch_examen_action.triggered.connect(self.choose_examen)
        self.window_menu.addAction(self.switch_examen_action)
        self.quit_action = QAction('Выйти')
        self.quit_action.triggered.connect(lambda: self.close())
        self.window_menu.addAction(self.quit_action)

        # self.setGeometry(50, 50, 1000, 800)
        self.setMinimumWidth(1200)
        self.setMinimumHeight(700)
        self.setWindowTitle(f"Станция записи ответов {version}")
        # Если программа уже установлена
        if os.path.isfile(f'/usr/share/icons/hicolor/scalable/apps/{icon_file}'):
            self.setWindowIcon(QIcon.fromTheme(icon_file.replace('.svg', '')))
        # Если ещё тестируется
        else:
            self.setWindowIcon(QIcon(icon_file))
        self.show()
        logging.info('Программа запущена')


if __name__ == '__main__':
    root = logging.getLogger()
    if root.handlers:
        for handler in root.handlers:
            root.removeHandler(handler)
    filename = log_file
    logging.basicConfig(
        filename=filename,
        format=u'%(asctime)s %(filename)s line:%(lineno)d %(funcName)s() %(message)s',
        level=logging.DEBUG)
    reset_log_to_last_n_days(log_file=log_file, n=7)
    
    try:
        app = QApplication(sys.argv)
        if os.path.isfile(f'/usr/share/icons/hicolor/scalable/apps/{icon_file}'):
            app.setWindowIcon(QIcon.fromTheme(icon_file.replace('.svg', '')))
        lockfile = QLockFile('/tmp/school-interview-system-record.lock')
        if not lockfile.tryLock(100):
            alert(message='Программа уже запущена. Запуск второго экземпляра не допускается.')
            sys.exit(0)
        window = MyWindow()
        sys.exit(app.exec())
    except Exception as e:
        logging.error(f'Во время выполнения программы возникла ошибка: {e}')
