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

class PandocGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Pandoc Document Converter")
        self.root.geometry("800x700")
        
        # Settings file path
        self.settings_file = Path(os.path.expanduser("~")) / ".pandoc_gui_settings.json"
        
        # Load settings
        self.settings = self.load_settings()
        
        # Variables
        self.input_file_var = tk.StringVar()
        self.output_file_var = tk.StringVar()
        self.input_format_var = tk.StringVar(value="markdown")
        self.output_format_var = tk.StringVar(value="html")
        self.standalone_var = tk.BooleanVar()
        self.toc_var = tk.BooleanVar()
        self.number_sections_var = tk.BooleanVar()
        
        # Directory tracking
        self.last_input_dir = self.settings.get("last_input_dir", str(Path.home()))
        self.last_output_dir = self.settings.get("last_output_dir", str(Path.home()))
        self.favorite_input_dir = self.settings.get("favorite_input_dir", "")
        self.favorite_output_dir = self.settings.get("favorite_output_dir", "")
        
        # Common formats
        self.input_formats = [
            "markdown", "html", "docx", "odt", "latex", "rst", "org", 
            "textile", "mediawiki", "epub", "rtf", "pdf", "json"
        ]
        
        self.output_formats = [
            "html", "html5", "docx", "odt", "pdf", "latex", "beamer",
            "markdown", "rst", "org", "epub", "rtf", "pptx", "json"
        ]
        
        self.setup_ui()
        
        # Bind window close event to save settings
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        
    def load_settings(self):
        """Load settings from JSON file"""
        try:
            if self.settings_file.exists():
                with open(self.settings_file, 'r') as f:
                    return json.load(f)
        except Exception:
            pass
        return {}
    
    def save_settings(self):
        """Save settings to JSON file"""
        settings = {
            "last_input_dir": self.last_input_dir,
            "last_output_dir": self.last_output_dir,
            "favorite_input_dir": self.favorite_input_dir,
            "favorite_output_dir": self.favorite_output_dir,
            "window_geometry": self.root.geometry()
        }
        try:
            with open(self.settings_file, 'w') as f:
                json.dump(settings, f, indent=2)
        except Exception:
            pass
    
    def on_closing(self):
        """Handle window closing"""
        self.save_settings()
        self.root.destroy()
        
    def setup_ui(self):
        # Main frame
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # Configure grid weights
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)
        
        # Menu bar
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)
        
        # Settings menu
        settings_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Settings", menu=settings_menu)
        settings_menu.add_command(label="Set Favorite Directories", command=self.show_directory_settings)
        settings_menu.add_separator()
        settings_menu.add_command(label="Reset Settings", command=self.reset_settings)
        
        # Input file selection
        ttk.Label(main_frame, text="Input File:").grid(row=0, column=0, sticky=tk.W, pady=5)
        input_frame = ttk.Frame(main_frame)
        input_frame.grid(row=0, column=1, columnspan=2, sticky=(tk.W, tk.E), pady=5)
        input_frame.columnconfigure(0, weight=1)
        
        ttk.Entry(input_frame, textvariable=self.input_file_var).grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 5))
        ttk.Button(input_frame, text="Browse", command=self.browse_input_file).grid(row=0, column=1)
        
        # Output file selection
        ttk.Label(main_frame, text="Output File:").grid(row=1, column=0, sticky=tk.W, pady=5)
        output_frame = ttk.Frame(main_frame)
        output_frame.grid(row=1, column=1, columnspan=2, sticky=(tk.W, tk.E), pady=5)
        output_frame.columnconfigure(0, weight=1)
        
        ttk.Entry(output_frame, textvariable=self.output_file_var).grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 5))
        ttk.Button(output_frame, text="Browse", command=self.browse_output_file).grid(row=0, column=1)
        
        # Format selection
        format_frame = ttk.LabelFrame(main_frame, text="Format Settings", padding="10")
        format_frame.grid(row=2, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)
        format_frame.columnconfigure(1, weight=1)
        format_frame.columnconfigure(3, weight=1)
        
        ttk.Label(format_frame, text="From:").grid(row=0, column=0, sticky=tk.W)
        ttk.Combobox(format_frame, textvariable=self.input_format_var, 
                    values=self.input_formats, state="readonly").grid(row=0, column=1, sticky=(tk.W, tk.E), padx=5)
        
        ttk.Label(format_frame, text="To:").grid(row=0, column=2, sticky=tk.W)
        ttk.Combobox(format_frame, textvariable=self.output_format_var, 
                    values=self.output_formats, state="readonly").grid(row=0, column=3, sticky=(tk.W, tk.E), padx=5)
        
        # Options
        options_frame = ttk.LabelFrame(main_frame, text="Options", padding="10")
        options_frame.grid(row=3, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)
        
        ttk.Checkbutton(options_frame, text="Standalone (include headers/footers)", 
                       variable=self.standalone_var).grid(row=0, column=0, sticky=tk.W)
        ttk.Checkbutton(options_frame, text="Include Table of Contents", 
                       variable=self.toc_var).grid(row=1, column=0, sticky=tk.W)
        ttk.Checkbutton(options_frame, text="Number Sections", 
                       variable=self.number_sections_var).grid(row=2, column=0, sticky=tk.W)
        
        # Additional options entry
        ttk.Label(main_frame, text="Additional Options:").grid(row=4, column=0, sticky=tk.W, pady=5)
        self.additional_options = tk.StringVar()
        ttk.Entry(main_frame, textvariable=self.additional_options).grid(row=4, column=1, columnspan=2, sticky=(tk.W, tk.E), pady=5)
        
        # Buttons
        button_frame = ttk.Frame(main_frame)
        button_frame.grid(row=5, column=0, columnspan=3, pady=20)
        
        ttk.Button(button_frame, text="Convert", command=self.convert_document).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="Clear", command=self.clear_fields).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="Quick Convert", command=self.quick_convert).pack(side=tk.LEFT, padx=5)
        
        # Progress bar
        self.progress = ttk.Progressbar(main_frame, mode='indeterminate')
        self.progress.grid(row=6, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
        
        # Output/Log area
        log_frame = ttk.LabelFrame(main_frame, text="Output Log", padding="5")
        log_frame.grid(row=7, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10)
        log_frame.columnconfigure(0, weight=1)
        log_frame.rowconfigure(0, weight=1)
        main_frame.rowconfigure(7, weight=1)
        
        self.log_text = scrolledtext.ScrolledText(log_frame, height=10, width=80)
        self.log_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
    def show_directory_settings(self):
        """Show directory settings dialog"""
        settings_window = tk.Toplevel(self.root)
        settings_window.title("Directory Settings")
        settings_window.geometry("500x300")
        settings_window.transient(self.root)
        settings_window.grab_set()
        
        # Center the window
        settings_window.geometry("+%d+%d" % (
            self.root.winfo_rootx() + 50,
            self.root.winfo_rooty() + 50
        ))
        
        frame = ttk.Frame(settings_window, padding="20")
        frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        settings_window.columnconfigure(0, weight=1)
        settings_window.rowconfigure(0, weight=1)
        frame.columnconfigure(1, weight=1)
        
        # Favorite input directory
        ttk.Label(frame, text="Favorite Input Directory:").grid(row=0, column=0, sticky=tk.W, pady=5)
        input_dir_frame = ttk.Frame(frame)
        input_dir_frame.grid(row=0, column=1, columnspan=2, sticky=(tk.W, tk.E), pady=5)
        input_dir_frame.columnconfigure(0, weight=1)
        
        favorite_input_var = tk.StringVar(value=self.favorite_input_dir)
        ttk.Entry(input_dir_frame, textvariable=favorite_input_var).grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 5))
        ttk.Button(input_dir_frame, text="Browse", 
                  command=lambda: self._browse_directory(favorite_input_var)).grid(row=0, column=1)
        
        # Favorite output directory
        ttk.Label(frame, text="Favorite Output Directory:").grid(row=1, column=0, sticky=tk.W, pady=5)
        output_dir_frame = ttk.Frame(frame)
        output_dir_frame.grid(row=1, column=1, columnspan=2, sticky=(tk.W, tk.E), pady=5)
        output_dir_frame.columnconfigure(0, weight=1)
        
        favorite_output_var = tk.StringVar(value=self.favorite_output_dir)
        ttk.Entry(output_dir_frame, textvariable=favorite_output_var).grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 5))
        ttk.Button(output_dir_frame, text="Browse", 
                  command=lambda: self._browse_directory(favorite_output_var)).grid(row=0, column=1)
        
        # Info label
        info_text = "Set favorite directories to quickly navigate to your most-used folders.\nLeave blank to use the last opened directory instead."
        ttk.Label(frame, text=info_text, foreground="gray").grid(row=2, column=0, columnspan=3, pady=20)
        
        # Buttons
        button_frame = ttk.Frame(frame)
        button_frame.grid(row=3, column=0, columnspan=3, pady=20)
        
        def save_and_close():
            self.favorite_input_dir = favorite_input_var.get()
            self.favorite_output_dir = favorite_output_var.get()
            self.save_settings()
            settings_window.destroy()
        
        ttk.Button(button_frame, text="Save", command=save_and_close).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="Cancel", command=settings_window.destroy).pack(side=tk.LEFT, padx=5)
    
    def _browse_directory(self, var):
        """Browse for directory"""
        directory = filedialog.askdirectory(initialdir=var.get() or str(Path.home()))
        if directory:
            var.set(directory)
    
    def reset_settings(self):
        """Reset all settings to defaults"""
        if messagebox.askyesno("Reset Settings", "This will reset all settings including favorite directories. Continue?"):
            self.settings_file.unlink(missing_ok=True)
            self.last_input_dir = str(Path.home())
            self.last_output_dir = str(Path.home())
            self.favorite_input_dir = ""
            self.favorite_output_dir = ""
            messagebox.showinfo("Settings Reset", "Settings have been reset to defaults.")
        
    def browse_input_file(self):
        # Determine initial directory
        initial_dir = self.favorite_input_dir if self.favorite_input_dir else self.last_input_dir
        
        filename = filedialog.askopenfilename(
            title="Select Input File",
            initialdir=initial_dir,
            filetypes=[
                ("All supported", "*.md *.html *.docx *.odt *.tex *.rst *.org *.txt"),
                ("Markdown files", "*.md *.markdown"),
                ("HTML files", "*.html *.htm"),
                ("Word documents", "*.docx"),
                ("OpenDocument", "*.odt"),
                ("LaTeX files", "*.tex *.latex"),
                ("All files", "*.*")
            ]
        )
        if filename:
            self.input_file_var.set(filename)
            # Update last input directory
            self.last_input_dir = str(Path(filename).parent)
            
            # Auto-detect input format
            ext = Path(filename).suffix.lower()
            format_map = {
                '.md': 'markdown', '.markdown': 'markdown',
                '.html': 'html', '.htm': 'html',
                '.docx': 'docx', '.odt': 'odt',
                '.tex': 'latex', '.latex': 'latex',
                '.rst': 'rst', '.org': 'org'
            }
            if ext in format_map:
                self.input_format_var.set(format_map[ext])
    
    def browse_output_file(self):
        # Get default filename based on input file and output format
        default_name = ""
        initial_dir = self.favorite_output_dir if self.favorite_output_dir else self.last_output_dir
        
        if self.input_file_var.get():
            input_path = Path(self.input_file_var.get())
            output_format = self.output_format_var.get()
            
            # Map format to extension
            format_to_ext = {
                'html': '.html', 'html5': '.html',
                'docx': '.docx', 'pdf': '.pdf',
                'odt': '.odt', 'latex': '.tex',
                'markdown': '.md', 'beamer': '.tex',
                'rst': '.rst', 'org': '.org',
                'epub': '.epub', 'rtf': '.rtf',
                'pptx': '.pptx', 'json': '.json'
            }
            
            ext = format_to_ext.get(output_format, f'.{output_format}')
            # Use the preferred output directory with the input filename
            default_name = str(Path(initial_dir) / input_path.with_suffix(ext).name)
        
        # Set up filetypes based on current output format
        current_format = self.output_format_var.get()
        filetypes = []
        
        # Add current format first
        format_filetypes = {
            'html': ("HTML files", "*.html"),
            'html5': ("HTML files", "*.html"),
            'docx': ("Word documents", "*.docx"),
            'pdf': ("PDF files", "*.pdf"),
            'odt': ("OpenDocument", "*.odt"),
            'latex': ("LaTeX files", "*.tex"),
            'beamer': ("LaTeX Beamer", "*.tex"),
            'markdown': ("Markdown files", "*.md"),
            'rst': ("reStructuredText", "*.rst"),
            'org': ("Org files", "*.org"),
            'epub': ("EPUB files", "*.epub"),
            'rtf': ("RTF files", "*.rtf"),
            'pptx': ("PowerPoint", "*.pptx"),
            'json': ("JSON files", "*.json")
        }
        
        if current_format in format_filetypes:
            filetypes.append(format_filetypes[current_format])
        
        # Add other common formats
        other_formats = [
            ("HTML files", "*.html"),
            ("Word documents", "*.docx"),
            ("PDF files", "*.pdf"),
            ("OpenDocument", "*.odt"),
            ("LaTeX files", "*.tex"),
            ("Markdown files", "*.md"),
            ("All files", "*.*")
        ]
        
        for fmt in other_formats:
            if fmt not in filetypes:
                filetypes.append(fmt)
        
        filename = filedialog.asksaveasfilename(
            title="Save Output File As",
            initialfile=Path(default_name).name if default_name else "",
            initialdir=Path(default_name).parent if default_name else initial_dir,
            filetypes=filetypes
        )
        
        if filename:
            # Update last output directory
            self.last_output_dir = str(Path(filename).parent)
            
            # Ensure the file has the correct extension for the output format
            file_path = Path(filename)
            output_format = self.output_format_var.get()
            
            format_to_ext = {
                'html': '.html', 'html5': '.html',
                'docx': '.docx', 'pdf': '.pdf',
                'odt': '.odt', 'latex': '.tex',
                'markdown': '.md', 'beamer': '.tex',
                'rst': '.rst', 'org': '.org',
                'epub': '.epub', 'rtf': '.rtf',
                'pptx': '.pptx', 'json': '.json'
            }
            
            expected_ext = format_to_ext.get(output_format, f'.{output_format}')
            
            # If the file doesn't have the right extension, add it
            if file_path.suffix.lower() != expected_ext:
                filename = str(file_path.with_suffix(expected_ext))
            
            self.output_file_var.set(filename)
            
            # Auto-detect output format based on extension
            ext = Path(filename).suffix.lower()
            format_map = {
                '.html': 'html', '.htm': 'html',
                '.docx': 'docx', '.pdf': 'pdf',
                '.odt': 'odt', '.tex': 'latex',
                '.md': 'markdown', '.markdown': 'markdown',
                '.rst': 'rst', '.org': 'org',
                '.epub': 'epub', '.rtf': 'rtf',
                '.pptx': 'pptx', '.json': 'json'
            }
            if ext in format_map:
                self.output_format_var.set(format_map[ext])
    
    def quick_convert(self):
        """Quick convert using auto-detected formats and common options"""
        if not self.input_file_var.get():
            messagebox.showerror("Error", "Please select an input file first")
            return
        
        input_path = Path(self.input_file_var.get())
        if not self.output_file_var.get():
            # Auto-generate output filename with proper extension
            output_format = self.output_format_var.get()
            format_to_ext = {
                'html': '.html', 'html5': '.html',
                'docx': '.docx', 'pdf': '.pdf',
                'odt': '.odt', 'latex': '.tex',
                'markdown': '.md', 'beamer': '.tex',
                'rst': '.rst', 'org': '.org',
                'epub': '.epub', 'rtf': '.rtf',
                'pptx': '.pptx', 'json': '.json'
            }
            output_ext = format_to_ext.get(output_format, f'.{output_format}')
            output_path = input_path.with_suffix(output_ext)
            self.output_file_var.set(str(output_path))
        
        # Set common options for quick convert
        self.standalone_var.set(True)
        self.convert_document()
    
    def clear_fields(self):
        self.input_file_var.set("")
        self.output_file_var.set("")
        self.additional_options.set("")
        self.standalone_var.set(False)
        self.toc_var.set(False)
        self.number_sections_var.set(False)
        self.log_text.delete(1.0, tk.END)
    
    def log_message(self, message):
        self.log_text.insert(tk.END, message + "\n")
        self.log_text.see(tk.END)
        self.root.update_idletasks()
    
    def convert_document(self):
        if not self.input_file_var.get():
            messagebox.showerror("Error", "Please select an input file")
            return
        
        if not self.output_file_var.get():
            messagebox.showerror("Error", "Please specify an output file")
            return
        
        # Start conversion in a separate thread to prevent GUI freezing
        threading.Thread(target=self._run_conversion, daemon=True).start()
    
    def _run_conversion(self):
        try:
            # Start progress bar
            self.progress.start()
            
            # Build pandoc command
            cmd = ["pandoc"]
            
            # Input format
            cmd.extend(["-f", self.input_format_var.get()])
            
            # Output format
            cmd.extend(["-t", self.output_format_var.get()])
            
            # Options
            if self.standalone_var.get():
                cmd.append("-s")
            
            if self.toc_var.get():
                cmd.append("--toc")
            
            if self.number_sections_var.get():
                cmd.append("--number-sections")
            
            # Additional options
            if self.additional_options.get().strip():
                # Split additional options by spaces (simple parsing)
                additional = self.additional_options.get().strip().split()
                cmd.extend(additional)
            
            # Input and output files
            cmd.extend(["-o", self.output_file_var.get()])
            cmd.append(self.input_file_var.get())
            
            self.log_message(f"Running command: {' '.join(cmd)}")
            
            # Run pandoc with hidden window on Windows
            startupinfo = None
            if os.name == 'nt':  # Windows
                startupinfo = subprocess.STARTUPINFO()
                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                startupinfo.wShowWindow = subprocess.SW_HIDE
            
            result = subprocess.run(cmd, capture_output=True, text=True, 
                                  startupinfo=startupinfo, creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0)
            
            if result.returncode == 0:
                self.log_message("✓ Conversion completed successfully!")
                if result.stdout:
                    self.log_message(f"Output: {result.stdout}")
                messagebox.showinfo("Success", f"Document converted successfully!\nOutput saved to: {self.output_file_var.get()}")
            else:
                self.log_message(f"✗ Conversion failed with return code {result.returncode}")
                if result.stderr:
                    self.log_message(f"Error: {result.stderr}")
                messagebox.showerror("Error", f"Conversion failed:\n{result.stderr}")
        
        except FileNotFoundError:
            error_msg = "Pandoc not found. Please make sure Pandoc is installed and in your PATH."
            self.log_message(f"✗ {error_msg}")
            messagebox.showerror("Error", error_msg)
        except Exception as e:
            error_msg = f"An error occurred: {str(e)}"
            self.log_message(f"✗ {error_msg}")
            messagebox.showerror("Error", error_msg)
        finally:
            # Stop progress bar
            self.progress.stop()

def main():
    root = tk.Tk()
    app = PandocGUI(root)
    root.mainloop()

if __name__ == "__main__":
    main()