import os
import re
import zlib
import tkinter as tk
from tkinter import ttk, filedialog, messagebox

def decode_pdf_string(raw):
    result = bytearray()
    i = 0
    while i < len(raw):
        b = raw[i]
        if b == 0x5C:
            i += 1
            if i >= len(raw):
                break
            c = raw[i]
            if c == 0x6E:
                result.append(0x0A)
            elif c == 0x72:
                result.append(0x0D)
            elif c == 0x74:
                result.append(0x09)
            elif c == 0x62:
                result.append(0x08)
            elif c == 0x66:
                result.append(0x0C)
            elif c == 0x28 or c == 0x29 or c == 0x5C:
                result.append(c)
            elif 0x30 <= c <= 0x37:
                oct_digits = [c]
                for _ in range(2):
                    if i + 1 < len(raw) and 0x30 <= raw[i + 1] <= 0x37:
                        i += 1
                        oct_digits.append(raw[i])
                    else:
                        break
                result.append(int(bytes(oct_digits), 8))
            else:
                result.append(c)
        else:
            result.append(b)
        i += 1
    for enc in ('utf-8', 'cp1251', 'latin-1'):
        try:
            return result.decode(enc)
        except UnicodeDecodeError:
            continue
    return result.decode('latin-1', errors='replace')

def extract_text_from_pdf(path):
    try:
        with open(path, 'rb') as f:
            data = f.read()
    except Exception as e:
        raise RuntimeError(f"Не удалось прочитать файл: {e}")
    texts = []
    stream_pattern = re.compile(rb'stream\r?\n(.*?)\r?\nendstream', re.DOTALL)
    string_pattern = re.compile(rb'\(((?:\\.|[^\\()])*)\)')
    hex_string_pattern = re.compile(rb'\<([0-9A-Fa-f]+)\>')
    for m in stream_pattern.finditer(data):
        content = m.group(1)
        try:
            content = zlib.decompress(content)
        except zlib.error:
            pass
        for sm in string_pattern.finditer(content):
            raw = sm.group(1)
            decoded = decode_pdf_string(raw)
            if decoded.strip():
                texts.append(decoded)
        for hm in hex_string_pattern.finditer(content):
            hex_bytes = bytes.fromhex(hm.group(1).decode('ascii', errors='ignore'))
            decoded = decode_pdf_string(hex_bytes)
            if decoded.strip():
                texts.append(decoded)
    return '\n'.join(texts)

class PDFRenamerApp:
    def __init__(self, root):
        self.root = root
        root.title("Переименование PDF по внутреннему имени")
        root.geometry("600x250")
        root.resizable(True, True)

        self.file_path = tk.StringVar()
        self.regex_pattern = tk.StringVar(value=r"[А-ЯЁ][а-яё]+\s+[А-ЯЁ][а-яё]+\s+[А-ЯЁ][а-яё]+")
        self.extracted_name = tk.StringVar()
        self.new_name = tk.StringVar()

        main_frame = ttk.Frame(root, padding="10")
        main_frame.grid(row=0, column=0, sticky="nsew")
        root.columnconfigure(0, weight=1)
        root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)

        ttk.Label(main_frame, text="Файл PDF:").grid(row=0, column=0, sticky=tk.W, pady=2)
        ttk.Entry(main_frame, textvariable=self.file_path, state="readonly").grid(row=0, column=1, sticky="we", padx=5, pady=2)
        ttk.Button(main_frame, text="Выбрать...", command=self.select_file).grid(row=0, column=2, padx=5, pady=2)

        ttk.Label(main_frame, text="Регулярное выражение:").grid(row=1, column=0, sticky=tk.W, pady=2)
        ttk.Entry(main_frame, textvariable=self.regex_pattern).grid(row=1, column=1, sticky="we", padx=5, pady=2)

        ttk.Button(main_frame, text="Извлечь имя", command=self.extract_name).grid(row=2, column=1, pady=5)

        ttk.Label(main_frame, text="Извлечённое имя:").grid(row=3, column=0, sticky=tk.W, pady=2)
        ttk.Entry(main_frame, textvariable=self.extracted_name, state="readonly").grid(row=3, column=1, sticky="we", padx=5, pady=2)

        ttk.Label(main_frame, text="Новое имя файла:").grid(row=4, column=0, sticky=tk.W, pady=2)
        ttk.Entry(main_frame, textvariable=self.new_name).grid(row=4, column=1, sticky="we", padx=5, pady=2)

        ttk.Button(main_frame, text="Переименовать", command=self.rename_file).grid(row=5, column=1, pady=10)

        for child in main_frame.winfo_children():
            child.grid_configure(padx=3, pady=3)

    def select_file(self):
        path = filedialog.askopenfilename(
            title="Выберите PDF файл",
            filetypes=[("PDF files", "*.pdf"), ("All files", "*.*")]
        )
        if path:
            self.file_path.set(path)
            self.extracted_name.set("")
            self.new_name.set("")

    def extract_name(self):
        path = self.file_path.get()
        if not path:
            messagebox.showerror("Ошибка", "Сначала выберите PDF файл.")
            return
        try:
            text = extract_text_from_pdf(path)
        except Exception as e:
            messagebox.showerror("Ошибка", f"Не удалось извлечь текст из PDF:\n{e}")
            return
        if not text:
            messagebox.showerror("Ошибка", "Не удалось найти текст в PDF файле. Возможно, он не содержит текстового слоя.")
            return
        pattern = self.regex_pattern.get()
        try:
            match = re.search(pattern, text)
        except re.error as e:
            messagebox.showerror("Ошибка", f"Неверное регулярное выражение:\n{e}")
            return
        if not match:
            messagebox.showerror("Ошибка", "Имя не найдено по заданному шаблону.")
            return
        if match.groups():
            extracted = match.group(1)
        else:
            extracted = match.group(0)
        extracted = extracted.strip()
        if not extracted:
            messagebox.showerror("Ошибка", "Извлечённое имя пустое.")
            return
        self.extracted_name.set(extracted)
        self.new_name.set(extracted + ".pdf")

    def rename_file(self):
        old_path = self.file_path.get()
        new_name = self.new_name.get().strip()
        if not old_path:
            messagebox.showerror("Ошибка", "Сначала выберите PDF файл.")
            return
        if not new_name:
            messagebox.showerror("Ошибка", "Введите новое имя файла.")
            return
        invalid_chars = r'\/:*?"<>|'
        if any(c in new_name for c in invalid_chars):
            messagebox.showerror("Ошибка", "Новое имя содержит недопустимые символы.")
            return
        if not new_name.lower().endswith(".pdf"):
            new_name += ".pdf"
        new_path = os.path.join(os.path.dirname(old_path), new_name)
        if os.path.abspath(new_path) == os.path.abspath(old_path):
            messagebox.showinfo("Информация", "Имя файла не изменилось.")
            return
        if os.path.exists(new_path):
            messagebox.showerror("Ошибка", f"Файл с именем '{new_name}' уже существует.")
            return
        try:
            os.rename(old_path, new_path)
            self.file_path.set(new_path)
            messagebox.showinfo("Успех", f"Файл переименован в:\n{new_path}")
        except Exception as e:
            messagebox.showerror("Ошибка", f"Не удалось переименовать файл:\n{e}")

if __name__ == "__main__":
    root = tk.Tk()
    app = PDFRenamerApp(root)
    root.mainloop()