#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Storm Engine Resource Checker
Проверяет что все используемые ресурсы существуют:
- Корабли (SHIP_*)
- Предметы (blade*, gun*, и т.д.)
- Локации (DoQuestReloadToLocation, ChangeCharacterAddressGroup)
- Диалоги (Dialog.FileName)

Использование:
  python lint-resources.py <путь_к_PROGRAM>                  # проверить все .c файлы
  python lint-resources.py <путь_к_PROGRAM> file1.c file2.c  # проверить конкретные файлы

Примеры:
  python lint-resources.py "C:/Games/SeaDogs/PROGRAM"
  python lint-resources.py ./PROGRAM quests/ChardLine_reaction.c
"""

import sys
import os
import re
from pathlib import Path

class Colors:
    RED = '\033[91m'
    YELLOW = '\033[93m'
    GREEN = '\033[92m'
    CYAN = '\033[96m'
    GRAY = '\033[90m'
    RESET = '\033[0m'

    @classmethod
    def disable(cls):
        cls.RED = cls.YELLOW = cls.GREEN = cls.CYAN = cls.GRAY = cls.RESET = ''

error_count = 0
warning_count = 0

# Кеш ресурсов
ships_cache = set()
items_cache = set()
locations_cache = set()
dialogs_cache = set()

def error(file_path, line_num, message):
    global error_count
    print(f"{Colors.RED}ERROR:{Colors.RESET} {Colors.YELLOW}{file_path}:{line_num}{Colors.RESET} {message}")
    error_count += 1

def warning(file_path, line_num, message):
    global warning_count
    print(f"{Colors.YELLOW}WARNING:{Colors.RESET} {Colors.GRAY}{file_path}:{line_num}{Colors.RESET} {message}")
    warning_count += 1

def load_ships(program_dir):
    """Загрузить список всех кораблей из ships/ships_init.c"""
    global ships_cache

    ships_file = os.path.join(program_dir, "ships", "ships_init.c")
    if not os.path.exists(ships_file):
        print(f"{Colors.YELLOW}ВНИМАНИЕ: ships/ships_init.c не найден{Colors.RESET}")
        return

    try:
        with open(ships_file, 'r', encoding='utf-8', errors='ignore') as f:
            content = f.read()

            ships = re.findall(r'SHIP_[A-Z_]+', content)
            ships_cache.update(ships)

            ships2 = re.findall(r'ShipsTypes\[([A-Z_]+)\]', content)
            ships_cache.update(ships2)

            print(f"{Colors.CYAN}  Загружено кораблей: {len(ships_cache)}{Colors.RESET}")
    except Exception as e:
        print(f"{Colors.RED}ОШИБКА загрузки кораблей: {e}{Colors.RESET}")

def load_items(program_dir):
    """Загрузить список всех предметов из ITEMS/initItems.c"""
    global items_cache

    items_file = os.path.join(program_dir, "ITEMS", "initItems.c")
    if not os.path.exists(items_file):
        print(f"{Colors.YELLOW}ВНИМАНИЕ: ITEMS/initItems.c не найден{Colors.RESET}")
        return

    try:
        with open(items_file, 'r', encoding='utf-8', errors='ignore') as f:
            content = f.read()

            items = re.findall(r'it\.id\s*=\s*"([^"]+)"', content)
            items_cache.update(items)

            items2 = re.findall(r'itm\.id\s*=\s*"([^"]+)"', content)
            items_cache.update(items2)

            print(f"{Colors.CYAN}  Загружено предметов: {len(items_cache)}{Colors.RESET}")
    except Exception as e:
        print(f"{Colors.RED}ОШИБКА загрузки предметов: {e}{Colors.RESET}")

def load_locations(program_dir):
    """Загрузить список всех локаций из locations/init/*.c"""
    global locations_cache

    locations_dir = os.path.join(program_dir, "locations", "init")
    if not os.path.exists(locations_dir):
        print(f"{Colors.YELLOW}ВНИМАНИЕ: locations/init/ не найден{Colors.RESET}")
        return

    try:
        for file_path in Path(locations_dir).glob("*.c"):
            with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                content = f.read()

                locs = re.findall(r'Locations\[n\]\.id\s*=\s*"([^"]+)"', content)
                locations_cache.update(locs)

                locs2 = re.findall(r'loc\.id\s*=\s*"([^"]+)"', content)
                locations_cache.update(locs2)

        print(f"{Colors.CYAN}  Загружено локаций: {len(locations_cache)}{Colors.RESET}")
    except Exception as e:
        print(f"{Colors.RED}ОШИБКА загрузки локаций: {e}{Colors.RESET}")

def load_dialogs(program_dir):
    """Загрузить список всех файлов диалогов"""
    global dialogs_cache

    dialogs_dir = os.path.join(program_dir, "dialogs", "russian")
    if not os.path.exists(dialogs_dir):
        print(f"{Colors.YELLOW}ВНИМАНИЕ: dialogs/russian/ не найден{Colors.RESET}")
        return

    try:
        for file_path in Path(dialogs_dir).rglob("*.c"):
            rel_path = str(file_path.relative_to(program_dir))
            rel_path = rel_path.replace(os.sep, '\\')
            dialogs_cache.add(rel_path)

        print(f"{Colors.CYAN}  Загружено диалогов: {len(dialogs_cache)}{Colors.RESET}")
    except Exception as e:
        print(f"{Colors.RED}ОШИБКА загрузки диалогов: {e}{Colors.RESET}")

def check_resources_in_file(file_path):
    """Проверить ресурсы в файле"""
    if not os.path.exists(file_path):
        print(f"{Colors.RED}ОШИБКА: Файл не найден: {file_path}{Colors.RESET}")
        return

    print(f"\n{Colors.CYAN}Проверка ресурсов: {file_path}{Colors.RESET}")

    try:
        with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
            lines = f.readlines()
    except Exception as e:
        print(f"{Colors.RED}ОШИБКА: Не удалось прочитать файл: {e}{Colors.RESET}")
        return

    for line_num, line in enumerate(lines, 1):
        stripped = line.strip()
        if stripped.startswith('//') or stripped.startswith('/*') or stripped.startswith('*'):
            continue

        # Проверка кораблей SHIP_*
        ships_used = re.findall(r'SHIP_([A-Z_]+)', line)
        for ship in ships_used:
            ship_full = f"SHIP_{ship}"
            if ship_full not in ships_cache and ship not in ships_cache:
                warning(file_path, line_num, f"Корабль '{ship_full}' не найден в ships_init.c")

        # Проверка предметов "blade*", "gun*" и т.д.
        items_used = re.findall(r'["\']([a-z][a-z0-9_]*)["\']', line)
        for item in items_used:
            if re.match(r'^(blade|gun|spyglass|jewelry|potion|letter|key|map|book|pistol)', item):
                if item not in items_cache:
                    warning(file_path, line_num, f"Предмет '{item}' не найден в initItems.c")

        # Проверка локаций DoQuestReloadToLocation("...", ...)
        locations_used = re.findall(r'DoQuestReloadToLocation\s*\(\s*"([^"]+)"', line)
        for loc in locations_used:
            if loc not in locations_cache:
                warning(file_path, line_num, f"Локация '{loc}' не найдена в locations/init/")

        # Проверка локаций ChangeCharacterAddressGroup(chr, "...", ...)
        locations_used2 = re.findall(r'ChangeCharacterAddressGroup\s*\([^,]+,\s*"([^"]+)"', line)
        for loc in locations_used2:
            if loc not in locations_cache:
                warning(file_path, line_num, f"Локация '{loc}' не найдена в locations/init/")

        # Проверка диалогов chr.Dialog.FileName = "..."
        dialogs_used = re.findall(r'Dialog\.FileName\s*=\s*"([^"]+)"', line)
        for dialog in dialogs_used:
            dialog_path = dialog.replace('/', '\\')
            dialog_full = f"dialogs\\russian\\{dialog_path}"

            if dialog_full not in dialogs_cache:
                error(file_path, line_num, f"Диалог '{dialog}' не найден в dialogs/russian/")

def main():
    if len(sys.argv) < 2 or '--help' in sys.argv or '-h' in sys.argv:
        print(__doc__)
        return 0

    # Разбор аргументов
    program_dir = None
    files_to_check = []
    no_color = False
    for arg in sys.argv[1:]:
        if arg == '--no-color':
            no_color = True
        elif program_dir is None:
            program_dir = arg
        else:
            files_to_check.append(arg)

    if no_color or not sys.stdout.isatty():
        Colors.disable()

    program_dir = os.path.abspath(program_dir)
    if not os.path.isdir(program_dir):
        print(f"ОШИБКА: Директория PROGRAM не найдена: {program_dir}")
        return 1

    print("")
    print(f"{Colors.CYAN}============================================{Colors.RESET}")
    print(f"{Colors.CYAN}  STORM ENGINE RESOURCE CHECKER{Colors.RESET}")
    print(f"{Colors.CYAN}============================================{Colors.RESET}")
    print("")

    # Загрузить все ресурсы
    print(f"{Colors.YELLOW}Загрузка ресурсов из: {program_dir}{Colors.RESET}")
    load_ships(program_dir)
    load_items(program_dir)
    load_locations(program_dir)
    load_dialogs(program_dir)
    print("")

    # Проверить файлы
    if files_to_check:
        for file_path in files_to_check:
            full_path = file_path
            if not os.path.isabs(file_path):
                full_path = os.path.join(program_dir, file_path)
            check_resources_in_file(full_path)
    else:
        # Проверить все .c файлы
        print(f"{Colors.YELLOW}Проверка всех .c файлов...{Colors.RESET}")
        c_files = sorted(Path(program_dir).rglob("*.c"))
        for fpath in c_files:
            check_resources_in_file(str(fpath))

    # РЕЗУЛЬТАТЫ
    print("")
    print(f"{Colors.CYAN}============================================{Colors.RESET}")
    print(f"{Colors.CYAN}  РЕЗУЛЬТАТЫ{Colors.RESET}")
    print(f"{Colors.CYAN}============================================{Colors.RESET}")

    if error_count == 0 and warning_count == 0:
        print(f"{Colors.GREEN}ПРОБЛЕМ НЕ НАЙДЕНО!{Colors.RESET}")
        return 0

    print(f"{Colors.RED}Ошибок: {error_count}{Colors.RESET}")
    print(f"{Colors.YELLOW}Предупреждений: {warning_count}{Colors.RESET}")

    if error_count > 0:
        print("")
        print(f"{Colors.RED}НАЙДЕНЫ КРИТИЧЕСКИЕ ОШИБКИ! Исправьте их перед запуском игры.{Colors.RESET}")
        return 1

    print("")
    print(f"{Colors.YELLOW}Найдены предупреждения. Рекомендуется проверить.{Colors.RESET}")
    return 0

if __name__ == "__main__":
    sys.exit(main())
