import pygame
import sys
import os
import re
import random

# Инициализация Pygame
pygame.init()

# Настройки окна
WIDTH, HEIGHT = 1280, 720
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Визуальная новелла")

# Цвета
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (200, 200, 200)
LIGHT_BLUE = (173, 216, 230)
DARK_BLUE = (0, 0, 139)
CHOICE_BG = (240, 240, 255)

# Шрифты
font = pygame.font.Font(None, 36)
character_font = pygame.font.Font(None, 32)
choice_font = pygame.font.Font(None, 30)
small_font = pygame.font.Font(None, 24)

class NovelEngine:
    def __init__(self):
        self.script = []
        self.current_line = 0
        self.running = True
        self.background = None
        self.characters = {}
        self.variables = {}
        self.choices = []
        self.waiting_for_choice = False
        self.call_stack = []  # Стек для хранения позиций возврата
        self.text_box_rect = pygame.Rect(50, HEIGHT - 250, WIDTH - 100, 200)
        self.choice_box_rect = pygame.Rect(WIDTH//2 - 300, HEIGHT//2 - 150, 600, 300)
        
    def load_script(self, filename):
        """Загрузка скрипта из файла"""
        self.script = []
        script_path = os.path.join(os.path.dirname(__file__), filename)
        
        if not os.path.exists(script_path):
            print(f"Ошибка: файл {script_path} не найден!")
            return False
            
        with open(script_path, 'r', encoding='utf-8') as file:
            for line in file:
                line = line.strip()
                if line and not line.startswith('#'):
                    self.script.append(line)
        return True
    
    def parse_character(self, command):
        """Обработка описания персонажа с дополнительными атрибутами"""
        if command.startswith("char "):
            parts = command[5:].split("|")
            char_id = parts[0].strip().split(":")[0]
            char_data = {
                "name": parts[0].split(":")[1].strip(),
                "appearance": parts[1].strip() if len(parts) > 1 else "",
                "details": parts[2].strip() if len(parts) > 2 else "",
                "scent": parts[3].strip() if len(parts) > 3 else ""
            }
            self.characters[char_id] = char_data
            return True
        return False
        
    def parse_command(self, command):
        """Обработка команд скрипта"""
        # Случайный переход в подпрограмму
        if command.startswith("random_gosub "):
            parts = command[12:].split()
            if len(parts) >= 2:
                probability = float(parts[0])
                if random.random() < probability:
                    # Сохраняем текущую позицию для возврата
                    self.call_stack.append(self.current_line + 1)
                    # Переходим к случайной подпрограмме
                    sub_label = random.choice(parts[1:])
                    self.jump_to_label(sub_label)
            return True
        
        # Возврат из подпрограммы
        elif command == "return":
            if self.call_stack:
                self.current_line = self.call_stack.pop()
            else:
                print("Ошибка: стек вызовов пуст!")
            return True
        
        # Установка переменной
        elif command.startswith("set "):
            parts = command[4:].split("|")
            for part in parts:
                if "=" in part:
                    var, val = part.split("=", 1)
                    self.variables[var.strip()] = val.strip()
            return True
        
        # Условие
        elif command.startswith("if "):
            match = re.match(r'if (\w+)([=!<>]+)(.+?) then goto (\w+)', command[3:])
            if match:
                var_name, op, value, label = match.groups()
                current_value = self.variables.get(var_name, "")
                
                condition_met = False
                if op == "==":
                    condition_met = str(current_value) == value
                elif op == "!=":
                    condition_met = str(current_value) != value
                elif op == ">":
                    condition_met = float(current_value) > float(value)
                elif op == "<":
                    condition_met = float(current_value) < float(value)
                
                if condition_met:
                    self.jump_to_label(label)
            return True
        
        # Метка
        elif command.startswith("label "):
            return True
        
        # Подпрограмма
        elif command.startswith("sub_"):
            return True
        
        # Переход
        elif command.startswith("goto "):
            label = command[5:].strip()
            self.jump_to_label(label)
            return True
        
        # Фон
        elif command.startswith("bg "):
            bg_color = command[3:].strip().lower()
            if bg_color == "black":
                self.background = BLACK
            elif bg_color == "white":
                self.background = WHITE
            elif bg_color == "blue":
                self.background = LIGHT_BLUE
            elif bg_color == "sunset":
                self.background = (255, 153, 51)  # Оранжевый для заката
            else:
                self.background = GRAY
            return True
        
        # Персонаж
        elif command.startswith("char "):
            return self.parse_character(command)
        
        return False
    
    def draw_descriptions(self, text, x, y, font_obj, color):
        """Отрисовка текста с учётом звёздочек"""
        if text.startswith("*") and text.endswith("*"):
            # Рисуем курсивом для описаний
            italic_font = pygame.font.Font(None, 32)
            italic_font.set_italic(True)
            self.draw_text(text[1:-1], x, y, italic_font, color)
        else:
            self.draw_text(text, x, y, font_obj, color)
    
    def jump_to_label(self, label):
        """Переход к метке в скрипте"""
        for i, line in enumerate(self.script):
            if line.startswith(f"@label {label}") or line.startswith(f"@{label}"):
                self.current_line = i
                return
        print(f"Метка '{label}' не найдена!")
    
    def process_choices(self, line):
        """Обработка строки с выбором"""
        if line.startswith("choice "):
            choices_text = line[7:].split("|")
            self.choices = []
            
            for choice_text in choices_text:
                parts = choice_text.split("=>")
                if len(parts) == 2:
                    choice_display = parts[0].strip()
                    choice_action = parts[1].strip()
                    self.choices.append((choice_display, choice_action))
            
            self.waiting_for_choice = True
            return True
        return False
    
    def substitute_variables(self, text):
        """Подстановка переменных в текст"""
        for var_name, var_value in self.variables.items():
            text = text.replace(f"{{{var_name}}}", str(var_value))
        return text
    
    def get_character_info(self, char_id):
        """Возвращает полное описание персонажа"""
        if char_id not in self.characters:
            return f"Персонаж {char_id}", ""
        
        char = self.characters.get(char_id)
        if isinstance(char, str):
            return char, ""
        
        details = []
        if char["appearance"]: details.append(char['appearance'])
        if char["details"]: details.append(char['details'])
        if char["scent"]: details.append(char['scent'])
        
        return char["name"], " | ".join(details)
    
    def process_line(self):
        if self.current_line >= len(self.script):
            self.running = False
            return None
            
        line = self.script[self.current_line]
        line = self.substitute_variables(line)
        
        # Обработка команд
        if line.startswith("@"):
            if self.parse_command(line[1:]):
                self.current_line += 1
                return self.process_line()
            return None
        
        # Обработка выбора
        if self.process_choices(line):
            self.current_line += 1
            return None
            
        # Обработка диалога с проверкой разделителя
        if ":" in line and not line.startswith("*"):
            parts = line.split(":", 1)
            if len(parts) == 2:
                char_id = parts[0].strip()
                text = parts[1].strip()
                return char_id, text
        
        # Обработка описания или простого текста
        return None, line
    
    def make_choice(self, choice_index):
        """Обработка выбора игрока"""
        if 0 <= choice_index < len(self.choices):
            choice_action = self.choices[choice_index][1]
            
            if choice_action.startswith("goto "):
                label = choice_action[5:].strip()
                self.jump_to_label(label)
            elif "+=" in choice_action:
                var, value = choice_action.split("+=", 1)
                var = var.strip()
                self.variables[var] = str(int(self.variables.get(var, 0)) + int(value.strip()))
                self.current_line += 1
            elif "-=" in choice_action:
                var, value = choice_action.split("-=", 1)
                var = var.strip()
                self.variables[var] = str(int(self.variables.get(var, 0)) - int(value.strip()))
                self.current_line += 1
            elif "=" in choice_action:
                var, value = choice_action.split("=", 1)
                self.variables[var.strip()] = value.strip()
                self.current_line += 1
            
            self.choices = []
            self.waiting_for_choice = False
    
    def next_line(self):
        """Переход к следующей строке"""
        self.current_line += 1
        if self.current_line >= len(self.script):
            self.running = False
    
    def draw_text_box(self):
        """Отрисовка текстового поля"""
        pygame.draw.rect(screen, WHITE, self.text_box_rect)
        pygame.draw.rect(screen, DARK_BLUE, self.text_box_rect, 3)
    
    def draw_choice_box(self):
        """Отрисовка поля выбора"""
        pygame.draw.rect(screen, CHOICE_BG, self.choice_box_rect)
        pygame.draw.rect(screen, DARK_BLUE, self.choice_box_rect, 3)
    
    def draw_text(self, text, x, y, font_obj, color=BLACK, max_width=None):
        """Отрисовка текста с переносом строк"""
        if max_width is None:
            max_width = self.text_box_rect.width - 40
        
        words = text.split(' ')
        space_width = font_obj.size(' ')[0]
        line_height = font_obj.get_height()
        
        current_line = []
        current_width = 0
        
        for word in words:
            word_surface = font_obj.render(word, True, color)
            word_width = word_surface.get_width()
            
            if current_width + word_width <= max_width:
                current_line.append(word)
                current_width += word_width + space_width
            else:
                line_text = ' '.join(current_line)
                line_surface = font_obj.render(line_text, True, color)
                screen.blit(line_surface, (x, y))
                y += line_height
                current_line = [word]
                current_width = word_width + space_width
        
        if current_line:
            line_text = ' '.join(current_line)
            line_surface = font_obj.render(line_text, True, color)
            screen.blit(line_surface, (x, y))
    
    def draw(self):
        """Отрисовка текущего состояния"""
        # Фон
        bg_color = self.background if self.background else LIGHT_BLUE
        screen.fill(bg_color)
        
        # Текстовое поле
        self.draw_text_box()
        
        # Текст
        line_data = self.process_line()
        x = self.text_box_rect.x + 20
        y = self.text_box_rect.y + 20
        
        if line_data and not self.waiting_for_choice:
            if isinstance(line_data, tuple):  # Диалог с персонажем
                char_id, text = line_data
                name, details = self.get_character_info(char_id)
                
                # Отрисовка имени персонажа
                name_surface = character_font.render(name + ":", True, DARK_BLUE)
                screen.blit(name_surface, (x, y))
                y += 40
                
                # Отрисовка текста персонажа
                self.draw_text(text, x, y, font, BLACK)
                y += 60
                
                # Отрисовка деталей персонажа (если есть)
                if details:
                    details_surface = small_font.render(details, True, (100, 100, 100))
                    screen.blit(details_surface, (x, y))
            else:  # Описание или простой текст
                _, text = line_data
                self.draw_descriptions(text, x, y, font, BLACK)

        # Варианты выбора
        if self.waiting_for_choice and self.choices:
            self.draw_choice_box()
            choice_y = self.choice_box_rect.y + 30
            for i, (choice_text, _) in enumerate(self.choices):
                choice_surface = choice_font.render(f"{i+1}. {choice_text}", True, BLACK)
                screen.blit(choice_surface, (self.choice_box_rect.x + 30, choice_y))
                choice_y += 40
        
        # Индикатор продолжения
        if self.running and not self.waiting_for_choice and line_data:
            indicator = font.render("> Нажмите ПРОБЕЛ", True, DARK_BLUE)
            screen.blit(indicator, (WIDTH - 200, HEIGHT - 50))
        
        pygame.display.flip()
    
    def run(self):
        """Основной цикл"""
        clock = pygame.time.Clock()
        
        while self.running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_SPACE and not self.waiting_for_choice:
                        self.next_line()
                    elif event.key == pygame.K_1 and self.waiting_for_choice and len(self.choices) >= 1:
                        self.make_choice(0)
                    elif event.key == pygame.K_2 and self.waiting_for_choice and len(self.choices) >= 2:
                        self.make_choice(1)
                    elif event.key == pygame.K_3 and self.waiting_for_choice and len(self.choices) >= 3:
                        self.make_choice(2)
                    elif event.key == pygame.K_4 and self.waiting_for_choice and len(self.choices) >= 4:
                        self.make_choice(3)
                    elif event.key == pygame.K_ESCAPE:
                        self.running = False
            
            self.draw()
            clock.tick(60)

# Создаем экземпляр движка и запускаем
if __name__ == "__main__":
    engine = NovelEngine()
    
    if engine.load_script("island_script.txt"):
        engine.run()
    
    pygame.quit()
    sys.exit()