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


class PDFRenameApp:
    def __init__(self, root):
        self.root = root
        self.root.title("PDF Rename Tool")
        self.root.geometry("800x600")

        self.filepath = tk.StringVar()
        self.new_filename = tk.StringVar()

        top_frame = ttk.Frame(root, padding=10)
        top_frame.pack(fill=tk.X)

        ttk.Button(top_frame, text="Open PDF", command=self.open_pdf).pack(side=tk.LEFT)
        ttk.Label(top_frame, textvariable=self.filepath, anchor=tk.W).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=10)

        text_frame = ttk.Frame(root, padding=10)
        text_frame.pack(fill=tk.BOTH, expand=True)

        self.text_area = tk.Text(text_frame, wrap=tk.WORD, state=tk.DISABLED)
        scrollbar = ttk.Scrollbar(text_frame, orient=tk.VERTICAL, command=self.text_area.yview)
        self.text_area.configure(yscrollcommand=scrollbar.set)
        self.text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        bottom_frame = ttk.Frame(root, padding=10)
        bottom_frame.pack(fill=tk.X)

        ttk.Label(bottom_frame, text="New filename:").pack(side=tk.LEFT)
        entry = ttk.Entry(bottom_frame, textvariable=self.new_filename, width=60)
        entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        ttk.Button(bottom_frame, text="Rename File", command=self.rename_file).pack(side=tk.LEFT)

    def open_pdf(self):
        path = filedialog.askopenfilename(
            title="Select PDF file",
            filetypes=[("PDF files", "*.pdf"), ("All files", "*.*")]
        )
        if not path:
            return
        self.filepath.set(path)
        try:
            text = extract_text_from_pdf(path)
        except Exception as e:
            messagebox.showerror("Error", f"Failed to extract text: {str(e)}")
            return
        self.display_text(text)
        first_line = next((line.strip() for line in text.splitlines() if line.strip()), "")
        base_name = os.path.splitext(os.path.basename(path))[0]
        suggested = sanitize_filename(first_line) if first_line else base_name
        if suggested:
            suggested += ".pdf"
        else:
            suggested = base_name + ".pdf"
        self.new_filename.set(suggested)

    def display_text(self, text):
        self.text_area.config(state=tk.NORMAL)
        self.text_area.delete(1.0, tk.END)
        self.text_area.insert(tk.END, text)
        self.text_area.config(state=tk.DISABLED)

    def rename_file(self):
        old_path = self.filepath.get()
        if not old_path or not os.path.isfile(old_path):
            messagebox.showerror("Error", "No source file selected.")
            return
        new_name = self.new_filename.get().strip()
        if not new_name:
            messagebox.showerror("Error", "New filename cannot be empty.")
            return
        new_name = sanitize_filename(new_name)
        if not new_name.lower().endswith(".pdf"):
            new_name += ".pdf"
        dir_name = os.path.dirname(old_path)
        new_path = os.path.join(dir_name, new_name)
        if new_path == old_path:
            messagebox.showinfo("Info", "New name is same as old name.")
            return
        if os.path.exists(new_path):
            messagebox.showerror("Error", f"File '{new_name}' already exists.")
            return
        try:
            os.rename(old_path, new_path)
            self.filepath.set(new_path)
            messagebox.showinfo("Success", f"File renamed to:\n{new_path}")
        except Exception as e:
            messagebox.showerror("Error", f"Rename failed: {str(e)}")


def sanitize_filename(name):
    forbidden = r'\/:*?"<>|'
    cleaned = ''.join(c for c in name if c not in forbidden)
    cleaned = cleaned.strip().rstrip('.')
    return cleaned


def extract_text_from_pdf(filepath):
    with open(filepath, 'rb') as f:
        data = f.read()

    text_chunks = []

    stream_pattern = re.compile(rb'stream\r?\n(.*?)endstream', re.DOTALL)
    obj_pattern = re.compile(rb'(\d+)\s+(\d+)\s+obj(.*?)stream', re.DOTALL)

    obj_dicts = {}
    for match in obj_pattern.finditer(data):
        obj_num = match.group(1)
        dict_part = match.group(3)
        obj_dicts[obj_num] = dict_part

    for match in stream_pattern.finditer(data):
        stream_data = match.group(1)
        start = match.start()
        preceding = data[max(0, start-200):start]
        if b'/FlateDecode' in preceding or b'/Fl' in preceding:
            try:
                stream_data = zlib.decompress(stream_data)
            except zlib.error:
                pass
        text_chunks.extend(extract_strings_from_stream(stream_data))

    text = '\n'.join(text_chunks)
    return text if text else "(No extractable text found)"


def extract_strings_from_stream(stream_data):
    strings = []
    tj_pattern = re.compile(rb'\(((?:\\.|[^\\)])*)\)\s*Tj', re.DOTALL)
    for m in tj_pattern.finditer(stream_data):
        s = m.group(1)
        strings.append(decode_pdf_string(s))

    tj_array_pattern = re.compile(rb'\[((?:\\.|[^\]])*)\]\s*TJ', re.DOTALL)
    for m in tj_array_pattern.finditer(stream_data):
        array_content = m.group(1)
        for sm in re.finditer(rb'\(((?:\\.|[^\\)])*)\)', array_content):
            strings.append(decode_pdf_string(sm.group(1)))

    return strings


def decode_pdf_string(raw):
    raw = raw.replace(b'\\(', b'(').replace(b'\\)', b')').replace(b'\\\\', b'\\')
    if raw.startswith(b'\xfe\xff'):
        return raw[2:].decode('utf-16-be', errors='replace')
    elif raw.startswith(b'\xff\xfe'):
        return raw[2:].decode('utf-16-le', errors='replace')
    else:
        try:
            return raw.decode('latin-1')
        except UnicodeDecodeError:
            return raw.decode('utf-8', errors='replace')


if __name__ == '__main__':
    root = tk.Tk()
    app = PDFRenameApp(root)
    root.mainloop()