from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtWidgets import (
    QDialog,
    QVBoxLayout,
    QScrollArea,
    QWidget,
    QPushButton,
    QLineEdit,
    QMessageBox,
    QHBoxLayout,
    QCheckBox,
)

import numpy as np

from .button_group import ButtonGroup
from .graph_requester import GraphRequester

from parser import Parser

from graph_widget import GraphWidget


class PlotterDialog(QDialog):
    focused_line_edit = None

    def __init__(
        self,
        variable_full_names: dict[str, str],
        function_full_names: dict[str, str],
        variable_values: dict[str, np.ndarray],
    ):
        super().__init__()

        self.variable_values = variable_values

        self.setWindowTitle("Графопостроитель")

        layout_boss = QVBoxLayout()  # главный лояут

        self.scroll = QScrollArea()

        self.scroll.verticalScrollBar().rangeChanged.connect(self.scroll_bottom)

        scrollWidget = QWidget()

        # self.scroll.setFrameShape(QFrame.NoFrame)
        self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)

        self.inputs_layout = QVBoxLayout()  # лаяут первой трети
        self.inputs_layout.addStretch(1)

        self.num_of_input = 0  # инициализация первого графика
        self.add_input()

        QTimer.singleShot(0, self.focus_first_input)

        scrollWidget.setLayout(self.inputs_layout)

        self.scroll.setWidgetResizable(True)
        self.scroll.setWidget(scrollWidget)

        layout_boss.addWidget(self.scroll)

        Button_make_fun_button = QPushButton("+")
        Button_make_fun_button.clicked.connect(self.add_input)
        Button_make_fun_button.setFixedWidth(80)
        layout_boss.addWidget(Button_make_fun_button, alignment=Qt.AlignRight)

        layout_boss.addWidget(
            ButtonGroup(
                "Переменные",
                full_names=variable_full_names,
                buttons_action=self.insert_variable,
            )
        )
        layout_boss.addWidget(
            ButtonGroup(
                "Функции",
                full_names=function_full_names,
                buttons_action=self.insert_function,
            )
        )

        layout_boss.addSpacing(10)

        buttons_layout = QHBoxLayout()

        buttons_layout.setDirection(QHBoxLayout.RightToLeft)

        submit_button = QPushButton("Построить")
        reset_button = QPushButton("Сброс")

        submit_button.clicked.connect(self.plot)
        reset_button.clicked.connect(self.reset)

        submit_button.setDefault(True)

        buttons_layout.addWidget(submit_button)
        buttons_layout.addWidget(reset_button)

        self.subplots_checkbox = QCheckBox("Рисовать графики на отдельных осях")
        buttons_layout.addWidget(self.subplots_checkbox)

        buttons_layout.addStretch(1)

        layout_boss.addLayout(buttons_layout)

        self.setLayout(layout_boss)

    def add_input(self):
        self.num_of_input += 1

        graph_requester = GraphRequester(self.num_of_input)

        for line_edit in (graph_requester.LineEditX, graph_requester.LineEditY):
            line_edit.focused_in.connect(
                lambda line_edit=line_edit: self.set_focused_line_edit(line_edit)
            )

        graph_requester.LineEditGraf.focused_in.connect(
            lambda: self.set_focused_line_edit(None)
        )

        self.inputs_layout.insertWidget(self.inputs_layout.count() - 1, graph_requester)

        graph_requester.LineEditGraf.setFocus()

    def set_focused_line_edit(self, line_edit: QLineEdit | None):
        self.focused_line_edit = line_edit

    def scroll_bottom(self):
        self.scroll.verticalScrollBar().setValue(
            self.scroll.verticalScrollBar().maximum()
        )

    def insert_string(self, string: str, string_cursor_padding=-1):
        line_edit = self.focused_line_edit

        if line_edit is None:
            dlg = QMessageBox(
                QMessageBox.Warning,
                "Ошибка",
                "Выберите поле ввода выражения",
            )
            dlg.exec()

            return

        cusor_pos = line_edit.cursorPosition()
        text = line_edit.text()

        if string_cursor_padding < 1:
            string_cursor_padding = len(string)

        line_edit.setText(text[:cusor_pos] + string + text[cusor_pos:])
        line_edit.setCursorPosition(cusor_pos + string_cursor_padding)

        self.scroll.ensureWidgetVisible(line_edit)
        line_edit.setFocus()

    def insert_variable(self, name: str):
        self.insert_string(f" {name} ")

    def insert_function(self, name: str):
        string = f" {name}()"
        self.insert_string(string, len(string) - 1)  # len - 1 for cursor between braces

    def plot(self):
        xs, ys, labels = [], [], []

        for i in range(self.inputs_layout.count()):
            graph_requester = self.inputs_layout.itemAt(i).widget()

            if graph_requester is not None:
                x_expr = graph_requester.LineEditX.text()
                y_expr = graph_requester.LineEditY.text()
                label = graph_requester.LineEditGraf.text()

                if len(x_expr) * len(y_expr) == 0:
                    dlg = QMessageBox(
                        QMessageBox.Warning,
                        "Ошибка",
                        f'График "{label}" не задан',
                    )
                    dlg.exec()
                    return

                x = Parser(x_expr).evaluate(self.variable_values)
                y = Parser(y_expr).evaluate(self.variable_values)

                xs.append(x)
                ys.append(y)
                labels.append(label)

        mult_subplots = self.subplots_checkbox.isChecked()

        self.graph = GraphWidget(xs, ys, labels, mult_plots=mult_subplots)

        self.graph.show()

    def reset(self):
        dlg = QMessageBox(
            QMessageBox.Question,
            "Очистка",
            "Вы уверены, что хотите очистить все введённые выражения?",
            buttons=QMessageBox.Yes | QMessageBox.No,
        )

        res = dlg.exec()

        if res != QMessageBox.Yes:
            return

        while self.inputs_layout.count() > 0:
            widget = self.inputs_layout.takeAt(0).widget()
            if widget is not None:
                widget.setParent(None)

        self.focused_line_edit = None

        self.num_of_input = 0

        self.add_input()

    def focus_first_input(self):
        first_graph_request = self.inputs_layout.itemAt(0).widget()
        if first_graph_request is not None:
            first_graph_request.LineEditGraf.setFocus()
