import logging
import re
import shutil
import subprocess
from json import dumps

import openpyxl
from PyQt5 import QtGui, QtCore
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QMovie
from PyQt5.QtWidgets import QWidget, QGridLayout, QTableWidget, QTabWidget, QInputDialog, QPushButton, QCheckBox, \
    QComboBox, QGroupBox, QHBoxLayout, QHeaderView, QTableWidgetItem, QFileDialog, QMenu, \
    QAction, QMessageBox, QLabel

from school_ringer_modules.config import config_path
from school_ringer_modules.groups_dataclasses import all_groups_of_calls
from school_ringer_modules.schedules_dataclasses import Schedule, schedules
from school_ringer_modules.system import run_command
from school_ringer_modules.time_functions import time_correct, time_string_to_minute

time_pattern = r'^(([0-1][0-9]|2[0-3]|[0-9]):([0-5][0-9]|[0-9])|-)$'
weekdays_names = (
    "monday",
    "tuesday",
    "wednesday",
    "thursday",
    "friday",
    "saturday",
    "sunday"
)


def get_true_service_status() -> bool:
    """
    Возвращает True или False в зависимости от состояния сервиса
    @return: True, если сервис работает, иначе False
    """
    status = subprocess.run("systemctl is-active school-ringer.service",
                            capture_output=True, shell=True).stdout.decode()

    if status.strip() == 'active':
        return True
    return False


class ParentClassForAllSchedules(QWidget):
    def __init__(self) -> None:
        """
        Базовый конструктор класса, определяет имя текущего расписания, доступное во всех функциях
        """
        super().__init__()


class OneScheduleEdit(ParentClassForAllSchedules):
    def __init__(self, schedule_name: str) -> None:
        """
        Базовый конструктор класса, определяет имя текущего расписания, доступное во всех функциях
        """
        self.schedule_name = schedule_name
        super().__init__()
        self.initUI()

    def get_all_lessons_numbers(self) -> [str]:
        """
        Возвращает список номеров уроков для текущего расписания
        """
        res = []
        for row in range(self.current_schedule_table.rowCount()):
            if not self.current_schedule_table.item(row, 0) is None:
                res.append(self.current_schedule_table.item(row, 0).text())
        logging.info(f'Все номера уроков в расписании {self.current_schedule.name}: {res}')
        return res

    def get_current_call_groups(self) -> [str]:
        """
        Возвращает список названий активных групп звонков
        @return: список названий групп звонков
        """
        ans = list(all_groups_of_calls.groups.keys())
        logging.info(f'Активные группы звонков: {ans}')
        return ans

    def switch_schedule_active(self) -> None:
        """
        Меняет активность текущего расписания на противоположную
        @rtype: None
        """
        if self.current_schedule.active is True:
            self.current_schedule.active = False
            self.switch_enable_schedule_button.setStyleSheet("background-color : red")
            logging.info(f'Расписание {self.current_schedule.name} выключено')
        else:
            self.current_schedule.active = True
            self.switch_enable_schedule_button.setStyleSheet("background-color : green")
            logging.info(f'Расписание {self.current_schedule.name} включено')
        schedules.__setitem__(self.current_schedule.name, self.current_schedule)
        self.current_schedule.__repr__()
        self.saveSchedule()
        if (self.current_schedule.active is True and
                run_command('systemctl is-active school-ringer.service').strip() != 'active'):
            ret = QMessageBox.question(self, 'Сервис выключен', "Включить работу сервиса звонков?",
                                       QMessageBox.Yes | QMessageBox.No)
            if ret == QMessageBox.Yes:
                run_command('pkexec sh -c "systemctl enable school-ringer.service && '
                            'systemctl restart school-ringer.service"')

    def change_precall(self) -> None:
        """
        Меняет наличие/отсутствие предварительного звонка для текущего расписания
        @rtype: None
        """
        checkbox = self.sender()
        if checkbox.isChecked():
            self.pre_call_combo_box.setDisabled(False)
            if self.current_schedule.pre_call == 0:
                self.current_schedule.pre_call = 1
            self.pre_call_combo_box.setCurrentIndex(self.current_schedule.pre_call)
            logging.info(f'Предварительный звонок для расписания {self.current_schedule.name} установлен на 1 минуту')
        else:
            self.current_schedule.pre_call = 0
            self.pre_call_combo_box.setDisabled(True)
            logging.info(f'Предварительный звонок для расписания {self.current_schedule.name} выключен')
        schedules.__setitem__(self.current_schedule.name, self.current_schedule)
        logging.info(f'Расписание {self.current_schedule.name}: {self.current_schedule.to_dict()}')
        self.current_schedule.__repr__()

    def change_precall_time(self) -> None:
        """
        Меняет время предварительного звонка для текущего расписания
        @rtype: None
        """
        sender = self.sender()
        self.current_schedule.pre_call = sender.currentIndex() if sender.currentIndex() != 0 else 0
        if sender.currentIndex() == 0:
            self.pre_call_combo_box.setDisabled(True)
            self.pre_call_check_box.setChecked(False)
            logging.info(f'Предварительный звонок для расписания {self.current_schedule.name} выключен')
        else:
            logging.info(f'Предварительный звонок для расписания {self.current_schedule.name} установлен на '
                         '{sender.currentIndex()} минут')
        schedules.__setitem__(self.current_schedule.name, self.current_schedule)
        logging.info(f'Расписание {self.current_schedule.name}: {self.current_schedule.to_dict()}')
        self.current_schedule.__repr__()

    def change_weekdays(self) -> None:
        """
        Меняет дни недели для текущего расписания
        @rtype: None
        """
        checkbox = self.sender()
        weekday_name = checkbox.weekday_name
        if checkbox.isChecked():
            self.current_schedule.weekdays[weekday_name] = True
            logging.info(f'В расписание {self.current_schedule.name} добавлен день недели {weekday_name}')
        else:
            self.current_schedule.weekdays[weekday_name] = False
            logging.info(f'Из расписания {self.current_schedule.name} убран день недели {weekday_name}')
        schedules.__setitem__(self.current_schedule.name, self.current_schedule)
        logging.info(f'Расписание {self.current_schedule.name}: {self.current_schedule.to_dict()}')
        self.current_schedule.__repr__()

    def check_schedule_input_table_values(self, item: QTableWidgetItem) -> None:
        """
        Проверяет корректность расписания при вводе с клавиатуры или импорте из файла.
        При необходимости происходит корректировка.
        :param item: QTableWidgetItem - ячейка, которая была изменена
        @rtype: None
        """
        if item is None:
            for row in range(self.current_schedule_table.rowCount()):
                if not self.current_schedule_table.item(row, 0) is None:
                    try:
                        self.current_schedule.lessons[self.current_schedule_table.item(row, 0).text()]["group"] = \
                            self.current_schedule_table.cellWidget(row, 3).currentText()
                    except Exception:
                        logging.info(f'В расписании {self.current_schedule.name} не все поля заполнены корректно')
                        logging.info(f'Ошибка возникла при установке группы урока')
                        print('Не все поля для урока заполнены корректно')

        elif item.column() == 0:
            lesson_number = item.text()
            current_lessons_numbers = self.get_all_lessons_numbers()
            if lesson_number in current_lessons_numbers:
                current_lessons_numbers.remove(lesson_number)
            done1 = True
            while (not lesson_number.isnumeric() or lesson_number in current_lessons_numbers) and done1:
                if lesson_number.isnumeric():
                    lesson_number, done1 = QInputDialog.getText(
                        self, 'Ошибка', f'Урок № {lesson_number} уже есть\nВведите другой номер урока')
                else:
                    lesson_number, done1 = QInputDialog.getText(
                        self, 'Ошибка',
                        f'Введён некорректный номер урока {lesson_number}\nВведите одно число - номер урока')
            if done1:
                self.current_schedule_table.item(item.row(), 0).setText(lesson_number)
                logging.info(f'В расписании {self.current_schedule.name} введён номер урока {lesson_number}')
            else:
                row = item.row()
                self.current_schedule_table.setItem(row, 0, None)
                if self.current_schedule_table.cellWidget(row, 3) is not None:
                    self.current_schedule_table.removeCellWidget(row, 3)
                    logging.info(
                        f'В расписании {self.current_schedule.name} после отмены ввода урока в строке {row}'
                        'удалена группа звонка')
                logging.info(
                    f'В расписании {self.current_schedule.name} не введён номер урока (отмена ввода), строка {row}')

            self.current_schedule.lessons = dict()
            for row in range(self.current_schedule_table.rowCount()):
                if self.current_schedule_table.item(row, 0) is not None:
                    lesson = self.current_schedule_table.item(row, 0).text()
                    if self.current_schedule_table.cellWidget(row, 3) is None:
                        list_of_groups = QComboBox()
                        list_of_groups.addItems(self.get_current_call_groups())
                        list_of_groups.currentIndexChanged.connect(
                            lambda: self.check_schedule_input_table_values(self.current_schedule_table.item(row, 3)))
                        self.current_schedule_table.setCellWidget(row, 3, list_of_groups)
                    try:
                        self.current_schedule.lessons[lesson] = {
                            "start": self.current_schedule_table.item(row, 1).text(),
                            "end": self.current_schedule_table.item(row, 2).text(),
                            "group": self.current_schedule_table.cellWidget(row, 3).currentText()
                        }
                    except Exception:
                        logging.info(
                            f'В расписание {self.current_schedule.name} не внесён урок '
                            f'{lesson_number}, не все остальные поля заполнены корректно')
                        print('Не все поля для урока заполнены корректно')

        elif item.column() in (1, 2):
            time = item.text()
            done1 = True
            while done1:
                if time == '-' and item.column() == 2:
                    item.setText('-')
                    break
                if not re.match(time_pattern, time) or (time == '-' and item.column() == 1):
                    time, done1 = QInputDialog.getText(
                        self, 'Ошибка', 'Введён некорректный формат времени\nПример ввода: 11:45')
                    continue
                if re.match(time_pattern, time) is not None:
                    if len(time) != 5 and time != '-':
                        time = time_correct(time)
                    all_times = schedules.get_all_schedules_times()
                    print(f'All times: {all_times}')
                    if time in all_times and self.time_repeat_notification_enabled:
                        dlg = QMessageBox()
                        dlg.setWindowTitle('Внимание')
                        dlg.setText('Это время уже есть в одном из расписаний. '
                                    'При совпадении времени прозвучит только один звонок.\nПродолжить?')
                        dlg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
                        if dlg.exec() == QMessageBox.Cancel:
                            self.current_schedule_table.setItem(item.row(), item.column(), None)
                            break
                        else:
                            self.time_repeat_notification_enabled = False
                            item.setText(time)
                    self.time_repeat_notification_enabled = True
                    lesson_start = self.current_schedule_table.item(item.row(), 1)
                    lesson_finish = self.current_schedule_table.item(item.row(), 2)
                    if lesson_finish is not None and lesson_start is not None:
                        lesson_start = lesson_start.text()
                        lesson_finish = lesson_finish.text()
                        if lesson_finish == '-' or lesson_start == '-':
                            item.setText(time)
                            break
                        if item.column() == 1:
                            lesson_start = time
                            logging.info(f'Установлено время начала урока {lesson_start}')
                        else:
                            lesson_finish = time
                            logging.info(f'Установлено время окончания урока {lesson_finish}')
                        if lesson_finish != '-' and lesson_start != '-':
                            lesson_time = time_string_to_minute(lesson_finish) - \
                                          time_string_to_minute(lesson_start)
                            if lesson_time <= 0:
                                logging.info(
                                    f'Введены время начала урока {lesson_start}, окончания - {lesson_finish}'
                                    ', недопустимые значения')
                                time, done1 = QInputDialog.getText(
                                    self, 'Ошибка',
                                    'Урок не может начинаться позже, чем заканчивается :(\nВведите другое значение '
                                    'времени')
                                continue
                            else:
                                item.setText(time)
                                break
                    else:
                        item.setText(time)
                        break

            if not done1:
                row, column = item.row(), item.column()
                if self.current_schedule_table.item(row, column) is not None:
                    self.current_schedule_table.setItem(row, column, None)
                try:
                    lesson_name = self.current_schedule_table.item(row, 0).text()
                    self.current_schedule.lessons.pop(lesson_name)
                    logging.info(
                        f'Урок {lesson_name} удалён из расписания из-за некорректного или отсутствующего времени')
                except KeyError:
                    print('Этот урок уже не в расписании')
                except AttributeError:
                    print('Номер урока уже удалён')

            try:
                if self.current_schedule_table.item(item.row(), item.column()) is not None and \
                        self.current_schedule_table.item(item.row(), 0).text() not in self.current_schedule.lessons:
                    lesson = self.current_schedule_table.item(item.row(), 0).text()
                    self.current_schedule.lessons[lesson] = {
                        "start": self.current_schedule_table.item(item.row(), 1).text(),
                        "end": self.current_schedule_table.item(item.row(), 2).text(),
                        "group": self.current_schedule_table.cellWidget(item.row(), 3).currentText()
                    }
                if item.column() == 1:
                    self.current_schedule.lessons[self.current_schedule_table.item(item.row(), 0).text()][
                        "start"] = time
                else:
                    self.current_schedule.lessons[self.current_schedule_table.item(item.row(), 0).text()]["end"] = time
                logging.info(f'В расписание {self.current_schedule.name} добавлен урок \
                            {self.current_schedule.to_dict()}')
            except Exception:
                logging.info(f'В расписание {self.current_schedule.name} не добавлен урок, \
                не все поля заполнены корректно')
                print('Не все поля для урока заполнены корректно')

        self.current_schedule.__repr__()
        schedules.__setitem__(self.current_schedule.name, self.current_schedule)
        logging.info(f'Расписание {self.current_schedule.name}: {self.current_schedule.to_dict()}')

    def saveSchedule(self) -> None:
        """
        Сохраняет текущее расписание в файле json
        @rtype: None
        """
        correct_schedule = True
        for row in range(self.current_schedule_table.rowCount()):
            empty_cnt = 0
            for column in range(3):
                if self.current_schedule_table.item(row, column) is None:
                    empty_cnt += 1
            if empty_cnt in (1, 2):
                correct_schedule = False
                logging.info(
                    f'Расписание {self.current_schedule.name} некорректное, строка {row} заполнена не полностью')
                break

        if not correct_schedule:
            alert = QMessageBox()
            alert.setWindowTitle("Внимание!")
            alert.setText("Будут сохранены только те строки расписания, в которых заполнены все ячейки.")
            alert.exec_()

        schedules.save()
        print('\nSaved schedules:\n')
        schedules.__repr__()
        logging.info(f'Сохранено в файл расписание {self.current_schedule.name}: {self.current_schedule.to_dict()}')
        if (self.current_schedule.active is True and
                run_command('systemctl is-active school-ringer.service').strip() != 'active'):
            ret = QMessageBox.question(self, 'Сервис выключен', "Включить работу сервиса звонков?",
                                       QMessageBox.Yes | QMessageBox.No)
            if ret == QMessageBox.Yes:
                run_command('pkexec sh -c "systemctl enable school-ringer.service && '
                            'systemctl restart school-ringer.service"')

    def import_schedule(self) -> None:
        """
        Импортирует расписание из файла Excel.
        При наличии некорректно заполненных строк в файле они игнорируются.
        Также игнорируются строки после 12-й корректной, если их больше 12.
        @rtype: None
        """
        box = QMessageBox()
        box.setStandardButtons(QMessageBox.Yes | QMessageBox.Ok | QMessageBox.Cancel)
        box.setWindowTitle('Выберите действие')
        box.setText('Скачать шаблон расписания?')
        buttonY = box.button(QMessageBox.Yes)
        buttonY.setText('Скачать шаблон')
        buttonN = box.button(QMessageBox.Ok)
        buttonN.setText('Импорт файла')
        buttonC = box.button(QMessageBox.Cancel)
        buttonC.setText('Отмена')
        box.exec_()
        if box.clickedButton() == buttonY:
            directory = QFileDialog.getExistingDirectory(self, 'Выберите директорию для сохранения шаблона')
            if directory:
                shutil.copyfile(f'{config_path}/ring_schedule.xlsx', f'{directory}/ring_schedule.xlsx')
                # subprocess.run(f'xdg-open {directory}/ring_schedule.xlsx', shell=True)
                info_box = QMessageBox(self)
                info_box.setWindowTitle("Шаблон сохранён")
                info_box.setText(f"Шаблон расписания сохранён в {directory}.")
                info_box.exec()
        elif box.clickedButton() == buttonN:

            file, ok = QFileDialog.getOpenFileName(self, "Выберите файл",
                                                   '.', "Excel files (*.xls *.xlsx)")
            if file:
                xl = openpyxl.open(file)
                ws = xl.active
                self.current_schedule_table.clear()
                headers = ["№", "Начало", "Конец", "Группа"]
                self.current_schedule_table.setHorizontalHeaderLabels(headers)
                row_in_ui = 0
                lesson_numbers = []
                # корректный номер урока
                # нумерация строк и столбцов в openpyxl начинается с 1!
                for row in range(1, ws.max_row + 1):
                    if ((type(ws.cell(row, 1).value) is int) or (
                            type(ws.cell(row, 1).value) is str and ws.cell(row, 1).value.isnumeric())) and \
                            str(ws.cell(row, 1).value) not in lesson_numbers:
                        lesson_number = str(ws.cell(row, 1).value)
                        lesson_numbers.append(lesson_number)
                    else:
                        logging.info(
                            f'При импорте расписания из {file} встретился некорректный номер урока'
                            f'{str(ws.cell(row, 1).value)}')
                        continue

                    # корректное время начала и конца
                    time_start, time_finish = ws.cell(row, 2).value, ws.cell(row, 3).value
                    if type(time_start) == str and type(time_finish) == str and \
                            re.match(time_pattern, time_start) and re.match(time_pattern, time_finish):
                        time_start = time_correct(time_start)
                        time_finish = time_correct(time_finish)
                        if time_start == '-':
                            logging.info(f'У урока должно быть время начала. '
                                         f'В таблице встретился урок '
                                         f'{ws.cell(row, 1)} {ws.cell(row, 2)} {ws.cell(row, 3)} {ws.cell(row, 4)}')
                            continue
                        if (time_finish != '-' and
                                time_string_to_minute(time_finish) - time_string_to_minute(time_start) <= 0):
                            lesson_numbers.pop()
                            logging.info(
                                f'При импорте расписания из {file} встретился урок с некорректным временем '
                                f'{time_start} - {time_finish}')
                            continue
                    else:
                        lesson_numbers.pop()
                        logging.info(
                            f'При импорте расписания из {file} встретился урок с некорректным временем '
                            f'{time_start} - {time_finish}')
                        continue

                    # корректная группа урока
                    if type(ws.cell(row, 4).value) in (str, int):
                        group = str(ws.cell(row, 4).value)
                        logging.info(f'При импорте из {file} для урока в строке {row} установлена группа {group}')
                    else:
                        group = 'По умолчанию'
                        logging.info(f'При импорте из {file} для урока в строке {row} установлена группа По умолчанию')

                    self.current_schedule_table.setItem(row_in_ui, 0, QTableWidgetItem(lesson_number))
                    self.current_schedule_table.setItem(row_in_ui, 1, QTableWidgetItem(time_start))
                    self.current_schedule_table.setItem(row_in_ui, 2, QTableWidgetItem(time_finish))
                    list_of_groups = QComboBox()
                    list_of_groups.addItems(self.get_current_call_groups())
                    list_of_groups.currentIndexChanged.connect(
                        lambda: self.check_schedule_input_table_values(self.current_schedule_table.item(row_in_ui, 3)))
                    self.current_schedule_table.setCellWidget(row_in_ui, 3, list_of_groups)
                    if group in self.get_current_call_groups():
                        list_of_groups.setCurrentIndex(self.get_current_call_groups().index(group))
                    row_in_ui += 1
                    if row_in_ui > 11:
                        break

                    self.current_schedule_table.sortItems(1)
            logging.info(f'Из файла {file} импортировано расписание')
            schedules.__repr__()
        else:
            pass

    def initUI(self) -> None:
        """
        Инициализация графического интерфейса
        """
        self.current_schedule = schedules.__getitem__(self.schedule_name)
        grid = QGridLayout()

        self.switch_enable_schedule_button = QPushButton('Вкл/Выкл')
        if self.current_schedule.active is True:
            self.switch_enable_schedule_button.setStyleSheet("background-color : green")
        else:
            self.switch_enable_schedule_button.setStyleSheet("background-color : red")
        self.switch_enable_schedule_button.clicked.connect(self.switch_schedule_active)
        grid.addWidget(self.switch_enable_schedule_button, 0, 0)

        import_schedule_button = QPushButton('Импорт')
        import_schedule_button.clicked.connect(self.import_schedule)
        grid.addWidget(import_schedule_button, 0, 1)

        self.save_schedule_button = QPushButton('Сохранить')
        self.save_schedule_button.clicked.connect(self.saveSchedule)
        grid.addWidget(self.save_schedule_button, 0, 2)

        self.cancel_schedule_button = QPushButton('Отмена')
        grid.addWidget(self.cancel_schedule_button, 0, 3)

        menu_for_pre_calls = [
            "Нет",
            "За 1 минуту",
            "За 2 минуты",
            "За 3 минуты",
            "За 4 минуты",
            "За 5 минут"
        ]
        self.pre_call_check_box = QCheckBox('Предварительный звонок за:')

        self.pre_call_combo_box = QComboBox(self)
        self.pre_call_combo_box.addItems(menu_for_pre_calls)

        self.pre_call_check_box.toggled.connect(self.change_precall)
        if self.current_schedule.pre_call == 0:
            self.pre_call_check_box.setChecked(False)
            self.pre_call_combo_box.setDisabled(True)
        else:
            self.pre_call_check_box.setChecked(True)
            self.pre_call_combo_box.setDisabled(False)
        grid.addWidget(self.pre_call_check_box, 1, 0, 1, 2)

        self.pre_call_combo_box.currentIndexChanged.connect(self.change_precall_time)
        grid.addWidget(self.pre_call_combo_box, 1, 2, 1, 2)

        weekdays_groupbox = QGroupBox("Дни недели")
        grid.addWidget(weekdays_groupbox, 2, 0, 1, 4)

        self.hbox_with_weekdays_labels = QHBoxLayout()
        weekdays_groupbox.setLayout(self.hbox_with_weekdays_labels)
        weekdays_labels = ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс']
        for weekday in range(7):
            one_checkbox_with_weekday = QCheckBox(weekdays_labels[weekday])
            one_checkbox_with_weekday.weekday_name = weekdays_names[weekday]
            one_checkbox_with_weekday.toggled.connect(self.change_weekdays)
            self.hbox_with_weekdays_labels.addWidget(one_checkbox_with_weekday)
            self.hbox_with_weekdays_labels.itemAt(weekday).widget().setChecked(False)
            if self.current_schedule.weekdays[weekdays_names[weekday]] is True:
                self.hbox_with_weekdays_labels.itemAt(weekday).widget().setChecked(True)

        self.current_schedule_table = QTableWidget()
        self.current_schedule_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.current_schedule_table.setColumnCount(4)
        self.current_schedule_table.setRowCount(max(12, len(self.current_schedule.lessons)))
        headers = ["№", "Начало", "Конец", "Группа"]
        self.current_schedule_table.setHorizontalHeaderLabels(headers)

        row = 0
        for lesson_number in self.current_schedule.lessons:
            lesson = self.current_schedule.lessons[lesson_number]
            self.current_schedule_table.setItem(row, 0, QTableWidgetItem(lesson_number))
            self.current_schedule_table.setItem(row, 1, QTableWidgetItem(lesson["start"]))
            self.current_schedule_table.setItem(row, 2, QTableWidgetItem(lesson["end"]))
            list_of_groups = QComboBox()
            list_of_groups.addItems(self.get_current_call_groups())
            list_of_groups.currentIndexChanged.connect(
                lambda: self.check_schedule_input_table_values(self.current_schedule_table.item(row, 3)))
            self.current_schedule_table.setCellWidget(row, 3, list_of_groups)
            if lesson["group"] in self.get_current_call_groups():
                list_of_groups.setCurrentIndex(self.get_current_call_groups().index(lesson["group"]))

            row += 1

        self.current_schedule_table.sortItems(1)

        self.current_schedule_table.itemChanged.connect(self.check_schedule_input_table_values)
        grid.addWidget(self.current_schedule_table, 3, 0, 1, 4)
        self.setLayout(grid)

        self.time_repeat_notification_enabled = True


class QTabWidgetWithRightClick(QTabWidget):
    right_clicked = pyqtSignal()

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

    def mousePressEvent(self, event):
        super().mousePressEvent(event)
        if event.button() == Qt.RightButton:
            self.right_clicked.emit()


class ScheduleEditor(ParentClassForAllSchedules):
    def __init__(self) -> None:
        """
        Базовый конструктор класса
        """
        super().__init__()
        self.initUI()

    def new_tab(self) -> None:
        """
        Добавляет новую вкладку с расписанием
        :return: None
        """
        all_schedules_names = schedules.get_all_schedules_names()
        schedule_name = ''
        done1 = True
        while done1 and (schedule_name == '' or schedule_name in all_schedules_names):
            if schedule_name == '':
                logging.info(f'При создании нового расписания была попытка ввести пустое название')
                schedule_name, done1 = QInputDialog.getText(
                    self, 'Внимание!', f'Введите название для нового расписания')
            else:
                logging.info(
                    f'При создании нового расписания была попытка ввести название {schedule_name}, которое уже есть')
                schedule_name, done1 = QInputDialog.getText(
                    self, 'Внимание!', f'Расписание {schedule_name} уже существует.\nВведите другое название')
        if not done1:
            logging.info(f'Отмена создания нового расписания, сейчас расписаний {self.count}')
            self.tableWidget.tabBar().setCurrentIndex(self.count - 2)

        if done1:
            lessons = dict()
            schedules.__setitem__(schedule_name, Schedule(
                name=schedule_name,
                pre_call=1,
                active=False,
                weekdays={"monday": True,
                          "tuesday": True,
                          "wednesday": True,
                          "thursday": True,
                          "friday": True,
                          "saturday": False,
                          "sunday": False,
                          },
                lessons=lessons
            ))
            tab_page = OneScheduleEdit(schedule_name)
            tab_page.cancel_schedule_button.clicked.connect(self.close_window_with_schedules)
            self.tableWidget.insertTab(self.count - 1, tab_page, schedule_name)
            self.tableWidget.setCurrentIndex(self.count - 1)
            # self.tableWidget.right_clicked.connect(self.right_click_on_schedule_tab)
            self.count += 1
            logging.info(f'Добавлено новое расписание: {schedules[schedule_name].to_dict()}')
            print('All schedules:')

            schedules.__repr__()
        else:
            if self.tableWidget.tabBar().currentIndex() == self.count - 1:
                self.tableWidget.tabBar().setCurrentIndex(self.count - 2)
            logging.info(f'Отмена создания нового расписания')

    def right_click_on_schedule_tab(self) -> None:
        """
        Обработчик нажатия ПКМ на вкладку
        @return: None
        """
        sender = self.sender()
        index = sender.tabBar().currentIndex()
        bars_count = len(self.tableWidget.tabBar())
        if index == bars_count - 1:
            return
        self.tableWidget.setCurrentIndex(index)
        menu = QMenu()
        rename_action = QAction('Переименовать расписание')
        rename_action.triggered.connect(lambda: self.rename_schedule(index))
        menu.addAction(rename_action)
        delete_action = QAction('Удалить расписание')
        delete_action.triggered.connect(lambda: self.delete_schedule(index))
        menu.addAction(delete_action)
        menu.exec_(QtGui.QCursor.pos())

    def rename_schedule(self, index: int) -> None:
        """
        Функия переименования расписания
        @rtype: None
        @param index: индекс вкладки с расписанием, которое нужно переименовать
        """
        schedule_name = self.tableWidget.tabBar().tabText(index)
        all_schedules_names = list(schedules.get_all_schedules_names())
        new_schedule_name = ''
        done1 = True
        while done1 and (new_schedule_name == '' or new_schedule_name in all_schedules_names):
            if new_schedule_name == '':
                logging.info(f'При переименовании расписания была попытка ввести пустое название')
                new_schedule_name, done1 = QInputDialog.getText(
                    self, 'Внимание!', f'Введите новое название для расписания {schedule_name}')
            else:
                logging.info(f'При переименовании расписания была попытка ввести название {new_schedule_name}, '
                             f'которое уже есть')
                new_schedule_name, done1 = QInputDialog.getText(
                    self, 'Внимание!', f'Расписание {new_schedule_name} уже существует.\nВведите другое название')
        if done1:
            if new_schedule_name == 'cat-box':
                cat_window = QWidget(self)
                cat_grid = QGridLayout()
                cat_window.resize(225, 225)
                cat_window.setLayout(cat_grid)
                cat_window.setWindowTitle('cat-box')
                animation_label = QLabel()
                animation_label.setGeometry(QtCore.QRect(25, 25, 200, 200))
                cat_grid.addWidget(animation_label)
                movie = QMovie(f'{config_path}/cat-box.gif')
                animation_label.setMovie(movie)
                cat_button = QPushButton('Хватит уже')
                cat_button.clicked.connect(lambda: cat_window.close())
                cat_grid.addWidget(cat_button)
                movie.start()
                cat_window.move(200, 200)
                cat_window.show()

            schedule = Schedule(
                name=new_schedule_name,
                pre_call=schedules[schedule_name].pre_call,
                active=schedules[schedule_name].active,
                weekdays=schedules[schedule_name].weekdays.copy(),
                lessons=schedules[schedule_name].lessons.copy()
            )
            schedules.__setitem__(new_schedule_name, schedule)
            schedules.__delitem__(schedule_name)
            logging.info(f'Расписание {schedule_name} переименовано на {new_schedule_name}')
            schedules.__repr__()
            self.tableWidget.removeTab(index)
            inserted_schedule = OneScheduleEdit(new_schedule_name)
            self.tableWidget.insertTab(index, inserted_schedule, new_schedule_name)
            self.tableWidget.tabBar().setCurrentIndex(index)

    def delete_schedule(self, index: int) -> None:
        """
        Функция удаления расписания
        @rtype: None
        """
        schedule_name = self.tableWidget.tabBar().tabText(index)
        ret = QMessageBox.question(self, 'Внимание!', f"Вы действительно хотите удалить расписание {schedule_name}?",
                                   QMessageBox.Yes | QMessageBox.No)

        if ret == QMessageBox.Yes:
            self.tableWidget.removeTab(index)
            schedules.__delitem__(schedule_name)
            logging.info(f'Расписание {schedule_name} удалено')
            self.count -= 1
            print('\nAll schedules after del:\n')
            schedules.__repr__()
            if self.tableWidget.tabBar().currentIndex() == self.count - 1:
                self.tableWidget.tabBar().setCurrentIndex(self.count - 2)

    def set_active_tab(self, index: int) -> None:
        """
        Задание активной вкладки
        @rtype: None
        """
        if self.tableWidget.tabBar().tabText(index) == "Добавить":
            self.new_tab()
            self.tableWidget.setCurrentIndex(self.count - 2)
        else:
            self.tableWidget.setCurrentIndex(index)

    def close_window_with_schedules(self) -> None:
        """
        Закрывает окно редактирования расписания по кнопке Отмена
        @rtype: None
        """
        logging.info(f'Вызвано закрытие окна с расписанием')
        self.close()

    def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
        """
        Сигнал закрытия окна для обновления активных расписаний
        @rtype: None
        @param a0: событие, вызвавшее закрытие окна
        """
        tempfile = run_command('mktemp').strip()
        with open(tempfile, 'w', encoding='utf-8') as file:
            file.write(dumps(schedules.schedules, indent=4, ensure_ascii=False))
        diffs = run_command(f'diff {tempfile} {config_path}/schedule.json').strip()
        if diffs == '':
            self.close()
        else:
            ret = QMessageBox.question(self, 'Внимание!', f"Сохранить изменения?",
                                       QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)

            if ret == QMessageBox.Cancel:
                a0.ignore()
            elif ret == QMessageBox.No:
                schedules.open_read_from_file()
                logging.info(f'Отмена сохранения изменений. Расписание считано из файла: {schedules.schedules}')
                self.settings_window.schedule_editor = None
                self.close()
            elif ret == QMessageBox.Yes:
                logging.info(
                    f'Сохранение расписаний в файл, обновление списка активных расписаний, '
                    'закрытие окна редактирования расписаний...')
                schedules.save()
                if (self.tableWidget.currentWidget().current_schedule.active is True and
                        run_command('systemctl is-active school-ringer.service').strip() != 'active'):
                    ret = QMessageBox.question(self, 'Сервис выключен', "Включить работу сервиса звонков?",
                                               QMessageBox.Yes | QMessageBox.No)
                    if ret == QMessageBox.Yes:
                        run_command('pkexec sh -c "systemctl enable school-ringer.service && '
                                    'systemctl restart school-ringer.service"')
                self.settings_window.schedule_editor = None
                self.close()

    def switch_from_add_tab(self) -> None:
        """
        Установка активной вкладки после добавления
        """
        if self.tableWidget.tabBar().currentIndex() == self.count - 1:
            self.tableWidget.setCurrentIndex(self.count - 2)

    def initUI(self) -> None:
        """
        Инициализация графического интерфейса
        """
        grid = QGridLayout()
        self.setLayout(grid)

        self.schedules_names = list(schedules.get_all_schedules_names())

        self.tableWidget = QTabWidgetWithRightClick()
        self.tabs = []
        for i in range(len(self.schedules_names)):
            added_schedule = OneScheduleEdit(self.schedules_names[i])
            self.tabs.append(added_schedule)
            self.tabs[i].cancel_schedule_button.clicked.connect(self.close_window_with_schedules)
            self.tableWidget.addTab(self.tabs[i], self.schedules_names[i])

        self.tableWidget.addTab(QWidget(), "Добавить")
        self.count = self.tableWidget.count()
        self.tableWidget.tabBarClicked.connect(self.set_active_tab)
        self.tableWidget.right_clicked.connect(self.right_click_on_schedule_tab)
        self.tableWidget.currentChanged.connect(self.switch_from_add_tab)
        grid.addWidget(self.tableWidget)
        self.setGeometry(500, 400, 600, 500)
        self.setWindowTitle('Редактирование расписания')
        self.show()
