#!/usr/bin/python3

import datetime
import os.path
import subprocess
import sys

from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QKeyEvent, QBrush, QColor, QKeySequence, QPalette
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QApplication, QWidget, QAction, QStatusBar, QMenu, QMenuBar, QTableWidgetItem, QHBoxLayout, \
    QTableWidget, QMainWindow, QVBoxLayout, QPushButton, QAbstractItemView, QMessageBox, \
    QFileDialog, QComboBox, QLineEdit, QLabel, QScrollArea, QInputDialog, QSplitter, QShortcut
from anytree.exporter import UniqueDotExporter

from mosplan_modules.config import config_file, version
from mosplan_modules.data_store import import_tree_from_file, node_to_list, Node, node_params
from mosplan_modules.design import TableDateTimeEdit, TableCheckBox, WindowForSelectValue, CreateOrOpenDialog, \
    finish_program_with_error_message, alert, EditCommentDialog
from mosplan_modules.xml_import import import_xml


class NodeTableWidget(QTableWidget):
    move_signal = pyqtSignal(bool)
    open_comment_signal = pyqtSignal(int)

    def __init__(self, *__args):
        super().__init__(*__args)
        self.comment_shortcut = QShortcut(QKeySequence('Ctrl+K'), self)
        self.comment_shortcut.activated.connect(self.send_comment_signal)

    def keyPressEvent(self, a0: QKeyEvent):
        if a0.key() == Qt.Key_Tab:
            self.move_signal.emit(True)
            a0.ignore()
        elif a0.key() == Qt.Key_Backspace:
            self.move_signal.emit(False)
            a0.ignore()
        else:
            a0.accept()

    def send_comment_signal(self):
        self.open_comment_signal.emit(self.currentRow())


class MyWindow(QMainWindow):
    def initUI(self, MainWindow):
        MainWindow.showMaximized()
        self.verticalLayoutWidget = QWidget()
        self.verticalLayoutWidget.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
        self.verticalLayout = QVBoxLayout(self.verticalLayoutWidget)

        self.horizontal_splitter_and_comment_splitter = QSplitter()
        self.horizontal_splitter_and_comment_splitter.setOrientation(Qt.Vertical)
        self.table_and_diagram_splitter = QSplitter()
        self.table_and_diagram_splitter.setOrientation(Qt.Horizontal)

        self.tableWidget = NodeTableWidget()

        self.horizontalHeaders = ['№', 'Этап', 'Продолжительность', 'Дата начала', 'Дата окончания',
                                  'Предшествующие этапы', 'Ответственный', 'Выполнено']
        self.tableWidget.setColumnCount(len(self.horizontalHeaders))
        self.tableWidget.verticalHeader().setStretchLastSection(False)

        self.tableWidget.itemChanged.connect(self.update_table_data_from_item)
        self.tableWidget.move_signal.connect(self.move_node)
        self.tableWidget.open_comment_signal.connect(self.edit_comment)
        self.tableWidget.selectionModel().selectionChanged.connect(self.show_comment)
        self.tableWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.tableWidget.customContextMenuRequested.connect(self.on_customContextMenuRequested)
        self.tableWidget.setEditTriggers(QAbstractItemView.AllEditTriggers)
        self.tableWidget.setHorizontalHeaderLabels(self.horizontalHeaders)
        self.table_and_diagram_splitter.addWidget(self.tableWidget)

        self.graphicsView = QWebEngineView()
        self.graphicsView.setObjectName(u"graphicsView")

        create_or_open_dlg = CreateOrOpenDialog()
        res = create_or_open_dlg.exec_()
        if res:
            self.open_function()
        else:
            self.create_function()
            if not self.filename:
                finish_program_with_error_message('Не выбрана база для работы. Программа завершит работу.')
            if not os.path.isfile(self.filename):
                self.create_temp_tree()

        self.update_tree_from_file()
        self.update_gantt_diagram()

        self.table_and_diagram_splitter.addWidget(self.graphicsView)
        self.graphicsView.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        # self.verticalLayout.addWidget(self.table_and_diagram_splitter)
        self.horizontal_splitter_and_comment_splitter.addWidget(self.table_and_diagram_splitter)
        self.table_and_diagram_splitter.setSizes([self.screen().geometry().width() // 2, self.screen().geometry().width() // 2])
        self.table_and_diagram_splitter.splitterMoved.connect(self.adjust_table_size)

        self.comment_scroll_area = QScrollArea()
        self.comment_widget = QLabel('')
        self.comment_widget.setWordWrap(True)
        self.comment_widget.setOpenExternalLinks(True)
        self.comment_widget.setFixedWidth(self.comment_scroll_area.width())
        self.comment_scroll_area.setWidget(self.comment_widget)
        # self.comment_scroll_area.setMaximumHeight(50)
        # self.verticalLayout.addWidget(self.comment_scroll_area)
        self.horizontal_splitter_and_comment_splitter.addWidget(self.comment_scroll_area)

        self.verticalLayout.addWidget(self.horizontal_splitter_and_comment_splitter)
        self.low_layout = QHBoxLayout()
        self.table_rows_adding_buttons_layout = QHBoxLayout()
        self.add_node_button = QPushButton('+')
        self.add_node_button.clicked.connect(self.add_node)
        self.table_rows_adding_buttons_layout.addWidget(self.add_node_button)

        self.remove_node_button = QPushButton('-')
        self.remove_node_button.clicked.connect(self.remove_node)
        self.table_rows_adding_buttons_layout.addWidget(self.remove_node_button)

        self.move_left_node_button = QPushButton('←')
        self.move_left_node_button.clicked.connect(lambda: self.move_node(False))
        self.table_rows_adding_buttons_layout.addWidget(self.move_left_node_button)

        self.move_right_node_button = QPushButton('→')
        self.move_right_node_button.clicked.connect(lambda: self.move_node(True))
        self.table_rows_adding_buttons_layout.addWidget(self.move_right_node_button)

        self.table_rows_adding_buttons_layout.addStretch()

        self.buttons_layout = QHBoxLayout()
        self.buttons_layout.addStretch()

        self.change_diagram_visibility_button = QPushButton('Скрыть диаграмму')
        self.change_diagram_visibility_button.clicked.connect(self.change_diagram_visibility)
        self.buttons_layout.addWidget(self.change_diagram_visibility_button)

        create_button = QPushButton('Создать')
        create_button.clicked.connect(self.create_function)
        self.buttons_layout.addWidget(create_button)

        open_button = QPushButton('Открыть файл')
        open_button.clicked.connect(self.open_function)
        self.buttons_layout.addWidget(open_button)

        xml_import_button = QPushButton('Импорт xml')
        xml_import_button.setToolTip('Выберите экспортированный из Microsoft Project или ProjectLibre файл xml')
        xml_import_button.clicked.connect(self.xml_import_function)
        self.buttons_layout.addWidget(xml_import_button)

        close_button = QPushButton('Выход из программы')
        close_button.clicked.connect(self.close_function)
        self.buttons_layout.addWidget(close_button)

        if subprocess.run(f'py-ini-config get {config_file} -n diagram_is_visible',
                          shell=True, capture_output=True).stdout.decode().strip() == 'False':
            self.change_diagram_visibility()

        self.low_layout.addLayout(self.table_rows_adding_buttons_layout)
        self.low_layout.addLayout(self.buttons_layout)
        self.verticalLayout.addLayout(self.low_layout)

        MainWindow.setCentralWidget(self.verticalLayoutWidget)
        self.menubar = QMenuBar(MainWindow)
        self.menu = QMenu('Настройки', self.menubar)
        MainWindow.setMenuBar(self.menubar)
        self.action_days_to_delay = QAction('Дни до выполнения задачи', MainWindow)
        self.action_days_to_delay.triggered.connect(self.set_days_to_delay)
        self.action_hide_done_tasks = QAction('Скрывать выполненные задачи на диаграмме', MainWindow)
        self.action_hide_done_tasks.setCheckable(True)
        self.action_hide_done_tasks.triggered.connect(self.change_done_tasks_visibility)

        self.statusbar = QStatusBar(MainWindow)
        self.statusbar.setObjectName(u"statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.menubar.addAction(self.menu.menuAction())
        self.menu.addAction(self.action_days_to_delay)
        self.menu.addAction(self.action_hide_done_tasks)
        self.adjust_table_size()
        self.setWindowTitle(f'Планировщик задач МОСплан ver. {version}')

    @QtCore.pyqtSlot(QtCore.QPoint)
    def on_customContextMenuRequested(self, pos):
        it = self.tableWidget.itemAt(pos)
        if it is None: return
        row = it.row()

        menu = QtWidgets.QMenu()
        edit_comment_action = menu.addAction("Изменить комментарий")
        action = menu.exec_(self.tableWidget.viewport().mapToGlobal(pos))
        if action == edit_comment_action:
            self.edit_comment(row)

    def edit_comment(self, row):
        input_dialog = EditCommentDialog(
            taskname=self.root.descendants[row].name,
            current_text=self.root.descendants[row].comment
        )
        input_dialog.resize(800, 70)
        if input_dialog.exec_() == input_dialog.Accepted:
            text = input_dialog.area.toPlainText()
            if text != '':
                self.root.descendants[row].comment = text
                self.save_tree_to_file(self.filename)
                self.show_comment()

    def change_done_tasks_visibility(self):
        if self.action_hide_done_tasks.isChecked():
            self.is_done_tasks_visible = False
        else:
            self.is_done_tasks_visible = True
        self.update_gantt_diagram()

    def set_days_to_delay(self):
        window_for_select_value = WindowForSelectValue()
        if window_for_select_value.exec_():
            self.time_to_delay = window_for_select_value.spinbox.value()
            self.update_tree_from_file()

    def show_comment(self):
        row = self.tableWidget.currentRow()
        comment = self.root.descendants[row].comment
        words = comment.split()
        for word in words:
            if '://' in word:
                comment = comment.replace(word, f'<a href="{word}">{word}</a>')
        self.comment_widget.setText(comment)
        self.comment_widget.adjustSize()

    def change_diagram_visibility(self):
        if self.diagram_is_visible:
            self.table_and_diagram_splitter.widget(1).setParent(None)
            self.change_diagram_visibility_button.setText('Показать диаграмму')
        else:
            self.table_and_diagram_splitter.addWidget(self.graphicsView)
            self.change_diagram_visibility_button.setText('Скрыть диаграмму')
        self.diagram_is_visible = not self.diagram_is_visible
        if not os.path.isfile(config_file):
            res = subprocess.run(f'py-ini-config set -c {config_file} diagram_is_visible {self.diagram_is_visible} -n',
                                 shell=True).returncode
        else:
            res = subprocess.run(f'py-ini-config set {config_file} diagram_is_visible {self.diagram_is_visible} -n',
                                 shell=True).returncode
        # 1 - ошибка при выполнении
        if res:
            alert(f'Не удалось сохранить настройки в файл {config_file}. Проверьте првава доступа к файлу.')
        self.update_gantt_diagram()
        self.adjust_table_size()

    def update_table_data_from_datetime(self):
        row, column = self.sender().row, self.sender().column
        node = self.root.descendants[row]
        if column == 3:
            timedelta = self.tableWidget.cellWidget(row, 3).date().toPyDate() - node.start
            node.start = self.tableWidget.cellWidget(row, 3).date().toPyDate()
            node.finish = node.finish + timedelta
            self.tableWidget.cellWidget(row, 4).setDate(node.finish)
        if column == 4:
            node.finish = self.tableWidget.cellWidget(row, 4).date().toPyDate()
        if type(node.start) is datetime.datetime:
            node.start = node.start.date()
        if type(node.finish) is datetime.datetime:
            node.finish = node.finish.date()
        if node.finish >= node.start:
            duration = str((node.finish - node.start).days + 1)
        else:
            duration = 'Проверьте даты'
        self.tableWidget.setItem(row, 2, QTableWidgetItem(duration))
        self.tableWidget.item(row, 2).setFlags(Qt.ItemIsEnabled)
        self.save_tree_to_file(self.filename)
        self.change_row_color(row)
        for child in node.children:
            child_row = self.root.descendants.index(child)
            self.change_row_color(child_row)
        self.update_gantt_diagram()

    def update_table_data_from_item(self, item):
        row, column = item.row(), item.column()
        node = self.root.descendants[row]
        if column == 1:
            node.name = item.text().strip()
            item.setText('\t' * (node.depth - 1) + node.name)
        elif column == 5:
            if item.text:
                try:
                    entered_numbers = list(map(int, item.text().split(',')))
                    assert all(1 <= i <= len(self.root.descendants) for i in entered_numbers)
                    node.previous = self.previous_numbers_to_ids(item.text())
                except AssertionError:
                    entered_numbers = [i for i in entered_numbers if 1 <= i <= len(self.root.descendants)]
                    node.previous = self.previous_numbers_to_ids(item.text())
                    self.tableWidget.setItem(
                        row, column, QTableWidgetItem(', '.join([str(i) for i in entered_numbers])))
                except Exception:
                    print(f'Не удалось обработать текст {item.text()}')
                    self.update_tree_from_file()
                    return
            else:
                node.previous = None
        self.save_tree_to_file(self.filename)
        self.update_gantt_diagram()

    def update_table_data_from_worker_list(self):
        try:
            row = self.tableWidget.findChildren(QLineEdit).index(self.sender()) // 3
        except Exception as e:
            print(f'Error occured: {e}')
            return
        node = self.root.descendants[row]
        node.worker = self.sender().text()
        if node.worker not in self.all_workers:
            self.all_workers.add(node.worker)
            self.add_worker_to_lists(node.worker)
        self.save_tree_to_file(self.filename)
        self.update_gantt_diagram()

    def update_table_data_from_checkbox(self):
        try:
            row = self.tableWidget.findChildren(TableCheckBox).index(self.sender().parent())
        except Exception as e:
            print(f'Error occured: {e}')
            return
        node = self.root.descendants[row]
        node.done = self.sender().parent().state()
        self.save_tree_to_file(self.filename)
        self.update_gantt_diagram()

    def save_tree_to_file(self, filename):
        if self.filename:
            filename = self.filename
        exporter = UniqueDotExporter(self.root, nodeattrfunc=node_params)
        try:
            exporter.to_dotfile(filename)
        except Exception as e:
            dlg = QMessageBox()
            dlg.setWindowTitle('Внимание!')
            dlg.setText(f'Ошибка записи в файл {e}')
            dlg.exec()
            sys.exit(1)
        # Необходимо считать и второй раз записать в файл, чтобы у узлов прописались уникальные ID от UniqueDotExporter
        self.root = import_tree_from_file(self.filename)
        # При новом импорте обновляются ID, но имена должны сохраниться. Иначе - сообщение об ошибке
        exporter = UniqueDotExporter(self.root, nodeattrfunc=node_params)
        exporter.to_dotfile(filename)
        # self.update_gantt_diagram()

    def add_worker_to_lists(self, new_worker: str):
        for worker in self.tableWidget.findChildren(QComboBox):
            if new_worker not in [worker.itemText(i) for i in range(worker.count())]:
                worker.addItem(new_worker)
        # TODO разобраться с Enter

    def update_tree_from_file(self):
        try:
            self.tableWidget.itemChanged.disconnect()
        except Exception:
            pass
        self.all_workers = set()
        self.tableWidget.clear()
        self.tableWidget.setHorizontalHeaderLabels(self.horizontalHeaders)
        self.root = import_tree_from_file(self.filename)
        self.tableWidget.setRowCount(len(self.root.descendants))
        for i, node in enumerate(self.root.descendants):
            self.set_node_data_to_table_row(node, i)
            self.all_workers.add(node.worker)
        for worker in self.all_workers:
            self.add_worker_to_lists(worker)
        self.tableWidget.itemChanged.connect(self.update_table_data_from_item)
        # self.update_gantt_diagram()

    def move_node(self, direction: bool = True):
        # True - move right, False - move left
        row = self.tableWidget.currentRow()
        node = self.root.descendants[row]
        if direction:
            other_row = row - 1
            while other_row >= 0 and node.parent != self.root.descendants[other_row].parent:
                other_row -= 1
            if other_row != -1:
                node.parent = self.root.descendants[other_row]
        else:
            if node.parent != self.root:
                node.parent = node.parent.parent
        self.save_tree_to_file(self.filename)
        self.update_tree_from_file()
        self.update_gantt_diagram()

    def previous_ids_to_numbers(self, ids: str):
        ids = ids.replace(' ', '').split(',')
        numbers = []
        for i in range(len(ids)):
            numbers.append(str([node.id for node in self.root.descendants].index(ids[i]) + 1))
        return ', '.join(numbers)

    def previous_numbers_to_ids(self, numbers: str):
        numbers = numbers.replace(' ', '').split(',')
        ids = []
        for i in numbers:
            try:
                ids.append(self.root.descendants[int(i) - 1].id)
            except Exception as e:
                print(f'Не найдено узла с номером {i}, ошибка {e}')
                continue
        return ', '.join(ids)

    def set_node_data_to_table_row(self, node, i):
        node_values = node_to_list(node)
        self.tableWidget.setItem(i, 0, QTableWidgetItem(str(i + 1)))
        self.tableWidget.item(i, 0).setFlags(Qt.ItemIsEnabled)
        self.tableWidget.setItem(i, 1, QTableWidgetItem('\t' * (node.depth - 1) + node_values[0]))
        self.tableWidget.setItem(i, 2, QTableWidgetItem(node_values[1]))
        self.tableWidget.item(i, 2).setFlags(Qt.ItemIsEnabled)
        self.tableWidget.setCellWidget(i, 3, TableDateTimeEdit(node.start))
        self.tableWidget.cellWidget(i, 3).setCalendarPopup(True)
        self.tableWidget.cellWidget(i, 3).setDisplayFormat('dd.MM.yyyy')
        self.tableWidget.cellWidget(i, 3).set_row(i)
        self.tableWidget.cellWidget(i, 3).set_column(3)
        self.tableWidget.cellWidget(i, 3).dateChanged.connect(self.update_table_data_from_datetime)
        self.tableWidget.setCellWidget(i, 4, TableDateTimeEdit(node.finish))
        self.tableWidget.cellWidget(i, 4).setCalendarPopup(True)
        self.tableWidget.cellWidget(i, 4).setDisplayFormat('dd.MM.yyyy')
        self.tableWidget.cellWidget(i, 4).set_row(i)
        self.tableWidget.cellWidget(i, 4).set_column(4)
        self.tableWidget.cellWidget(i, 4).dateChanged.connect(self.update_table_data_from_datetime)
        if node_values[4]:
            self.tableWidget.setItem(i, 5, QTableWidgetItem(self.previous_ids_to_numbers(node_values[4])))
        self.tableWidget.setCellWidget(i, 6, QComboBox())
        self.tableWidget.cellWidget(i, 6).setEditable(True)
        self.tableWidget.cellWidget(i, 6).addItem(node_values[5])
        self.tableWidget.cellWidget(i, 6).lineEdit().editingFinished.connect(self.update_table_data_from_worker_list)
        self.tableWidget.setCellWidget(i, 7, TableCheckBox())
        self.tableWidget.cellWidget(i, 7).set_state(node_values[6])
        self.tableWidget.cellWidget(i, 7).checkbox.stateChanged.connect(self.update_table_data_from_checkbox)
        self.change_row_color(i)

    def change_row_color(self, row):
        if self.tableWidget.item(row, 1):
            if type(self.root.descendants[row].finish) is datetime.datetime:
                self.root.descendants[row].finish = self.root.descendants[row].finish.date()
            # Если задача просрочена
            if self.root.descendants[row].finish <= datetime.datetime.today().date():
                self.tableWidget.item(row, 1).setBackground(QBrush(Qt.red))
            # Если осталась неделя
            elif self.root.descendants[row].finish <= datetime.datetime.today().date() + \
                    datetime.timedelta(self.time_to_delay):
                self.tableWidget.item(row, 1).setBackground(QBrush(QColor('#762176')))
            # Если родитель заканчивается раньше, чем вложенная задача
            elif self.root.descendants[row].parent is not self.root and \
                    self.root.descendants[row].finish > self.root.descendants[row].parent.finish:
                self.tableWidget.item(row, 1).setBackground(QBrush(QColor('#767621')))
            else:
                self.tableWidget.item(row, 1).setBackground(QBrush(self.tableWidget.palette().window().color()))

    def adjust_table_size(self):
        size = self.table_and_diagram_splitter.sizes()[0] if self.diagram_is_visible else self.window().width() - 10
        self.tableWidget.setColumnWidth(0, 30)
        self.tableWidget.setColumnWidth(1, 300)
        self.tableWidget.setColumnWidth(self.tableWidget.columnCount() - 1, 100)
        for column in range(2, self.tableWidget.columnCount() - 1):
            self.tableWidget.setColumnWidth(column, (size - 430) // (self.tableWidget.columnCount() - 3))

        self.tableWidget.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)

    def add_node(self):
        new_node = Node()
        selected = self.tableWidget.selectedItems()
        if selected:
            self.tableWidget.insertRow(self.tableWidget.currentRow() + 1)
            current_level = list(self.root.descendants[self.tableWidget.currentRow()].parent.children)
            index_to_insert = current_level.index(self.root.descendants[self.tableWidget.currentRow()]) + 1
            current_level.insert(index_to_insert, new_node)
            self.root.descendants[self.tableWidget.currentRow()].parent.children = tuple(current_level)
        else:
            self.tableWidget.setRowCount(self.tableWidget.rowCount() + 1)
            if self.tableWidget.rowCount() == 1:
                new_node.parent = self.root
            else:
                new_node.parent = self.root.descendants[-1].parent
        self.save_tree_to_file(self.filename)
        self.update_tree_from_file()

    def remove_node(self):
        row = self.tableWidget.currentRow()
        reply = QMessageBox.question(None, 'Внимание!',
                                     f'Вы действительно хотите удалить задачу "{self.root.descendants[row].name}" '
                                     f'и все вложенные задачи?',
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)

        if reply == QMessageBox.Yes:
            self.root.descendants[row].parent = None
            self.save_tree_to_file(self.filename)
            self.update_tree_from_file()

    def create_function(self):
        fileName, ok = QFileDialog.getSaveFileName(self, "Выберите имя файла для хранения базы", "/tmp/plan1.mosplan",
                                                   "mosplan files (*.mosplan)")
        if ok:
            self.filename = fileName
            try:
                with open(fileName, 'w') as out:
                    pass
            except Exception as e:
                dlg = QMessageBox()
                dlg.setWindowTitle('Внимание!')
                dlg.setText(f'Не удалось выбрать файл {fileName} для хранения базы, ошибка {e}')
                dlg.exec()
                return
            if not os.path.isfile(fileName) or os.path.getsize(fileName) != 0:
                print(f'Не удалось выбрать файл {fileName} для хранения базы.')
                return
            self.tableWidget.clear()
            self.tableWidget.setHorizontalHeaderLabels(self.horizontalHeaders)
            self.tableWidget.setRowCount(0)
            self.root = Node(id='0x0', name='root')
            self.save_tree_to_file(self.filename)
            self.add_node()

    def open_function(self, given_filename=''):
        if not given_filename:
            fileName, ok = QFileDialog.getOpenFileName(self, "Выберите файл", "", "mosplan files (*.mosplan)")
        else:
            fileName, ok = given_filename, True
        if ok:
            try:
                self.root = import_tree_from_file(fileName)
            except Exception as e:
                alert(f'Не удалось загрузить базу из файла {fileName}, ошибка: {e}')
                if not self.filename:
                    sys.exit(1)
                return
            self.filename = fileName
            self.update_tree_from_file()
            self.update_gantt_diagram()
        if not self.filename:
            finish_program_with_error_message('Не выбрана база для работы. Программа завершит работу.')

    def xml_import_function(self):
        fileName, ok = QFileDialog.getOpenFileName(self, "Выберите файл", "", "xml files (*.xml)")
        if ok:
            try:
                root = import_xml(fileName)
            except Exception as e:
                print(f'Не удалось импортировать базу из файла {fileName}, ошибка: {e}')
                return
            if root:
                mosplan_filename, ok = QFileDialog.getSaveFileName(
                    self, 'Выберите файл для сохранения:', f'{fileName[:-4]}.mosplan', "mosplan files (*.mosplan)"
                )
                if ok:
                    self.root = root
                    self.filename = mosplan_filename
                    self.save_tree_to_file(self.filename)
                    self.update_tree_from_file()
                    self.update_gantt_diagram()
                else:
                    return
            else:
                print(f'При импорте возникла ошибка.')
                return

    def close_function(self):
        reply = QMessageBox.question(None, 'Внимание!', 'Выйти из программы?', QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)

        if reply == QMessageBox.Yes:
            self.close()
        else:
            return

    def create_temp_tree(self):
        from anytree.exporter import UniqueDotExporter
        root = Node(name='root')
        stage1 = Node('', 'Первый этап', '2024.06.11', '2024.07.19', 'Артём', parent=root)

        a = Node('', 'Начало', '2024.06.11', '2024.06.12', 'Артём', parent=stage1)
        b = Node('', 'Получить ТЗ', '2024.06.11', '2024.06.11', 'Я', parent=stage1)
        c = Node('', 'Выпить кофе', '2024.06.12', '2024.06.12', 'Милена', parent=stage1)
        d = Node('', 'Съесть печеньку', '2024.06.12', '2024.06.12', 'Милена', parent=c)
        e = Node('', 'Съесть печеньку', '2024.06.11', '2024.06.11', 'Миша', parent=stage1)
        exporter = UniqueDotExporter(root, nodeattrfunc=node_params)
        exporter.to_dotfile(self.filename)

    def update_gantt_diagram(self):
        if self.diagram_is_visible:
            import plotly.express as px
            import pandas as pd
            frame = []
            for node in self.root.descendants:
                if self.is_done_tasks_visible or not node.done:
                    frame.append(
                        dict(Задача=node.name, Начало=node.start, Окончание=node.finish, Ответственный=node.worker,
                             Выполнено='да' if node.done else 'нет'))
            df = pd.DataFrame(frame)

            # Уникальный ID задачи, при том, что их названия и даже исполнители могут совпадать
            df['id'] = df['Задача'] + df['Ответственный'] + str(df['Начало']) + str(df['Окончание'])
            df = df.sort_values('Начало').reset_index()

            fig = px.timeline(df, x_start="Начало", x_end="Окончание", y='id', color="Ответственный",
                              title="Диаграмма Ганта", text=['Выполнено' if i == 'да' else '' for i in df['Выполнено']])
            fig.add_vline(x=datetime.datetime.today())

            fig.update_yaxes(autorange="reversed", title='Задача', tickvals=df['id'], ticktext=df['Задача'])
            self.graphicsView.setHtml(fig.to_html(include_plotlyjs='cdn'))

    def keyPressEvent(self, a0: QKeyEvent):
        # TODO проверить на большой клавиатуре
        if a0.key() == Qt.Key_Plus:
            self.add_node()

    def __init__(self, file_to_open=None):
        QMainWindow.__init__(self)
        self.diagram_is_visible = True
        self.is_done_tasks_visible = True
        self.filename = None
        if not os.path.isfile(config_file):
            res = subprocess.run(f'py-ini-config set -c {config_file} time_to_delay 7 -n',
                                 shell=True).returncode
            # 1 - ошибка при выполнении
            if res:
                alert(f'Не удалось сохранить настройки в файл {config_file}. Проверьте првава доступа к файлу.')
            self.time_to_delay = 7
        else:
            try:
                self.time_to_delay = int(subprocess.run(f'py-ini-config get {config_file} -n time_to_delay',
                                                        shell=True, capture_output=True).stdout.decode())
                assert self.time_to_delay >= 0
            except Exception as e:
                if type(e) == AssertionError:
                    print(f'В файле конфига было записано значение time_to_delay, равное {self.time_to_delay}. '
                          f'Установлено значение 7', file=sys.stderr)
                self.time_to_delay = 7
                res = subprocess.run(f'py-ini-config set {config_file} time_to_delay 7 -n',
                                     shell=True).returncode
                # 1 - ошибка при выполнении
                if res:
                    alert(f'Не удалось сохранить настройки в файл {config_file}. Проверьте права доступа к файлу.')
        self.initUI(self)
        self.show()
        self.all_workers = set()
        if file_to_open:
            self.open_function(file_to_open)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    if len(sys.argv) > 1:
        window = MyWindow(sys.argv[1])
    else:
        window = MyWindow()
    sys.exit(app.exec_())
