from builders import TextFileMazeBuilder
from strategies import BFSStrategy, DFSStrategy, AStarStrategy
from solver import MazeSolver
from observers import ConsoleView
from experiments import run_experiments, save_to_csv
import random

WALL = '■'
PASSAGE = ' '
START = 'S'
EXIT = 'E'


def generate_maze_prim(size: int, filename: str, label: str):
    random.seed(hash(filename) % 10000)
    
    grid = [[WALL for _ in range(size)] for _ in range(size)]
    
    start_x, start_y = 1, 1
    grid[start_y][start_x] = PASSAGE
    
    walls = []
    
    for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]:
        wall_x = start_x + dx
        wall_y = start_y + dy
        cell_x = start_x + 2*dx
        cell_y = start_y + 2*dy
        
        if 0 <= cell_x < size and 0 <= cell_y < size:
            if grid[wall_y][wall_x] == WALL:
                walls.append((wall_x, wall_y, start_x, start_y, cell_x, cell_y))
    
    while walls:
        idx = random.randint(0, len(walls) - 1)
        wall_x, wall_y, from_x, from_y, to_x, to_y = walls.pop(idx)
        
        if grid[to_y][to_x] == WALL:
            grid[wall_y][wall_x] = PASSAGE
            grid[to_y][to_x] = PASSAGE
            
            for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]:
                new_wall_x = to_x + dx
                new_wall_y = to_y + dy
                new_cell_x = to_x + 2*dx
                new_cell_y = to_y + 2*dy
                
                if 0 <= new_cell_x < size and 0 <= new_cell_y < size:
                    if grid[new_wall_y][new_wall_x] == WALL:
                        walls.append((new_wall_x, new_wall_y, to_x, to_y, new_cell_x, new_cell_y))
    
    grid[1][1] = START
    grid[size-2][size-2] = EXIT
    
    with open(filename, 'w', encoding='utf-8') as f:
        for row in grid:
            f.write(''.join(row) + '\n')
    
    print(f"✓ {label}: {filename}")


def generate_small_maze(filename='small_maze.txt'):
    maze = [
        "■■■■■■■■■■",
        "■S       ■",
        "■ ■■■■ ■ ■",
        "■ ■  ■ ■ ■",
        "■ ■ ■■ ■ ■",
        "■ ■    ■ ■",
        "■ ■■■■■■ ■",
        "■        ■",
        "■ ■■■■■■■■",
        "■       E■",
        "■■■■■■■■■■"
    ]
    
    with open(filename, 'w', encoding='utf-8') as f:
        f.write('\n'.join(maze))
    print(f"Маленький лабиринт 10x10: {filename}")


def generate_no_exit_maze(filename='no_exit_maze.txt'):
    maze = [
        "■■■■■■■■■■",
        "■S       ■",
        "■ ■■■■ ■ ■",
        "■ ■  ■ ■ ■",
        "■ ■ ■■ ■ ■",
        "■ ■    ■ ■",
        "■ ■■■■■■ ■",
        "■        ■",
        "■ ■■■■■■■■",
        "■   ■■■■■■",
        "■■■■■E■■■■"
    ]
    
    with open(filename, 'w', encoding='utf-8') as f:
        f.write('\n'.join(maze))
    print(f"Лабиринт без выхода 10x10: {filename}")


def generate_empty_maze(filename='empty_maze.txt'):
    size = 10
    maze = []
    maze.append(WALL * size)
    
    for i in range(size - 2):
        if i == 0:
            row = WALL + START + PASSAGE * (size - 3) + WALL
        elif i == size - 3:
            row = WALL + PASSAGE * (size - 3) + EXIT + WALL
        else:
            row = WALL + PASSAGE * (size - 2) + WALL
        maze.append(row)
    
    maze.append(WALL * size)
    
    with open(filename, 'w', encoding='utf-8') as f:
        f.write('\n'.join(maze))
    print(f"Пустой лабиринт 10x10: {filename}")


def main():
    print("=" * 60)
    print("ГЕНЕРАЦИЯ ЛАБИРИНТОВ")
    print("=" * 60)
    
    generate_small_maze('small_maze.txt')
    generate_maze_prim(50, 'medium_maze.txt', 'Средний лабиринт 50x50')
    generate_maze_prim(100, 'large_maze.txt', 'Большой лабиринт 100x100')
    generate_no_exit_maze('no_exit_maze.txt')
    generate_empty_maze('empty_maze.txt')
    
    print("\n" + "=" * 60)
    print("ДЕМОНСТРАЦИЯ НА МАЛЕНЬКОМ ЛАБИРИНТЕ")
    print("=" * 60)
    
    builder = TextFileMazeBuilder()
    maze = builder.buildFromFile('small_maze.txt')
    
    print(f"Размер: {maze.width}x{maze.height}")
    print(f"Старт: ({maze.start.x}, {maze.start.y})")
    print(f"Выход: ({maze.exit.x}, {maze.exit.y})")
    
    view = ConsoleView()
    solver = MazeSolver(maze)
    solver.attach(view)
    
    strategies = {
        "BFS (поиск в ширину)": BFSStrategy(),
        "DFS (поиск в глубину)": DFSStrategy(),
        "A*": AStarStrategy()
    }
    
    for name, strat in strategies.items():
        print(f"\n{'─' * 40}")
        print(f"Стратегия: {name}")
        
        solver.setStrategy(strat)
        path, stats = solver.solve()
        
        if path:
            print(f"Путь найден! Длина: {len(path)} шагов")
            print(f"  Время: {stats.time_ms:.3f} мс | Посещено: {stats.visited_count}")
            view.render(maze, path=path)
        else:
            print("Путь не найден!")
    
    # Эксперименты
    print("\n" + "=" * 60)
    print("ЭКСПЕРИМЕНТЫ НА ВСЕХ ЛАБИРИНТАХ")
    print("=" * 60)
    
    test_mazes = {}
    maze_files = [
        ("Маленький (10x10)", "small_maze.txt"),
        ("Средний (50x50)", "medium_maze.txt"),
        ("Большой (100x100)", "large_maze.txt"),
        ("Без выхода (10x10)", "no_exit_maze.txt"),
        ("Пустой (10x10)", "empty_maze.txt")
    ]
    
    for name, filename in maze_files:
        try:
            test_mazes[name] = builder.buildFromFile(filename)
            m = test_mazes[name]
            print(f"{name} загружен ({m.width}x{m.height})")
        except Exception as e:
            print(f"Ошибка {name}: {e}")
    
    if test_mazes:
        print(f"\nЗапуск тестов (по 3 прогона)...")
        results = run_experiments(test_mazes, strategies, runs=3)
        save_to_csv(results)
        
        print("ГОТОВО! Графики: python visualize_results.py")



if __name__ == "__main__":
    main()
