from typing import Callable, Optional, Any

from PyQt5.QtWidgets import (QWidget, QPushButton, 
                             QLineEdit, QHBoxLayout, 
                             QVBoxLayout, QLabel, 
                             QTableWidget, QTableWidgetItem,
                             QStyledItemDelegate)
from PyQt5.QtGui import QIntValidator, QDoubleValidator

from utils.json_tools import read_json, write_json
from utils import qt_settings as qts


class settingsWindow(QWidget):
    def __init__(self, path: str, name: str, upd_func: Callable[[], None], names: dict):
        """
        Окно настроек для редактирования параметров.
        :param path: Путь к файлу настроек (JSON).
        :param name: Название набора настроек.
        :param upd_func: Функция обновления (коллбэк).
        """
        super().__init__()
        self._settingsPath = path
        self._name = name
        self._data: dict[str, list[Any]] = {}
        self._upd_func = upd_func

        self._num_points: Optional[QLineEdit] = None
        self._param_table: Optional[QTableWidget] = None
        self._assosiated_names = names

        self.load_settings()
        self._init_ui()

    def load_settings(self) -> None:
        """Загружает настройки из JSON-файла."""
        data = read_json(self._settingsPath)
        if isinstance(data, dict):
            self._data = data
        else:
            self._data = {}

    def write_settings(self) -> None:
        """Записывает текущие настройки в JSON-файл."""
        write_json(self._settingsPath, self._data)

    def getParams(self) -> dict:
        """Возвращает текущий словарь параметров."""
        return self._data

    def _init_ui(self) -> None:
        """Инициализирует UI: кнопки, поля ввода, таблицу."""
        save_button = QPushButton("Save")
        restore_button = QPushButton("Restore")

        self._num_points = QLineEdit()
        self._num_points.setPlaceholderText("Enter the number of welding points")
        self._num_points.setValidator(QIntValidator())

        control_layout = QHBoxLayout()
        control_layout.addWidget(save_button)
        control_layout.addWidget(restore_button)
        control_layout.addWidget(self._num_points)

        save_button.pressed.connect(self._save)
        restore_button.pressed.connect(self._restore)
        self._num_points.editingFinished.connect(self._expand)

        self._param_table = QTableWidget()
        self._populate_table()

        layout = QVBoxLayout()
        header = QLabel(self._name)
        layout.addWidget(header)
        layout.addLayout(control_layout)
        layout.addWidget(self._param_table)
        self.setLayout(layout)
        self.setStyleSheet(qts.dark_style)

    def _populate_table(self) -> None:
        """Заполняет таблицу значениями из self._data."""
        # Если нет данных для заполнения
        if not self._data:
            self._param_table.setRowCount(0)
            self._param_table.setColumnCount(0)
            return

        # Предполагаем, что у всех ключей одинаковая длина списков параметров.
        first_key = next(iter(self._data), None)
        if first_key is None:
            self._param_table.setRowCount(0)
            self._param_table.setColumnCount(0)
            return

        column_count = len(self._data[first_key])
        self._param_table.setRowCount(len(self._data))
        self._param_table.setColumnCount(column_count)
        headers = [self._assosiated_names[key] for key in self._data.keys()]
        self._param_table.setVerticalHeaderLabels(headers)

        int_delegate = ValidatorDelegate(data_type='int', parent=self._param_table)
        float_delegate = ValidatorDelegate(data_type='float', parent=self._param_table)
        str_delegate = ValidatorDelegate(data_type='str', parent=self._param_table)

        for i, (_, items) in enumerate(self._data.items()):
            for j, item in enumerate(items):
                self._param_table.setItem(i, j, QTableWidgetItem(str(item)))
            if isinstance(item, int):
                self._param_table.setItemDelegateForRow(i, int_delegate)
            elif isinstance(item, float):
                self._param_table.setItemDelegateForRow(i, float_delegate)
            else:
                self._param_table.setItemDelegateForRow(i, str_delegate)
            
                
    def _save(self) -> None:
        """Сохраняет текущие параметры из таблицы в self._data и вызывает _upd_func()."""
        new_data = {}
        col_count = self._param_table.columnCount()
        for i, key in enumerate(self._data.keys()):
            row_data = []
            for j in range(col_count):
                cell_item = self._param_table.item(i, j)
                if cell_item is None:
                    continue
                param_str = cell_item.text()
                # Если ключ не trace_storage_path, конвертируем в float
                if key != "trace_storage_path":
                    try:
                        param = float(param_str)
                    except ValueError:
                        param = 0.0
                else:
                    param = param_str
                row_data.append(param)

            new_data[key] = row_data

        self._data = new_data
        self.write_settings()
        self._upd_func()

    def _restore(self) -> None:
        """Перезагружает данные из файла и обновляет таблицу."""
        self.load_settings()
        self._populate_table()

    def _expand(self) -> None:
        """Расширяет количество столбцов таблицы в зависимости от введённого значения."""
        if not self._num_points:
            return

        num_points_text = self._num_points.text()
        if not num_points_text.isdigit():
            return

        num_points = int(num_points_text)
        if num_points < 0:
            return

        prev_columns = self._param_table.columnCount()
        desired_columns = num_points

        if desired_columns <= 0:
            return

        self._param_table.setColumnCount(desired_columns)

        # Новые столбцы заполняем последним известным параметром для каждого ключа
        for i, (key, items) in enumerate(self._data.items()):
            # Если нет данных, пропускаем
            if not items:
                continue
            last_value = str(items[-1])
            for col in range(prev_columns, desired_columns):
                self._param_table.setItem(i, col, QTableWidgetItem(last_value))
                # Добавляем новый элемент также в self._data для консистентности
                # После этого можно будет сохранить при нажатии Save
            # Дополним также и в self._data
            additional_count = desired_columns - prev_columns
            self._data[key].extend([float(last_value) if key != "trace_storage_path" else last_value] * additional_count)


class SystemSettings(settingsWindow):
    def __init__(self, path, name, upd_func):
        assosiated_names = {
            "trace_storage_path": "Trace path",
            "monitor_update_period": "Monitoring period",
            "a_max_1": "Max lin accel FE, m/s^2",
            "v_max_1": "Max lin speed FE, m/s",
            "a_max_2":"Max lin accel ME, m/s^2",
            "v_max_2": "Max lin speed FE, m/s",
            "mass_1": "Mass FE, kg",
            "mass_2": "Mass ME, kg",
            "k_hardness_1": "Hardness coef FE, N/m",
            "k_hardness_2": "Hardness coef ME, N/m",
            "torque_max_1": "Max torque FE, N*m",
            "torque_max_2": "Max torque ME, N*m",
            "transmission_ratio_1": "Transmission ratio FE",
            "transmission_ratio_2": "Transmission ratio ME",
            "contact_distance_1": "Contact distance FE, m",
            "contact_distance_2": "Contact distance ME, m",
            "k_prop": "Proportionality factor",
            "time_capture": "Calculated points per sec",
            "UML_time_scaler": "UML_time_scaler",
            "Range ME, mm": "Range ME, mm"
            }
        super().__init__(path, name, upd_func, assosiated_names)
        self._num_points.setVisible(False)

    def _expand(self):
        pass     

class OperatorSettings(settingsWindow):
    def __init__(self, path, name, upd_func):
        assosiated_names = {
            "distance_h_start_1": "Closing start dist FE, m" ,
            "distance_h_start_2": "Closing start dist ME, m",
            "distance_s_1": "Rob movement start dist FE, m",
            "distance_s_2": "Rob movement start dist ME, m",
            "distance_l_1": "Max oncoming dist FE, m",
            "distance_l_2": "Max oncoming dist ME, m",
            "distance_h_end1": "Oncoming end dist FE, m",
            "distance_h_end2": "Oncoming end dist FE, m",
            "time_wielding": "Time of welding, sec",
            "time_command": "Communication time compensator, sec",
            "time_robot_movement": "Rob movement time, sec",
            "object_thickness": "Workpiece thickness, m",
            "force_target": "Target force, N",
            "force_capture": "Capture force, N",
            "Tesla closing": "Client closing time, sec",
            "Tesla squeeze": "Client squeeze time, sec",
            "Tesla welding": "Client welding time, sec",
            "Tesla oncomming_relief": "Client moving to next point time, sec",
            "Tesla summary time": "Client summary time, sec"
            }
        super().__init__(path, name, upd_func, assosiated_names)
    pass 


class FilterSettings(settingsWindow):
    def __init__(self, path, name, upd_func):
        assosiated_names = {
            "act_pos_decrease": "ME max pos change in Relief, mm",
            "act_vel_min" : "Minimum for ME speed in Closing mm/sec",
            "act_vel_close" : "Maximum for ME speed in Squeeze mm/sec",
            "act_vel_thresh" : "ME zero speed threshold mm/sec",
            "act_vel_negative" : "ME Relief speed mm/sec",
            "rob_vel_thresh": "Robot zero speed threshold mm/sec",
            "act_force_close" : "Maximum ME force in Closing, N",
            "act_force_weld" : "Minimum ME force in Welding, N",
            "force_increase": "ME force rising speed in Squeeze",
            "force_decrease":"ME force falling speed in Relief"
            }
        super().__init__(path, name, upd_func, assosiated_names)
        self._num_points.setVisible(False)

    def _expand(self):
        pass     


class ValidatorDelegate(QStyledItemDelegate):
    def __init__(self, data_type='str', parent=None):
        super().__init__(parent)
        self.data_type = data_type
        if self.data_type == 'int':
            self.validator = QIntValidator()
        elif self.data_type == 'float':
            self.validator = QDoubleValidator()
            self.validator.setNotation(QDoubleValidator.StandardNotation)
        else:
            self.validator = None

    def createEditor(self, parent, option, index):
        editor = QLineEdit(parent)
        if self.validator:
            editor.setValidator(self.validator)
        return editor



if __name__ == '__main__':
    import pyqtgraph as pg
    app = pg.mkQApp("Parameter Tree Example")
    window = settingsWindow('params\operator_params.json', 'operator')
    app.exec()

    
