from __future__ import annotations
import os
from typing import Optional, Union
from dataclasses import dataclass, field

from cachetools import LRUCache
import pandas as pd
from PyQt5.QtCore import QObject, QTimer
from PyQt5.QtWidgets import QWidget, QTabWidget, QMainWindow, QVBoxLayout

from OptAlgorithm import OptAlgorithm
from utils.qt_settings import dark_style

@dataclass
class KukaTXT:
    time: float = 0
    endtime: float = 0
    #module: str
    func: str = ""
    type_: str = ""
    signal: str = ""
    #line: int = 0
    #point_name: str = ""
    #point_coord: dict = field(default_factory=lambda: {})
    #blending: str = ""
    #blending_param: float = 0
    #velocities: dict = field(default_factory=lambda: {})
    #accelerarions: dict = field(default_factory=lambda: {})
    #base: dict = field(default_factory=lambda: {})
    #tool: dict = field(default_factory=lambda: {})
    #ipo_mode: str = ""
    #motion_mode: str = ""
    #load: dict = field(default_factory=lambda: {})
    #load_a3: dict = field(default_factory=lambda: {})


@dataclass
class KukaDataHead:
    rob_ID: int = 0
    filename: str = ""
    channels: dict = field(default_factory= lambda: {})


@dataclass
class PlotItems:
    regions: dict = field(default_factory= lambda: {})
    curves: dict = field(default_factory= lambda: {})
    qt_items: dict = field(default_factory= lambda: {})


@dataclass
class PointPassport:
    timeframe: list = field(default_factory= lambda: [])
    events: dict = field(default_factory= lambda: {})
    ideal_data: dict = field(default_factory= lambda: {})
    useful_data: dict = field(default_factory= lambda: {})


@dataclass
class UsefulGraphData:
    client_time: float = 0
    range_ME: float = 0
    k_hardness: float = 0


@dataclass
class GraphicPassport:
    dataframe: pd.DataFrame = pd.DataFrame({})
    points_pocket: list[PointPassport] = field(default_factory= lambda: [])
    useful_data: UsefulGraphData = UsefulGraphData()


@dataclass
class Settings:
    operator: dict = field(default_factory= lambda: {})
    system: dict = field(default_factory= lambda: {})
    filter: dict = field(default_factory= lambda: {})



class BaseMediator:
    def __init__(self,
                 converter: BaseDataConverter,
                 passport_former: BasePointPassportFormer,
                 plot: BasePlotWidget,
                 controller: BaseController, 
                 file_manager: BaseFileManager,
                 trace_processor: BaseRawTraceProcessor):
        self._converter = converter
        self._converter.mediator = self
        self._passport_former = passport_former
        self._passport_former.mediator = self
        self._plot = plot
        self._plot.mediator = self
        self._controller = controller
        self._controller.mediator = self
        self._file_manager = file_manager
        self._file_manager.mediator = self
        self._trace_processor = trace_processor
        self._trace_processor.mediator = self

    def notify(self,
               source: Union[BaseDirectoryMonitor, BaseDataConverter, BasePointPassportFormer, BasePlotWidget, BaseRawTraceProcessor],
               data: Union[list[str], list[pd.DataFrame], list[list], list[QWidget], pd.DataFrame]):
        ...

    def prerender_TCW(self, source:Union[BaseController, BaseFileManager, BaseRawTraceProcessor], 
                  data:Union[str, list[str], list[pd.DataFrame, dict]]) -> None:
        ...

    def render_TCW(self, source:Union[BaseController, BaseRawTraceProcessor], 
                  data:Union[str, list[pd.DataFrame, dict]]) -> None:
        ...
    
    def set_mode(self, mode: int) -> None:
        ...

class BaseDirectoryMonitor:

    update_timer = QTimer()

    def __init__(self,
                 file_manager: Optional[BaseFileManager] = None):
        super().__init__()
        self._directory_path = None
        self._update_time = None
        self.isActive = False
        self._files = []
        self._file_manager = file_manager

    @property
    def directory_path(self) -> str:
        return self._directory_path

    @property
    def update_time(self) -> int:
        return self._update_time

    @property
    def files(self) -> list[str]:
        return self._files

    @property
    def file_manager(self) -> BaseFileManager:
        return self._file_manager

    @file_manager.setter
    def file_manager(self, file_manager: BaseFileManager) -> None:
        self._file_manager = file_manager

    def init_state(self):
        files = os.listdir(self._directory_path)
        self._files = files

    def start(self):
        self.isActive = True
        self.update_timer.start(int(self._update_time))

    def stop(self):
        self.isActive = False
        self.update_timer.stop()
    
    def pause(self):
        self.update_timer.stop()


class BaseDataConverter:
    def __init__(self, mediator: Optional[BaseMediator] = None):
        self._mediator = mediator

    @property
    def mediator(self) -> BaseMediator:
        return self._mediator

    @mediator.setter
    def mediator(self, mediator: BaseMediator) -> None:
        self._mediator = mediator

    def convert_data(self, files: list[str]) -> None:
        ...


class BasePlotWidget:
    def __init__(self, 
                 mediator: Optional[BaseMediator] = None,
                 controller: BaseController = None):
        super().__init__()
        self._mediator: BaseMediator = mediator
        self._controller: BaseController = controller
        self._datalen: int = 0
        self._datastep: int = 0

        self._stage_colors = {
                "Closing": [220, 20, 60, 100],     # Crimson
                "Squeeze": [30, 144, 255, 100],    # Dodger Blue
                "Welding": [128, 128, 128, 100],   # Gray
                "Relief": [34, 139, 34, 100],       # Forest Green
                "Oncomming": [255, 165, 0, 100]     # Orange
            }
        self._plt_channels = None

    @staticmethod
    def set_style(object_: Union[QTabWidget, QWidget]) -> None:
        object_.setStyleSheet(
            """QLabel {
                    color: #ffffff;
                    font-size: 26px;
                    font-weight: bold;
                    font-family: "Segoe UI", sans-serif;
                }""")
        
    @property
    def controller(self) -> BaseController:
        return self._controller
    
    @property
    def mediator(self) -> BaseMediator:
        return self._mediator

    @mediator.setter
    def mediator(self, mediator: BaseMediator) -> None:
        self._mediator = mediator

    def build(self, data: list[pd.DataFrame]) -> list[QWidget]:
        ...

    def build_raw_trace(self, data:pd.DataFrame) -> None:
        ...
    
    def set_mode(self, mode:int) -> None:
        ...

class BaseController(QObject):

    def __init__(self,
                 mediator: Optional[BaseMediator] = None, 
                 file_manager: Optional[BaseFileManager] = None):
        super().__init__()
        self._mediator = mediator
        self._file_manager = file_manager

    def send_widgets(self, widgets: list[QWidget]) -> None:
        ...

    def update_settings(self, settings: list[dict]) -> None:
        ...
    
    def set_working_mode(self, mode:int) -> None:
        ...

    def open_file(self, filepath: str) -> None:
        ...
    
    def open_dir(self, dirpath:str) -> None:
        ...

    def update_plots(self) -> None:
        ...
    
    def update_status(self, msg:str) -> None:
        ...
    
    def update_progress(self, progress:int) -> None:
        ...
        
    @property
    def mediator(self) -> BaseMediator:
        return self._mediator

    @mediator.setter
    def mediator(self, mediator: BaseMediator) -> None:
        self._mediator = mediator
    
    @property
    def file_manager(self) -> BaseFileManager:
        return self._file_manager

    @file_manager.setter
    def file_manager(self, file_manager: BaseFileManager) -> None:
        self._file_manager = file_manager


class BaseFileManager:

    def __init__(self,
                 mediator: Optional[BaseMediator] = None, 
                 monitor: Optional[BaseDirectoryMonitor] = None):
        self._mediator = mediator
        self._monitor = monitor
        self._paths_library:set = set()
        self._mode = 0

    @property
    def paths_library(self) -> set:
        return self._paths_library

    @property
    def mediator(self) -> BaseMediator:
        return self._mediator

    @mediator.setter
    def mediator(self, mediator: BaseMediator) -> None:
        self._mediator = mediator

    @property
    def monitor(self) -> BaseDirectoryMonitor:
        return self._monitor

    @monitor.setter
    def monitor(self, monitor: BaseDirectoryMonitor) -> None:
        self._monitor = monitor 

    def replot_all(self) -> None:
        ...
    
    def open_custom_file(self, path:str) -> None:
        ...

    def open_raw_traces_dir(self, path:str) -> None:
        ...
    
    def set_mode(self, num:int) -> None:
        ...
    
    def update_monitor_settings(self, settings:list[dict]) -> None:
        ...

    def add_new_paths(self, paths:list[str]):
        ...


class BaseIdealDataBuilder(OptAlgorithm):

    def __init__(self, settings: Settings):
        self.mul = settings.system['time_capture']
        self.welding_time = settings.operator['time_wielding']
        super().__init__(settings.system, settings.operator)

    def get_closingDF(self) -> pd.DataFrame:
        ...
    
    def get_compressionDF(self) -> pd.DataFrame:
        ...
    
    def get_openingDF(self) -> pd.DataFrame:
        ...
    
    def get_tmovementDF(self) -> pd.DataFrame:
        ...

    def get_weldingDF(self) -> pd.DataFrame:
        ...

    def get_oncomingDF(self) -> pd.DataFrame:
        ...
    
    def get_ideal_timings(self) -> list[float, float, float, float]:
        ...
    
    def get_cycle_time(self) -> float:
        result = sum(self.get_ideal_timings())
        return result


class BaseMainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.resize(200,200)
        # Создаем центральный виджет и устанавливаем его
        self._central_widget = QWidget()
        self.setCentralWidget(self._central_widget)

        # Устанавливаем основной вертикальный макет для центрального виджета
        self._central_layout = QVBoxLayout()
        self._central_widget.setLayout(self._central_layout)
        self.set_style(self)
        ...

    def set_style(self, object: Union[QTabWidget, QWidget, QMainWindow]) -> None:
        object.setStyleSheet(dark_style)

        
class BasePointPassportFormer:

    def __init__(self,
                 mediator: Optional[BaseMediator] = None):
        self._mediator = mediator
        self._clear_stage = "Welding"
        self._settings = Settings
        self._stages = [
            "Closing",
            "Squeeze",
            "Welding",
            "Relief",
            "Oncomming"
        ]
        self._tesla_stages = [
            "Tesla squeeze",
            "Tesla closing",
            "Tesla welding",
            "Tesla oncomming_relief"
        ]
        self._ideal_data_cashe = LRUCache(maxsize=1000)
        self._OptAlgorithm_operator_params = [
            "dist_open_start_1", 
            "dist_open_start_2", 
            "dist_open_after_1", 
            "dist_open_after_2", 
            "dist_open_end_1", 
            "dist_open_end_2", 
            "dist_close_end_1", 
            "dist_close_end_2", 
            "time_command", 
            "time_robot_movement", 
            "object_thickness", 
            "force_target", 
            "force_capture",
            "time_wielding"]
        self._OptAlgorithm_system_params = [
            "a_max_1", 
            "v_max_1", 
            "a_max_2", 
            "v_max_2", 
            "mass_1", 
            "mass_2", 
            "k_hardness_1", 
            "k_hardness_2", 
            "torque_max_1", 
            "torque_max_2", 
            "transmission_ratio_1", 
            "transmission_ratio_2", 
            "position_start_1", 
            "position_start_2", 
            "k_prop", 
            "time_capture"]
    
    def form_passports(self, data: list[pd.DataFrame]) -> None:
        ...
    
    def form_customer_passport(self, data: list[pd.DataFrame, dict]) -> None:
        ...
    
    def update_settings(self, params: list) -> None:
        ...

    @property
    def opt_algorithm(self) -> BaseIdealDataBuilder:
        return self._opt_algorithm
    
    @opt_algorithm.setter
    def opt_algorithm(self, opt_algorithm: BaseIdealDataBuilder):
        self._opt_algorithm = opt_algorithm

    @property
    def mediator(self) -> BaseMediator:
        return self._mediator

    @mediator.setter
    def mediator(self, mediator: BaseMediator) -> None:
        self._mediator = mediator


class BaseRawTraceProcessor:

    def __init__(self, 
                 dataparser:BaseKukaDataParser, 
                 textparser:BaseKukaTextParser,
                 data_detector:BaseTraceStageDetector,
                 text_detector:BaseTextStageDetector,
                 mediator:Optional[BaseMediator] = None):
        self._mediator = mediator
        self._dataparser = dataparser
        self._textparser = textparser
        self._data_detector = data_detector
        self._text_detector = text_detector
        self._trace_df = None
        self._text_data = None
        
    def prerender(self, data:list[str]) -> None:
        ...
    
    def final_render(self) -> None:
        ...

    def update_settings(self, data:Settings) -> None:
        ...

    @property
    def mediator(self) -> BaseMediator:
        return self._mediator

    @mediator.setter
    def mediator(self, mediator: BaseMediator) -> None:
        self._mediator = mediator


class BaseKukaDataParser:

    def __init__(self):
        self._ch_name = None

    def parse(self, head_path: str) -> pd.DataFrame:
        ...


class BaseKukaTextParser:

    def __init__(self):
        self._in_msg = None
        self._datapacks = None

    def parse(self, path:str ) -> list[KukaTXT]:
        ...



class BaseTraceStageDetector:
    
    def __init__(self, parent:BaseRawTraceProcessor = None):
        self._parent = parent

    def detect_stages(self, df: pd.DataFrame) -> list:
        ...


class BaseTextStageDetector:

    def __init__(self, parent:BaseRawTraceProcessor = None):
        self._parent = parent
    
    def detect_welding(self, data:list[KukaTXT]) -> list:
        ...