import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from pathlib import Path
import json
from PIL import Image, ImageTk
from PIL.PngImagePlugin import PngInfo
import platform
import subprocess

# Application metadata
APP_VERSION = "2.3.0"
APP_NAME = "PNG Metadata Editor"

class PNGMetadataEditor:
    def __init__(self, root):
        self.root = root
        self.root.title(f"{APP_NAME} v{APP_VERSION}")
        self.root.geometry("1000x700")  # Smaller window size
        self.current_file = None
        self.current_directory = None
        self.metadata_dict = {}
        self.has_unsaved_changes = False
        self.status_timer = None
        self.thumbnail_images = {}  # Keep references to PhotoImage objects
        self.thumbnail_frames = {}  # Track thumbnail frames for highlighting

        self.setup_ui()

        # Bring window to foreground on startup
        self.root.lift()
        self.root.attributes('-topmost', True)
        self.root.after_idle(self.root.attributes, '-topmost', False)
        self.root.focus_force()

    def detect_dark_mode(self):
        """Detect if the OS is in dark mode"""
        system = platform.system()

        if system == "Darwin":  # macOS
            try:
                result = subprocess.run(
                    ['defaults', 'read', '-g', 'AppleInterfaceStyle'],
                    capture_output=True,
                    text=True,
                    timeout=1
                )
                return result.returncode == 0 and 'Dark' in result.stdout
            except:
                return False
        elif system == "Windows":
            try:
                import winreg
                registry = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER)
                key = winreg.OpenKey(registry, r'SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize')
                value, _ = winreg.QueryValueEx(key, 'AppsUseLightTheme')
                return value == 0
            except:
                return False
        else:  # Linux
            try:
                result = subprocess.run(
                    ['gsettings', 'get', 'org.gnome.desktop.interface', 'gtk-theme'],
                    capture_output=True,
                    text=True,
                    timeout=1
                )
                return 'dark' in result.stdout.lower()
            except:
                return False

    def get_theme_colors(self):
        """Get background colors based on dark mode detection"""
        is_dark = self.detect_dark_mode()
        if is_dark:
            return {
                'canvas_bg': '#2b2b2b',
                'frame_bg': '#2b2b2b',
                'text_fg': '#ffffff',
                'loading_fg': '#6bb6ff'
            }
        else:
            return {
                'canvas_bg': '#f0f0f0',
                'frame_bg': '#f0f0f0',
                'text_fg': '#000000',
                'loading_fg': '#4a90d9'
            }

    def setup_ui(self):
        # Initialize theme colors
        self.theme_colors = self.get_theme_colors()
        
        # Top frame for file operations
        top_frame = ttk.Frame(self.root, padding="10")
        top_frame.pack(fill=tk.X)

        ttk.Button(top_frame, text="📄 Open PNG File", command=self.open_file).pack(side=tk.LEFT, padx=5)
        ttk.Button(top_frame, text="📥 Browse Directory", command=self.browse_directory).pack(side=tk.LEFT, padx=5)
        ttk.Button(top_frame, text="✅ Save Changes", command=self.save_changes).pack(side=tk.LEFT, padx=5)
        ttk.Button(top_frame, text="ℹ️ About", command=self.show_about).pack(side=tk.RIGHT, padx=5)

        self.file_label = ttk.Label(top_frame, text="No file loaded", foreground="gray")
        self.file_label.pack(side=tk.LEFT, padx=20)

        # Unsaved changes indicator
        self.changes_label = ttk.Label(top_frame, text="", foreground="orange")
        self.changes_label.pack(side=tk.LEFT, padx=10)

        # Main container with horizontal split
        self.main_paned = ttk.PanedWindow(self.root, orient=tk.HORIZONTAL)
        self.main_paned.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)

        # Left side - Thumbnail browser (smaller default width)
        thumbnail_frame = ttk.Frame(self.main_paned, relief=tk.SUNKEN, borderwidth=2)
        self.main_paned.add(thumbnail_frame, weight=0)

        ttk.Label(thumbnail_frame, text="Image Browser", font=("TkDefaultFont", 10, "bold")).pack(anchor=tk.W, padx=5, pady=5)

        # Canvas with scrollbar for thumbnails
        thumb_canvas_frame = ttk.Frame(thumbnail_frame)
        thumb_canvas_frame.pack(fill=tk.BOTH, expand=True)

        thumb_scroll = ttk.Scrollbar(thumb_canvas_frame, orient=tk.VERTICAL)
        thumb_scroll.pack(side=tk.RIGHT, fill=tk.Y)

        self.thumb_canvas = tk.Canvas(thumb_canvas_frame, yscrollcommand=thumb_scroll.set, bg=self.theme_colors["canvas_bg"], width=190)
        self.thumb_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        thumb_scroll.config(command=self.thumb_canvas.yview)

        # Frame inside canvas to hold thumbnails
        self.thumb_container = ttk.Frame(self.thumb_canvas)
        self.thumb_canvas_window = self.thumb_canvas.create_window((0, 0), window=self.thumb_container, anchor=tk.NW)

        # Bind canvas resize
        self.thumb_container.bind("<Configure>", self.on_thumb_frame_configure)
        self.thumb_canvas.bind("<Configure>", self.on_thumb_canvas_configure)

        # Bind trackpad/mousewheel scrolling
        self.bind_mousewheel(self.thumb_canvas)

        # Right side - Metadata editor (existing UI)
        editor_frame = ttk.Frame(self.main_paned)
        self.main_paned.add(editor_frame, weight=1)

        # Set the sash position after a short delay to ensure proper sizing
        self.root.after(100, lambda: self.main_paned.sashpos(0, 200))

        # PanedWindow for splitting tree and detail view
        paned = ttk.PanedWindow(editor_frame, orient=tk.VERTICAL)
        paned.pack(fill=tk.BOTH, expand=True)

        # Top pane - Treeview (1/3 of height)
        tree_frame = ttk.Frame(paned)
        paned.add(tree_frame, weight=1)  # Changed from weight=2 to weight=1

        # Treeview with scrollbar
        tree_scroll = ttk.Scrollbar(tree_frame)
        tree_scroll.pack(side=tk.RIGHT, fill=tk.Y)

        self.tree = ttk.Treeview(tree_frame, columns=("Key", "Value"), show="headings",
                                 yscrollcommand=tree_scroll.set)
        self.tree.heading("Key", text="Key")
        self.tree.heading("Value", text="Value Preview")
        self.tree.column("Key", width=150)
        self.tree.column("Value", width=400)
        self.tree.pack(fill=tk.BOTH, expand=True)
        tree_scroll.config(command=self.tree.yview)

        # Bind selection to update detail view
        self.tree.bind("<<TreeviewSelect>>", self.on_selection_change)

        # Bind double-click to edit
        self.tree.bind("<Double-1>", self.edit_entry)

        # Bottom pane - Detail view (2/3 of height)
        detail_frame = ttk.Frame(paned)
        paned.add(detail_frame, weight=2)  # Changed from weight=1 to weight=2

        ttk.Label(detail_frame, text="Value Detail:", font=("TkDefaultFont", 10, "bold")).pack(anchor=tk.W, padx=5, pady=5)

        # Text widget with scrollbar for value display
        detail_scroll = ttk.Scrollbar(detail_frame)
        detail_scroll.pack(side=tk.RIGHT, fill=tk.Y)

        self.detail_text = tk.Text(detail_frame, wrap=tk.WORD, yscrollcommand=detail_scroll.set,
                                   font=("Menlo", 11), height=8)
        self.detail_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        detail_scroll.config(command=self.detail_text.yview)

        # Button frame for operations
        button_frame = ttk.Frame(editor_frame, padding="10")
        button_frame.pack(fill=tk.X)

        ttk.Button(button_frame, text="🤌 Add Field", command=self.add_field).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="✏️ Edit Field", command=self.edit_entry).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="❌ Delete Field", command=self.delete_field).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="✌️ Copy Value", command=self.copy_value).pack(side=tk.LEFT, padx=5)

        # Status bar at the bottom
        status_frame = ttk.Frame(self.root, relief=tk.SUNKEN)
        status_frame.pack(fill=tk.X, side=tk.BOTTOM)

        self.status_label = ttk.Label(status_frame, text="Ready", padding=(10, 5))
        self.status_label.pack(side=tk.LEFT, fill=tk.X, expand=True)

    def bind_mousewheel(self, widget):
        """Bind mousewheel/trackpad scrolling to canvas"""
        system = platform.system()

        if system == "Darwin":  # macOS
            widget.bind("<MouseWheel>", self.on_mousewheel)
            widget.bind("<Button-4>", self.on_mousewheel)
            widget.bind("<Button-5>", self.on_mousewheel)
        elif system == "Windows":
            widget.bind("<MouseWheel>", self.on_mousewheel)
        elif system == "Linux":
            widget.bind("<Button-4>", self.on_mousewheel)
            widget.bind("<Button-5>", self.on_mousewheel)

        widget.bind("<Enter>", lambda e: widget.bind_all("<MouseWheel>", self.on_mousewheel))
        widget.bind("<Leave>", lambda e: widget.unbind_all("<MouseWheel>"))

    def on_mousewheel(self, event):
        """Handle mousewheel/trackpad scrolling"""
        system = platform.system()

        if system == "Darwin":  # macOS
            self.thumb_canvas.yview_scroll(int(-1 * (event.delta)), "units")
        elif system == "Windows":
            self.thumb_canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
        elif system == "Linux":
            if event.num == 4:
                self.thumb_canvas.yview_scroll(-1, "units")
            elif event.num == 5:
                self.thumb_canvas.yview_scroll(1, "units")

    def on_thumb_frame_configure(self, event):
        """Update scrollregion when thumbnail container size changes"""
        self.thumb_canvas.configure(scrollregion=self.thumb_canvas.bbox("all"))

    def on_thumb_canvas_configure(self, event):
        """Update canvas window width to match canvas width"""
        self.thumb_canvas.itemconfig(self.thumb_canvas_window, width=event.width)

    def scroll_to_thumbnail(self, filepath):
        """Scroll the canvas to make the selected thumbnail visible"""
        filepath_str = str(filepath)
        if filepath_str not in self.thumbnail_frames:
            return

        frame = self.thumbnail_frames[filepath_str]

        # Update the canvas to ensure all geometries are calculated
        self.thumb_canvas.update_idletasks()

        # Get the frame's position
        frame_y = frame.winfo_y()
        frame_height = frame.winfo_height()

        # Get canvas viewport
        canvas_height = self.thumb_canvas.winfo_height()

        # Calculate scroll position to center the thumbnail
        total_height = self.thumb_container.winfo_height()
        if total_height > 0:
            # Center the thumbnail in the viewport
            target_y = frame_y - (canvas_height / 2) + (frame_height / 2)
            scroll_fraction = max(0, min(1, target_y / total_height))
            self.thumb_canvas.yview_moveto(scroll_fraction)

    def browse_directory(self):
        """Browse a directory and show PNG thumbnails"""
        directory = filedialog.askdirectory(title="Select directory with PNG files")

        if directory:
            self.current_directory = directory
            self.load_directory_thumbnails(directory)

    def load_directory_thumbnails(self, directory):
        """Load and display thumbnails for all PNG files in directory"""
        # Clear existing thumbnails
        for widget in self.thumb_container.winfo_children():
            widget.destroy()
        self.thumbnail_images.clear()
        self.thumbnail_frames.clear()

        # Create and show loading indicator with more visibility
        loading_frame = tk.Frame(self.thumb_container, bg=self.theme_colors["frame_bg"])
        loading_frame.pack(fill=tk.BOTH, expand=True, pady=50)

        loading_label = tk.Label(loading_frame, text="Loading thumbnails...", 
                                font=("TkDefaultFont", 11, "bold"), foreground="#4a90d9", bg=self.theme_colors["frame_bg"])
        loading_label.pack()

        # Force immediate display
        self.thumb_container.update()
        self.root.update()

        # Find all PNG files
        png_files = sorted(Path(directory).glob("*.png"))

        if not png_files:
            loading_frame.destroy()
            no_files_frame = tk.Frame(self.thumb_container, bg=self.theme_colors["frame_bg"])
            no_files_frame.pack(fill=tk.BOTH, expand=True, pady=50)
            tk.Label(no_files_frame, text="No PNG files found", 
                    font=("TkDefaultFont", 10), foreground="gray", bg=self.theme_colors["frame_bg"]).pack()
            self.set_status(f"No PNG files found in {Path(directory).name}", color="orange")
            return

        # Update loading message with count
        loading_label.config(text=f"Loading {len(png_files)} thumbnails...")
        self.root.update()

        # Create thumbnails
        thumbnail_size = (150, 150)

        for idx, png_file in enumerate(png_files):
            try:
                # Update progress every 3 files for better visual feedback
                if idx % 3 == 0:
                    loading_label.config(text=f"Loading... {idx + 1}/{len(png_files)}")
                    self.root.update()

                # Load and create thumbnail
                img = Image.open(png_file)
                img.thumbnail(thumbnail_size, Image.Resampling.LANCZOS)

                # Convert to PhotoImage
                photo = ImageTk.PhotoImage(img)
                self.thumbnail_images[str(png_file)] = photo

                # Create frame for each thumbnail with better styling
                thumb_frame = tk.Frame(self.thumb_container, relief=tk.RIDGE, borderwidth=2, 
                                      bg="white", padx=5, pady=5)
                thumb_frame.pack(fill=tk.X, padx=5, pady=5)

                # Store frame reference
                self.thumbnail_frames[str(png_file)] = thumb_frame

                # Button with image
                btn = tk.Button(thumb_frame, image=photo, command=lambda f=png_file: self.load_file_from_path(f),
                               cursor="hand2", bg="white", relief=tk.FLAT, bd=0)
                btn.pack(side=tk.TOP, padx=2, pady=2)

                # Hover effects
                def on_enter(e, frame=thumb_frame, path=str(png_file)):
                    if self.current_file != path:
                        frame.config(bg="#e8f4f8", relief=tk.RAISED)

                def on_leave(e, frame=thumb_frame, path=str(png_file)):
                    if self.current_file != path:
                        frame.config(bg="white", relief=tk.RIDGE)

                thumb_frame.bind("<Enter>", on_enter)
                thumb_frame.bind("<Leave>", on_leave)
                btn.bind("<Enter>", on_enter)
                btn.bind("<Leave>", on_leave)

                # Filename label
                name_label = tk.Label(thumb_frame, text=png_file.name, wraplength=140, 
                                     justify=tk.CENTER, bg="white", font=("TkDefaultFont", 9))
                name_label.pack(side=tk.TOP, padx=2, pady=2)
                name_label.bind("<Enter>", on_enter)
                name_label.bind("<Leave>", on_leave)

            except Exception as e:
                print(f"Error loading thumbnail for {png_file}: {e}")

        # Hide loading indicator
        loading_frame.destroy()

        # Force scroll region update
        def update_scroll():
            self.thumb_container.update_idletasks()
            self.thumb_canvas.update_idletasks()
            bbox = self.thumb_canvas.bbox("all")
            if bbox:
                self.thumb_canvas.configure(scrollregion=bbox)

        update_scroll()
        self.root.after(100, update_scroll)
        self.root.after(300, update_scroll)

        self.set_status(f"Loaded {len(png_files)} PNG file(s) from {Path(directory).name}", color="green")

    def load_file_from_path(self, filepath, auto_scroll=True):
        """Load a specific file from thumbnail click"""
        try:
            self.current_file = str(filepath)

            # Update thumbnail highlighting with improved visual feedback
            for path, frame in self.thumbnail_frames.items():
                if path == str(filepath):
                    # Selected style: blue gradient-like background with sunken relief
                    frame.config(relief=tk.SUNKEN, bg="#4a90d9", borderwidth=3)
                    # Update all children backgrounds
                    for child in frame.winfo_children():
                        if isinstance(child, tk.Label):
                            child.config(bg="#4a90d9", fg="white", font=("TkDefaultFont", 9, "bold"))
                        elif isinstance(child, tk.Button):
                            child.config(bg="#4a90d9")
                else:
                    # Unselected style: white background with ridge relief
                    frame.config(relief=tk.RIDGE, bg="white", borderwidth=2)
                    for child in frame.winfo_children():
                        if isinstance(child, tk.Label):
                            child.config(bg="white", fg="black", font=("TkDefaultFont", 9))
                        elif isinstance(child, tk.Button):
                            child.config(bg="white")

            # Scroll to the selected thumbnail if requested
            if auto_scroll:
                self.root.after(100, lambda: self.scroll_to_thumbnail(filepath))

            # Read metadata using PIL
            img = Image.open(filepath)
            self.metadata_dict = {}

            # Extract text chunks (tEXt, zTXt, iTXt)
            if hasattr(img, 'text'):
                self.metadata_dict = dict(img.text)

            self.file_label.config(text=Path(filepath).name, foreground="")
            self.mark_as_saved()
            self.refresh_tree()

            field_count = len(self.metadata_dict)
            self.set_status(f"Loaded {field_count} metadata field(s) from {Path(filepath).name}", color="green")

        except Exception as e:
            messagebox.showerror("Error", f"Failed to open file:\n{str(e)}")
            self.set_status("Failed to open file", color="red")

    def show_about(self):
        """Show application about dialog"""
        about_text = f"{APP_NAME} v{APP_VERSION}\n\n" \
                     "A graphical tool for viewing and editing metadata in PNG files.\n\n" \
                     "Features:\n" \
                     "✅ Browse directories with thumbnail previews\n" \
                     "✅ Trackpad/mousewheel scrolling support\n" \
                     "✅ Auto-scroll to selected images\n" \
                     "✅ View and edit PNG metadata\n" \
                     "✅ Copy metadata between files\n\n" \
                     "Author: Robert Tusa\n" \
                     "License: MIT"

        messagebox.showinfo(
            f"About {APP_NAME}",
            about_text,
            parent=self.root
        )

    def set_status(self, message, duration=3000, color=""):
        """Set status bar message that auto-clears after duration (ms)"""
        if self.status_timer:
            self.root.after_cancel(self.status_timer)

        self.status_label.config(text=message, foreground=color)

        if duration > 0:
            self.status_timer = self.root.after(duration, lambda: self.status_label.config(text="Ready", foreground=""))

    def mark_as_modified(self):
        """Mark the document as having unsaved changes"""
        if not self.has_unsaved_changes:
            self.has_unsaved_changes = True
            self.update_title()
            self.changes_label.config(text="Unsaved changes")

    def mark_as_saved(self):
        """Mark the document as saved"""
        self.has_unsaved_changes = False
        self.update_title()
        self.changes_label.config(text="")

    def update_title(self):
        """Update window title with unsaved indicator"""
        base_title = f"{APP_NAME} v{APP_VERSION}"
        if self.current_file:
            filename = Path(self.current_file).name
            if self.has_unsaved_changes:
                self.root.title(f"{base_title} - {filename} *")
            else:
                self.root.title(f"{base_title} - {filename}")
        else:
            self.root.title(base_title)

    def format_value_for_display(self, value):
        """Try to format value as JSON if possible, otherwise return as-is"""
        try:
            parsed = json.loads(value)
            return json.dumps(parsed, indent=2, ensure_ascii=False)
        except (json.JSONDecodeError, TypeError):
            return value

    def truncate_value(self, value, max_length=80):
        """Truncate long values for tree display"""
        value_str = str(value)
        if len(value_str) > max_length:
            return value_str[:max_length] + "..."
        return value_str

    def on_selection_change(self, event):
        """Update detail view when selection changes"""
        selected = self.tree.selection()
        if selected:
            item = selected[0]
            key, _ = self.tree.item(item, "values")
            if key in self.metadata_dict:
                value = self.metadata_dict[key]
                formatted_value = self.format_value_for_display(value)

                self.detail_text.config(state=tk.NORMAL)
                self.detail_text.delete(1.0, tk.END)
                self.detail_text.insert(1.0, formatted_value)
                self.detail_text.config(state=tk.DISABLED)

    def open_file(self):
        filepath = filedialog.askopenfilename(
            title="Select PNG file",
            filetypes=[("PNG files", "*.png"), ("All files", "*.*")]
        )

        if filepath:
            parent_dir = str(Path(filepath).parent)
            if parent_dir != self.current_directory:
                self.current_directory = parent_dir
                self.load_directory_thumbnails(parent_dir)

            self.load_file_from_path(Path(filepath), auto_scroll=True)

    def refresh_tree(self):
        for item in self.tree.get_children():
            self.tree.delete(item)

        self.detail_text.config(state=tk.NORMAL)
        self.detail_text.delete(1.0, tk.END)
        self.detail_text.config(state=tk.DISABLED)

        for key, value in self.metadata_dict.items():
            truncated = self.truncate_value(value)
            self.tree.insert("", tk.END, values=(key, truncated))

    def add_field(self):
        if self.current_file is None:
            messagebox.showwarning("No File", "Please open a PNG file first")
            return

        dialog = tk.Toplevel(self.root)
        dialog.title("Add Field")
        dialog.geometry("600x400")
        dialog.minsize(400, 300)

        dialog.rowconfigure(1, weight=1)
        dialog.columnconfigure(1, weight=1)

        ttk.Label(dialog, text="Key:").grid(row=0, column=0, padx=10, pady=10, sticky=tk.W)
        key_entry = ttk.Entry(dialog, width=50)
        key_entry.grid(row=0, column=1, padx=10, pady=10, sticky=tk.EW)

        ttk.Label(dialog, text="Value:").grid(row=1, column=0, padx=10, pady=10, sticky=tk.NW)

        text_frame = ttk.Frame(dialog)
        text_frame.grid(row=1, column=1, padx=10, pady=10, sticky=tk.NSEW)

        value_scroll = ttk.Scrollbar(text_frame)
        value_scroll.pack(side=tk.RIGHT, fill=tk.Y)

        value_text = tk.Text(text_frame, wrap=tk.WORD, yscrollcommand=value_scroll.set,
                            font=("Menlo", 11))
        value_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        value_scroll.config(command=value_text.yview)

        button_frame = ttk.Frame(dialog)
        button_frame.grid(row=2, column=0, columnspan=2, pady=10, sticky=tk.E)

        def save_new_field():
            key = key_entry.get().strip()
            value = value_text.get(1.0, tk.END).strip()

            if not key:
                messagebox.showwarning("Invalid Input", "Key cannot be empty")
                return

            self.metadata_dict[key] = value
            self.mark_as_modified()
            self.refresh_tree()
            self.set_status(f"Added field '{key}'", color="green")
            dialog.destroy()

        ttk.Button(button_frame, text="Cancel", command=dialog.destroy).pack(side=tk.RIGHT, padx=5)
        ttk.Button(button_frame, text="Add", command=save_new_field).pack(side=tk.RIGHT, padx=5)

        key_entry.focus()

    def edit_entry(self, event=None):
        selected = self.tree.selection()
        if not selected:
            messagebox.showwarning("No Selection", "Please select a field to edit")
            return

        item = selected[0]
        key, _ = self.tree.item(item, "values")
        current_value = self.metadata_dict[key]

        formatted_value = self.format_value_for_display(current_value)

        dialog = tk.Toplevel(self.root)
        dialog.title("Edit Field")
        dialog.geometry("600x400")
        dialog.minsize(400, 300)

        dialog.rowconfigure(1, weight=1)
        dialog.columnconfigure(1, weight=1)

        ttk.Label(dialog, text="Key:").grid(row=0, column=0, padx=10, pady=10, sticky=tk.W)
        key_label = ttk.Label(dialog, text=key, font=("TkDefaultFont", 10, "bold"))
        key_label.grid(row=0, column=1, padx=10, pady=10, sticky=tk.W)

        ttk.Label(dialog, text="Value:").grid(row=1, column=0, padx=10, pady=10, sticky=tk.NW)

        text_frame = ttk.Frame(dialog)
        text_frame.grid(row=1, column=1, padx=10, pady=10, sticky=tk.NSEW)

        value_scroll = ttk.Scrollbar(text_frame)
        value_scroll.pack(side=tk.RIGHT, fill=tk.Y)

        value_text = tk.Text(text_frame, wrap=tk.WORD, yscrollcommand=value_scroll.set,
                            font=("Menlo", 11))
        value_text.insert(1.0, formatted_value)
        value_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        value_scroll.config(command=value_text.yview)

        button_frame = ttk.Frame(dialog)
        button_frame.grid(row=2, column=0, columnspan=2, pady=10, sticky=tk.E)

        def save_edit():
            new_value = value_text.get(1.0, tk.END).strip()
            self.metadata_dict[key] = new_value
            self.mark_as_modified()
            self.refresh_tree()
            self.set_status(f"Updated field '{key}'", color="green")
            dialog.destroy()

        ttk.Button(button_frame, text="Cancel", command=dialog.destroy).pack(side=tk.RIGHT, padx=5)
        ttk.Button(button_frame, text="Save", command=save_edit).pack(side=tk.RIGHT, padx=5)

        value_text.focus()

    def delete_field(self):
        selected = self.tree.selection()
        if not selected:
            messagebox.showwarning("No Selection", "Please select a field to delete")
            return

        item = selected[0]
        key, _ = self.tree.item(item, "values")

        if messagebox.askyesno("Confirm Delete", f"Delete field '{key}'?"):
            if key in self.metadata_dict:
                del self.metadata_dict[key]
                self.mark_as_modified()
                self.refresh_tree()
                self.set_status(f"Deleted field '{key}'", color="green")

    def copy_value(self):
        selected = self.tree.selection()
        if not selected:
            messagebox.showwarning("No Selection", "Please select a field to copy")
            return

        item = selected[0]
        key, _ = self.tree.item(item, "values")
        value = self.metadata_dict[key]

        self.root.clipboard_clear()
        self.root.clipboard_append(value)
        self.set_status(f"Copied value of '{key}' to clipboard", color="green")

    def save_changes(self):
        if self.current_file is None:
            messagebox.showwarning("No File", "Please open a PNG file first")
            return

        try:
            img = Image.open(self.current_file)

            metadata = PngInfo()
            for key, value in self.metadata_dict.items():
                metadata.add_text(key, value)

            img.save(self.current_file, pnginfo=metadata)

            self.mark_as_saved()
            self.set_status("Metadata saved successfully", color="green")

        except Exception as e:
            messagebox.showerror("Error", f"Failed to save metadata:\n{str(e)}")
            self.set_status("Failed to save metadata", color="red")

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