#!/usr/bin/env python3
"""
PySceneDetect GUI - Scene Splitter
A simple Windows GUI wrapper for PySceneDetect using Tkinter.

Usage:
    python scene_splitter_gui.py

Requirements:
    - Python 3.10+
    - PySceneDetect installed: pip install scenedetect
    - FFmpeg in PATH (for video splitting)
"""

import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
import subprocess
import os
import sys
import threading
import json
from pathlib import Path


class SceneSplitterGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Scene Splitter - PySceneDetect GUI")
        self.root.geometry("700x650")
        self.root.minsize(650, 550)
        
        # Try to set a decent icon if available
        try:
            self.root.iconbitmap(default="")
        except:
            pass
        
        # Variables
        self.input_file = tk.StringVar()
        self.output_folder = tk.StringVar()
        self.threshold = tk.DoubleVar(value=27.0)
        self.min_scene_len = tk.DoubleVar(value=0.6)
        
        # Checkboxes
        self.split_video = tk.BooleanVar(value=True)
        self.copy_mode = tk.BooleanVar(value=False)
        self.reencode_mkv = tk.BooleanVar(value=True)  # Default ON for MKV compatibility
        self.export_csv = tk.BooleanVar(value=True)
        self.export_edl = tk.BooleanVar(value=False)
        self.export_html = tk.BooleanVar(value=False)
        self.export_images = tk.BooleanVar(value=False)
        self.drop_scenes = tk.BooleanVar(value=False)
        self.show_progress = tk.BooleanVar(value=True)
        self.backend_opencv = tk.BooleanVar(value=True)
        self.backend_ffmpeg = tk.BooleanVar(value=False)
        
        self.build_ui()
        self.check_dependencies()
    
    def build_ui(self):
        # Main container with padding
        main_frame = ttk.Frame(self.root, padding="20")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # === TITLE ===
        title_label = ttk.Label(
            main_frame, 
            text="Scene Splitter", 
            font=("Segoe UI", 18, "bold")
        )
        title_label.pack(anchor=tk.W, pady=(0, 5))
        
        subtitle = ttk.Label(
            main_frame,
            text="GUI for PySceneDetect - Automatically detect and split video scenes",
            font=("Segoe UI", 10),
            foreground="#666666"
        )
        subtitle.pack(anchor=tk.W, pady=(0, 15))
        
        # === INPUT FILE SECTION ===
        input_frame = ttk.LabelFrame(main_frame, text="Input Video", padding="10")
        input_frame.pack(fill=tk.X, pady=(0, 10))
        
        input_row = ttk.Frame(input_frame)
        input_row.pack(fill=tk.X)
        
        self.input_entry = ttk.Entry(input_row, textvariable=self.input_file, font=("Segoe UI", 10))
        self.input_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
        
        browse_btn = ttk.Button(input_row, text="Browse...", command=self.browse_input)
        browse_btn.pack(side=tk.RIGHT)
        
        # === OUTPUT FOLDER SECTION ===
        output_frame = ttk.LabelFrame(main_frame, text="Output Folder", padding="10")
        output_frame.pack(fill=tk.X, pady=(0, 10))
        
        output_row = ttk.Frame(output_frame)
        output_row.pack(fill=tk.X)
        
        self.output_entry = ttk.Entry(output_row, textvariable=self.output_folder, font=("Segoe UI", 10))
        self.output_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
        
        out_browse_btn = ttk.Button(output_row, text="Browse...", command=self.browse_output)
        out_browse_btn.pack(side=tk.RIGHT)
        
        # === DETECTION SETTINGS ===
        settings_frame = ttk.LabelFrame(main_frame, text="Detection Settings", padding="10")
        settings_frame.pack(fill=tk.X, pady=(0, 10))
        
        # Threshold
        thresh_frame = ttk.Frame(settings_frame)
        thresh_frame.pack(fill=tk.X, pady=(0, 8))
        
        ttk.Label(thresh_frame, text="Threshold:", font=("Segoe UI", 10)).pack(side=tk.LEFT)
        ttk.Label(thresh_frame, text="(Higher = fewer scenes detected)", 
                  font=("Segoe UI", 9), foreground="#888").pack(side=tk.LEFT, padx=(5, 0))
        
        thresh_slider_frame = ttk.Frame(settings_frame)
        thresh_slider_frame.pack(fill=tk.X)
        
        thresh_slider = ttk.Scale(
            thresh_slider_frame, 
            from_=1, to=100, 
            variable=self.threshold,
            orient=tk.HORIZONTAL
        )
        thresh_slider.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))
        
        thresh_value = ttk.Spinbox(
            thresh_slider_frame, 
            from_=1, to=100, 
            textvariable=self.threshold,
            width=6,
            font=("Segoe UI", 10)
        )
        thresh_value.pack(side=tk.RIGHT)
        
        # Min scene length
        min_frame = ttk.Frame(settings_frame)
        min_frame.pack(fill=tk.X, pady=(8, 0))
        
        ttk.Label(min_frame, text="Min Scene Length (sec):", font=("Segoe UI", 10)).pack(side=tk.LEFT)
        
        min_spin = ttk.Spinbox(
            min_frame, 
            from_=0.1, to=60, 
            textvariable=self.min_scene_len,
            width=6,
            font=("Segoe UI", 10),
            increment=0.1
        )
        min_spin.pack(side=tk.LEFT, padx=(10, 0))
        
        # === OUTPUT OPTIONS ===
        options_frame = ttk.LabelFrame(main_frame, text="Output Options", padding="10")
        options_frame.pack(fill=tk.X, pady=(0, 10))
        
        # Left column
        left_opts = ttk.Frame(options_frame)
        left_opts.pack(side=tk.LEFT, fill=tk.Y, expand=True)
        
        ttk.Checkbutton(left_opts, text="Split Video into Scenes", 
                       variable=self.split_video).pack(anchor=tk.W, pady=2)
        ttk.Checkbutton(left_opts, text="Copy Mode (fast, no re-encode)", 
                       variable=self.copy_mode).pack(anchor=tk.W, pady=2)
        ttk.Checkbutton(left_opts, text="Re-encode H.264/AAC (for MKV)", 
                       variable=self.reencode_mkv).pack(anchor=tk.W, pady=2)
        ttk.Checkbutton(left_opts, text="Export CSV (timecodes)", 
                       variable=self.export_csv).pack(anchor=tk.W, pady=2)
        ttk.Checkbutton(left_opts, text="Export EDL (Premiere/FCP)", 
                       variable=self.export_edl).pack(anchor=tk.W, pady=2)
        
        # Right column
        right_opts = ttk.Frame(options_frame)
        right_opts.pack(side=tk.RIGHT, fill=tk.Y, expand=True)
        
        ttk.Checkbutton(right_opts, text="Export HTML Report", 
                       variable=self.export_html).pack(anchor=tk.W, pady=2)
        ttk.Checkbutton(right_opts, text="Save Scene Images (thumbnails)", 
                       variable=self.export_images).pack(anchor=tk.W, pady=2)
        ttk.Checkbutton(right_opts, text="Show Progress Bar", 
                       variable=self.show_progress).pack(anchor=tk.W, pady=2)
        
        # === BACKEND OPTIONS ===
        backend_frame = ttk.LabelFrame(main_frame, text="Detection Backend", padding="10")
        backend_frame.pack(fill=tk.X, pady=(0, 10))
        
        ttk.Radiobutton(backend_frame, text="OpenCV (default, faster)", 
                       variable=self.backend_opencv, value=True).pack(anchor=tk.W)
        ttk.Radiobutton(backend_frame, text="FFmpeg (more accurate, slower)", 
                       variable=self.backend_ffmpeg, value=True).pack(anchor=tk.W)
        
        # === RUN BUTTON ===
        btn_frame = ttk.Frame(main_frame)
        btn_frame.pack(fill=tk.X, pady=(0, 10))
        
        self.run_btn = ttk.Button(
            btn_frame, 
            text="Detect Scenes", 
            command=self.run_detection,
            style="Accent.TButton"
        )
        self.run_btn.pack(side=tk.LEFT, padx=(0, 10))
        
        style = ttk.Style()
        style.configure("Accent.TButton", font=("Segoe UI", 11, "bold"))
        
        ttk.Button(btn_frame, text="Clear Log", 
                  command=self.clear_log).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(btn_frame, text="Open Output Folder", 
                  command=self.open_output_folder).pack(side=tk.LEFT)
        
        # === PROGRESS & LOG ===
        log_frame = ttk.LabelFrame(main_frame, text="Progress & Output", padding="10")
        log_frame.pack(fill=tk.BOTH, expand=True)
        
        self.log_text = scrolledtext.ScrolledText(
            log_frame,
            wrap=tk.WORD,
            font=("Consolas", 10),
            height=10,
            state=tk.DISABLED
        )
        self.log_text.pack(fill=tk.BOTH, expand=True)
        
        # Status bar
        self.status_var = tk.StringVar(value="Ready")
        status_bar = ttk.Label(main_frame, textvariable=self.status_var, 
                              relief=tk.SUNKEN, anchor=tk.W, padding=(5, 2))
        status_bar.pack(fill=tk.X, pady=(5, 0))
    
    def check_dependencies(self):
        """Check if PySceneDetect and FFmpeg are available."""
        missing = []
        
        # Check scenedetect
        try:
            result = subprocess.run(["scenedetect", "--version"], 
                                   capture_output=True, text=True, timeout=5)
            if result.returncode != 0:
                missing.append("PySceneDetect")
        except FileNotFoundError:
            missing.append("PySceneDetect")
        
        # Check ffmpeg
        try:
            result = subprocess.run(["ffmpeg", "-version"], 
                                   capture_output=True, text=True, timeout=5)
            if result.returncode != 0:
                missing.append("FFmpeg")
        except FileNotFoundError:
            missing.append("FFmpeg")
        
        if missing:
            self.log(f"WARNING: Missing dependencies: {', '.join(missing)}\n")
            self.log("Install PySceneDetect: pip install scenedetect\n")
            self.log("Install FFmpeg: https://ffmpeg.org/download.html\n\n")
    
    def browse_input(self):
        """Open file dialog to select input video."""
        filetypes = [
            ("Video files", "*.mp4 *.avi *.mkv *.mov *.wmv *.flv *.webm *.m4v *.mpeg *.mpg"),
            ("MP4", "*.mp4"),
            ("AVI", "*.avi"),
            ("MKV", "*.mkv"),
            ("MOV", "*.mov"),
            ("All files", "*.*")
        ]
        filename = filedialog.askopenfilename(
            title="Select Video File",
            filetypes=filetypes
        )
        if filename:
            self.input_file.set(filename)
            # Auto-set output folder to same directory if not set
            if not self.output_folder.get():
                self.output_folder.set(str(Path(filename).parent / "scenes"))
    
    def browse_output(self):
        """Open folder dialog to select output directory."""
        folder = filedialog.askdirectory(title="Select Output Folder")
        if folder:
            self.output_folder.set(folder)
    
    def open_output_folder(self):
        """Open the output folder in Windows Explorer."""
        folder = self.output_folder.get()
        if folder and os.path.exists(folder):
            os.startfile(folder)
        else:
            messagebox.showwarning("No Folder", "Output folder does not exist yet.")
    
    def log(self, message):
        """Append message to the log text area."""
        self.log_text.configure(state=tk.NORMAL)
        self.log_text.insert(tk.END, message)
        self.log_text.see(tk.END)
        self.log_text.configure(state=tk.DISABLED)
    
    def clear_log(self):
        """Clear the log text area."""
        self.log_text.configure(state=tk.NORMAL)
        self.log_text.delete(1.0, tk.END)
        self.log_text.configure(state=tk.DISABLED)
    
    def build_command(self):
        """Build the PySceneDetect command based on selected options."""
        cmd = ["scenedetect"]
        
        # Input file
        input_path = self.input_file.get()
        if not input_path:
            messagebox.showerror("Error", "Please select an input video file.")
            return None
        if not os.path.exists(input_path):
            messagebox.showerror("Error", f"Input file not found:\n{input_path}")
            return None
        
        cmd.extend(["--input", input_path])
        
        # Output folder
        output_path = self.output_folder.get()
        if output_path:
            # Create output folder if it doesn't exist
            Path(output_path).mkdir(parents=True, exist_ok=True)
            cmd.extend(["--output", output_path])
        
        # Detection settings
        threshold = self.threshold.get()
        cmd.extend(["--threshold", str(threshold)])
        
        min_len = self.min_scene_len.get()
        cmd.extend(["--min-scene-len", str(min_len)])
        
        # Detection (always needed)
        cmd.append("detect-content")
        
        # Output options
        if self.split_video.get():
            if self.copy_mode.get():
                cmd.extend(["split-video", "--copy"])
            elif self.reencode_mkv.get():
                # User's preferred MKV encoding: H.264 + AAC for clean output
                cmd.extend(["split-video", "--preset", "fast", "--args", "-c:v libx264 -c:a aac"])
            else:
                cmd.append("split-video")
        
        if self.export_csv.get():
            cmd.append("export-html" if self.export_html.get() else "list-scenes")
            
        if self.export_edl.get():
            cmd.extend(["export-edl"])
        
        if self.export_images.get():
            cmd.extend(["save-images", "--num-images", "1"])
        
        return cmd
    
    def run_detection(self):
        """Run PySceneDetect in a background thread."""
        cmd = self.build_command()
        if not cmd:
            return
        
        self.run_btn.configure(state=tk.DISABLED, text="Running...")
        self.status_var.set("Processing...")
        self.log(f"\n{'='*50}\n")
        self.log(f"Command: {' '.join(cmd)}\n")
        self.log(f"{'='*50}\n\n")
        
        thread = threading.Thread(target=self._run_process, args=(cmd,))
        thread.daemon = True
        thread.start()
    
    def _run_process(self, cmd):
        """Execute the command and capture output."""
        try:
            process = subprocess.Popen(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                universal_newlines=True
            )
            
            for line in process.stdout:
                self.root.after(0, self.log, line)
            
            process.wait()
            
            if process.returncode == 0:
                self.root.after(0, self._on_success)
            else:
                self.root.after(0, self._on_error, f"Process exited with code {process.returncode}")
                
        except FileNotFoundError:
            self.root.after(0, self._on_error, 
                           "PySceneDetect not found. Run: pip install scenedetect")
        except Exception as e:
            self.root.after(0, self._on_error, str(e))
    
    def _on_success(self):
        """Called when detection completes successfully."""
        self.run_btn.configure(state=tk.NORMAL, text="Detect Scenes")
        self.status_var.set("Complete - Scenes detected successfully")
        self.log("\n✓ Scene detection complete!\n")
        self.log(f"Output saved to: {self.output_folder.get()}\n")
        
        # Show summary if possible
        try:
            output_dir = Path(self.output_folder.get())
            if output_dir.exists():
                scene_files = list(output_dir.glob("scene-*.mp4"))
                if scene_files:
                    self.log(f"Found {len(scene_files)} scene files\n")
        except:
            pass
    
    def _on_error(self, message):
        """Called when an error occurs."""
        self.run_btn.configure(state=tk.NORMAL, text="Detect Scenes")
        self.status_var.set("Error occurred")
        self.log(f"\n✗ ERROR: {message}\n")
        messagebox.showerror("Error", message)


def main():
    root = tk.Tk()
    
    # Set theme
    style = ttk.Style()
    style.theme_use('vista')  # Use native Windows theme
    
    app = SceneSplitterGUI(root)
    root.mainloop()


if __name__ == "__main__":
    main()
