import traceback
import sys
from typing import Union

import pyqtgraph as pg
from pyqtgraph.parametertree import Parameter, ParameterTree
from PyQt5 import QtWidgets
from loguru import logger


class ReportSettings(QtWidgets.QWidget):
    def __init__(self, parent = None):
        super().__init__(parent)
        #self._tab_cashe = LRUCache(maxsize=1000)

    def build(self, index, reg_items: dict, curve_items: dict, qt_items: dict) -> None:
        """Создает ParameterTree для элементов всех графиков выбранной вкладки"""
        try:
            self._clear()
            param_tree = ParameterTree()
            layout = self.layout()
            layout.addWidget(param_tree)
            """if index in self._tab_cashe:
                body = self._tab_cashe[index]
            else:
                body= [
                    self._generate_reg_params(reg_items),
                    self._generate_curve_params(curve_items)
                    ]
                self._tab_cashe[index] = body"""
            body= [
                    self._generate_reg_params(reg_items),
                    self._generate_curve_params(curve_items),
                    self._generate_qt_params(qt_items)
                    ]
            # Добавляем параметры в дерево
            params = Parameter.create(name='params', type='group', children=body)
            params.sigTreeStateChanged.connect(
                lambda: self._update_settings(reg_items, curve_items, qt_items, params)
                )
            param_tree.setParameters(params, showTop=False)
        except: 
            tb = sys.exc_info()[2]
            tbinfo = traceback.format_tb(tb)[0]
            pymsg = "Traceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1])
            logger.error(pymsg)

    def _clear(self) -> None:
        """
        Приводит виджет в готовое к работе состояние.
        Удаляет все содержимое, если имеется
        """
        main = self.layout()
        if self.layout() is not None:
            while main.count():
                child = main.takeAt(0)
                if child.widget() is not None:
                    child.widget().deleteLater()
        else:
            self.setLayout(QtWidgets.QVBoxLayout())

    def _generate_qt_params(self, qt_items: dict) -> dict:
        """Создает qt элементы"""
        res = {'name': 'Qt elements', 'type': 'group', 'children':[
            {'name': key, 'type': 'group', 'children': self._create_qt_samples(item)} for key, item in qt_items.items()
        ]}
        return res

    def _create_qt_samples(self, item: Union[QtWidgets.QWidget, QtWidgets.QLabel]) -> dict:
        visibility = item.isVisible()
        return [
            {'name': 'Visibility', 'type': 'bool', 'value': visibility}
        ]

    def _generate_reg_params(self, 
                             reg_items: dict) -> dict:
        
        """Созадет реальные и идеальные секторы"""

        res = {'name': 'Sectors', 'type': 'group', 'children': [
                {'name': 'Real sectors', 'type': 'group', 'children': self._create_samples(reg_items["real"])},
                {'name': 'Ideal sectors', 'type': 'group', 'children': self._create_samples(reg_items["ideal"])},
            ]}
        return res

    def _generate_curve_params(self, 
                               curve_items: dict) -> dict:
        
        """Создает реальные и идеальные линии графиков"""

        res = {'name': 'Plots', 'type': 'group', 'children': [
                {'name': 'Real plots', 'type': 'group', 'children': self._create_samples(curve_items["real"])},
                {'name': 'Ideal plots', 'type': 'group', 'children': self._create_samples(curve_items["ideal"])},
            ]}
        return res

    def _create_ideal_curves(self, 
                             curve: dict) -> list[dict]:
        
        """Создает секторы с этапами циклограммы"""

        res = []
        for key, item in curve.items():
            param = {'name': key, 'type': 'group', 'children': self._create_samples(item)}
            res.append(param)
        return res
    
    def _create_samples(self, 
                        sector: dict) -> list[dict]:
        
        """Создает список представленных элементов с их параметрами"""

        res = []
        for key, item in sector.items():
            sample = item[0] if type(item) == list else item
            param = {'name': key, 'type': 'group', 'children': self._create_settings(sample)}
            res.append(param)
        return res

    def _create_settings(self, 
                         item: Union[pg.LinearRegionItem, pg.PlotDataItem]) -> list[dict]:
        
        """Получает настройки для элемента"""

        if type(item) == pg.LinearRegionItem:
            pen = item.lines[0].pen
            brush = item.brush
            fill_color = brush.color().getRgb()
        else:
            pen = pg.mkPen(item.opts.get("pen"))
            fill_color = None
        line_color = pen.color().getRgb()
        line_thickness = pen.width()
        visibility = item.isVisible()
        
        return [
            {'name': 'Line color', 'type': 'color', 'value': line_color},
            {'name': 'Line thickness', 'type': 'int', 'value': line_thickness, 'limits': (1, 10)},
            {'name': 'Visibility', 'type': 'bool', 'value': visibility},
            {'name': 'Fill color', 'type': 'color', 'value': fill_color},
        ]

    def _update_settings(self, 
                         reg_items: dict, 
                         curve_items: dict,
                         qt_items: dict, 
                         params: Parameter) -> None:
        
        """Задает параметры элементов в соответствии с paramTree"""

        real_sectors = params.child("Sectors").child("Real sectors")
        ideal_sectors = params.child("Sectors").child("Ideal sectors")

        real_plots = params.child("Plots").child("Real plots")
        ideal_plots = params.child("Plots").child("Ideal plots")
        
        qt_settings = params.child("Qt elements")

        self._set_sector_settings(reg_items["real"], real_sectors)
        self._set_sector_settings(reg_items["ideal"], ideal_sectors)

        self._set_plot_settings(curve_items["real"], real_plots)
        self._set_plot_settings(curve_items["ideal"], ideal_plots)
        self._set_qt_settings(qt_items, qt_settings)

    def _set_sector_settings(self, 
                             sectors: dict, 
                             settings: Parameter) -> None:
        
        """Задает параметры секторов в соответствии с настройками"""
        
        for key, item in sectors.items():
            sample = settings.child(key)
            line_color = sample.child("Line color").value()
            line_width = sample.child("Line thickness").value()
            visibility = sample.child("Visibility").value()
            fill_color = sample.child("Fill color").value()

            pen = pg.mkPen(color=line_color, width=line_width)
            brush=pg.mkBrush(fill_color)
            for reg in item:
                reg.setVisible(visibility)
                reg.lines[0].setPen(pen)
                reg.lines[1].setPen(pen)
                reg.setBrush(brush)

    def _set_plot_settings(self, 
                           curves: dict, 
                           settings: Parameter) -> None:
        
        """Задает параметры кривых в соответствии с настройками"""

        for key, item in curves.items():
            sample = settings.child(key)
            line_color = sample.child("Line color").value()
            line_width = sample.child("Line thickness").value()
            visibility = sample.child("Visibility").value()
            pen = pg.mkPen(color=line_color, width=line_width)
            if type(item) == list:
                for curve in item:
                    curve.setVisible(visibility)
                    curve.setPen(pen)
            else: 
                item.setVisible(visibility)
                item.setPen(pen)
    
    def _set_qt_settings(self, 
                         qt_items: dict, 
                         settings: Parameter) -> None:
         
         """Задает параметры Qt элементов в соответствии с настройками"""

         for key, item in qt_items.items():
            sample = settings.child(key)
            visibility = sample.child("Visibility").value()
            item.setVisible(visibility)