from task2.mazeObjects.maze import Maze
from task2.mazeObjects.cell import Cell
from task2.observerSubject import MazeEvent, MazeEventType, Subject
from task2.strategyObjects.pathFindingStrategy import PathFindingStrategy
from task2.strategyObjects.BFS import BFS


import time

class SearchStats:
    maze_name:str = "None"
    """Время выполнения в миллисекундах, количество посещённых клеток, длина найденного пути"""
    def __init__(self, path: list[Cell]|None, duration:float, visited_cells:int, path_len:int, strategy_name:str):
        self.duration = duration
        self.visited_cells = visited_cells
        self.path_len = path_len
        self.path = path
        self.strategy_name = strategy_name
    
    def toDict(self,):
        return {
            "strategy_name" : self.strategy_name,
            "maze_name" : self.maze_name,
            "duration" : self.duration,
            "visited_cells" : self.visited_cells,
            "path_len" : self.path_len
        }

class MazeSolver(Subject):
    """
    MazeSolver содержит поля maze и strategy.
    Метод setStrategy(strategy) для динамической смены алгоритма.
    Метод solve() вызывает strategy.findPath(...) и возвращает объект SearchStats (время выполнения в миллисекундах,
    количество посещённых клеток, длина найденного пути).
    Для замера времени используйте time.perf_counter() до и после вызова стратегии.
    """

    def __init__(self, strategy:PathFindingStrategy, maze:Maze|None=None):
        super().__init__()
        self._maze = maze
        self.strategy = strategy

    def setMaze(self, maze: Maze|None):
        self._maze = maze
        self.notify(MazeEvent(MazeEventType.MAZE_LOADED, data=maze))

    def setStrategy(self, strategy:PathFindingStrategy):
        self.strategy = strategy

    def getStrategyName(self):
        return self.strategy.__class__.__name__

    def solve(self):
        if not self._maze:
            raise ValueError
        
        t_start = time.perf_counter()
        path = self.strategy.findPath(self._maze, self._maze.startCell, self._maze.endCell)
        duration = (time.perf_counter() - t_start) * 1000

        path_len = len(path.array) if path.array else -1
        strategy_name = self.getStrategyName()

        stats = SearchStats(path.array, duration, path.visited_cells, path_len, strategy_name)
        self.notify(MazeEvent(MazeEventType.PATH_FOUND, data=path))
        return stats