"""
Main GUI window for HBAT application.

This module provides the main tkinter interface for the HBAT application,
allowing users to load PDB files, configure analysis parameters, and view results.
"""

import os
import threading
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext, ttk
from typing import Optional

from ..constants import APP_NAME, APP_VERSION, GUIDefaults
from ..core.analysis import AnalysisParameters, HBondAnalyzer
from .parameter_panel import ParameterPanel
from .results_panel import ResultsPanel


class MainWindow:
    """Main application window for HBAT.

    This class provides the primary GUI interface for HBAT, including
    file loading, parameter configuration, analysis execution, and
    results visualization.

    :param None: This class takes no parameters during initialization
    """

    def __init__(self) -> None:
        """Initialize the main window.

        Sets up the complete GUI interface including menus, toolbar,
        main content area, and status bar.

        :returns: None
        :rtype: None
        """
        self.root = tk.Tk()
        self.root.title(f"{APP_NAME} v{APP_VERSION}")
        self.root.geometry(f"{GUIDefaults.WINDOW_WIDTH}x{GUIDefaults.WINDOW_HEIGHT}")
        self.root.minsize(GUIDefaults.MIN_WINDOW_WIDTH, GUIDefaults.MIN_WINDOW_HEIGHT)

        # Analysis components
        self.analyzer: Optional[HBondAnalyzer] = None
        self.current_file: Optional[str] = None
        self.analysis_thread: Optional[threading.Thread] = None

        # Create UI components
        self._create_menu()
        self._create_toolbar()
        self._create_main_content()
        self._create_status_bar()

        # Set up event handlers
        self.root.protocol("WM_DELETE_WINDOW", self._on_closing)

    def _create_menu(self) -> None:
        """Create the menu bar.

        Sets up the application menu with File, Analysis, Tools, and Help menus,
        including keyboard shortcuts and event bindings.

        :returns: None
        :rtype: None
        """
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)

        # File menu
        file_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="File", menu=file_menu)
        file_menu.add_command(
            label="Open PDB File...", accelerator="Ctrl+O", command=self._open_file
        )
        file_menu.add_separator()
        file_menu.add_command(
            label="Save Results...", accelerator="Ctrl+S", command=self._save_results
        )
        file_menu.add_command(
            label="Export All...", accelerator="Ctrl+E", command=self._export_all
        )
        file_menu.add_separator()
        file_menu.add_command(
            label="Exit", accelerator="Ctrl+Q", command=self._on_closing
        )

        # Analysis menu
        analysis_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Analysis", menu=analysis_menu)
        analysis_menu.add_command(
            label="Run Analysis", accelerator="F5", command=self._run_analysis
        )
        analysis_menu.add_command(label="Clear Results", command=self._clear_results)
        analysis_menu.add_separator()
        analysis_menu.add_command(
            label="Reset Parameters", command=self._reset_parameters
        )

        # Tools menu
        tools_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Tools", menu=tools_menu)
        tools_menu.add_command(label="Generate Charts", command=self._generate_charts)
        tools_menu.add_command(
            label="Export Visualization", command=self._export_visualization
        )

        # Help menu
        help_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Help", menu=help_menu)
        help_menu.add_command(label="About", command=self._show_about)
        help_menu.add_command(label="User Guide", command=self._show_help)

        # Bind keyboard shortcuts
        self.root.bind("<Control-o>", lambda e: self._open_file())
        self.root.bind("<Control-s>", lambda e: self._save_results())
        self.root.bind("<Control-e>", lambda e: self._export_all())
        self.root.bind("<Control-q>", lambda e: self._on_closing())
        self.root.bind("<F5>", lambda e: self._run_analysis())

    def _create_toolbar(self) -> None:
        """Create the toolbar.

        Creates a toolbar with buttons for common operations like file opening,
        analysis execution, and result management. Includes a progress bar.

        :returns: None
        :rtype: None
        """
        toolbar = ttk.Frame(self.root)
        toolbar.pack(fill=tk.X, padx=5, pady=2)

        # File operations
        ttk.Button(toolbar, text="Open PDB", command=self._open_file).pack(
            side=tk.LEFT, padx=2
        )
        ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)

        # Analysis operations
        self.run_button = ttk.Button(
            toolbar, text="Run Analysis", command=self._run_analysis, state=tk.DISABLED
        )
        self.run_button.pack(side=tk.LEFT, padx=2)

        ttk.Button(toolbar, text="Clear Results", command=self._clear_results).pack(
            side=tk.LEFT, padx=2
        )
        ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)

        # Export operations
        ttk.Button(toolbar, text="Save Results", command=self._save_results).pack(
            side=tk.LEFT, padx=2
        )

        # Progress bar
        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(
            toolbar, variable=self.progress_var, mode="indeterminate"
        )
        self.progress_bar.pack(side=tk.RIGHT, padx=5, fill=tk.X, expand=True)

    def _create_main_content(self) -> None:
        """Create the main content area.

        Sets up the main interface with a paned window containing file content,
        parameter panels, and results display areas.

        :returns: None
        :rtype: None
        """
        # Create main paned window
        main_paned = ttk.PanedWindow(self.root, orient=tk.HORIZONTAL)
        main_paned.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # Left panel - File content and parameters
        left_frame = ttk.Frame(main_paned)
        main_paned.add(left_frame, weight=1)

        # Create notebook for left panel
        left_notebook = ttk.Notebook(left_frame)
        left_notebook.pack(fill=tk.BOTH, expand=True)

        # File content tab
        file_frame = ttk.Frame(left_notebook)
        left_notebook.add(file_frame, text="PDB File")

        self.file_text = scrolledtext.ScrolledText(
            file_frame, wrap=tk.NONE, font=("Courier", 9)
        )
        self.file_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # Parameters tab
        self.parameter_panel = ParameterPanel(left_notebook)
        left_notebook.add(self.parameter_panel.frame, text="Parameters")

        # Right panel - Results
        right_frame = ttk.Frame(main_paned)
        main_paned.add(right_frame, weight=2)

        self.results_panel = ResultsPanel(right_frame)

        # Set initial pane positions
        main_paned.sashpos(0, GUIDefaults.LEFT_PANEL_WIDTH)

    def _create_status_bar(self) -> None:
        """Create the status bar.

        Creates a status bar at the bottom of the window to display
        application state and progress information.

        :returns: None
        :rtype: None
        """
        self.status_var = tk.StringVar()
        self.status_var.set("Ready")

        status_frame = ttk.Frame(self.root)
        status_frame.pack(fill=tk.X, side=tk.BOTTOM)

        ttk.Label(
            status_frame, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W
        ).pack(fill=tk.X, padx=2, pady=1)

    def _open_file(self) -> None:
        """Open a PDB file.

        Displays a file dialog to select a PDB file, loads its content,
        and enables analysis functionality.

        :returns: None
        :rtype: None
        """
        filename = filedialog.askopenfilename(
            title="Open PDB File",
            filetypes=[("PDB files", "*.pdb"), ("All files", "*.*")],
        )

        if filename:
            try:
                self.current_file = filename
                self._load_file_content(filename)
                self.run_button.config(state=tk.NORMAL)
                self.status_var.set(f"Loaded: {os.path.basename(filename)}")
                self._clear_results()

            except Exception as e:
                messagebox.showerror("Error", f"Failed to load file:\n{str(e)}")
                self.status_var.set("Error loading file")

    def _load_file_content(self, filename: str) -> None:
        """Load and display file content.

        Reads the PDB file content and displays it in the text widget
        with syntax highlighting for PDB record types.

        :param filename: Path to the PDB file to load
        :type filename: str
        :returns: None
        :rtype: None
        :raises Exception: If file cannot be read
        """
        try:
            with open(filename, "r") as file:
                content = file.read()

            self.file_text.delete(1.0, tk.END)
            self.file_text.insert(1.0, content)

            # Highlight ATOM and HETATM lines
            self._highlight_pdb_records()

        except Exception as e:
            raise Exception(f"Cannot read file: {e}")

    def _highlight_pdb_records(self) -> None:
        """Highlight important PDB record types.

        Applies color coding to different PDB record types (ATOM, HETATM,
        HEADER, etc.) for better readability.

        :returns: None
        :rtype: None
        """
        # Configure text tags
        self.file_text.tag_configure("atom", foreground="blue")
        self.file_text.tag_configure("hetatm", foreground="red")
        self.file_text.tag_configure(
            "header", foreground="green", font=("Courier", 9, "bold")
        )

        content = self.file_text.get(1.0, tk.END)
        lines = content.split("\n")

        for i, line in enumerate(lines):
            line_start = f"{i+1}.0"
            line_end = f"{i+1}.end"

            if line.startswith("ATOM"):
                self.file_text.tag_add("atom", line_start, line_end)
            elif line.startswith("HETATM"):
                self.file_text.tag_add("hetatm", line_start, line_end)
            elif line.startswith(("HEADER", "TITLE", "COMPND")):
                self.file_text.tag_add("header", line_start, line_end)

    def _run_analysis(self) -> None:
        """Run the molecular interaction analysis.

        Initiates analysis in a separate thread using current parameters
        and loaded PDB file. Updates UI to show progress.

        :returns: None
        :rtype: None
        """
        if not self.current_file:
            messagebox.showwarning("Warning", "Please open a PDB file first.")
            return

        if self.analysis_thread and self.analysis_thread.is_alive():
            messagebox.showinfo("Info", "Analysis is already running.")
            return

        # Get parameters from the parameter panel
        params = self.parameter_panel.get_parameters()

        # Start analysis in a separate thread
        self.analysis_thread = threading.Thread(
            target=self._perform_analysis, args=(params,)
        )
        self.analysis_thread.daemon = True
        self.analysis_thread.start()

        # Update UI
        self.run_button.config(state=tk.DISABLED)
        self.progress_bar.config(mode="indeterminate")
        self.progress_bar.start(GUIDefaults.PROGRESS_BAR_INTERVAL)
        self.status_var.set("Running analysis...")

    def _perform_analysis(self, params: AnalysisParameters) -> None:
        """Perform the analysis in a separate thread.

        Executes the molecular interaction analysis using the provided
        parameters and updates the UI upon completion or error.

        :param params: Analysis parameters to use
        :type params: AnalysisParameters
        :returns: None
        :rtype: None
        """
        try:
            # Create analyzer
            self.analyzer = HBondAnalyzer(params)

            # Run analysis
            success = self.analyzer.analyze_file(self.current_file)

            if success:
                # Update results on main thread
                self.root.after(0, self._analysis_complete)
            else:
                self.root.after(0, self._analysis_error, "Analysis failed")

        except Exception as e:
            self.root.after(0, self._analysis_error, str(e))

    def _analysis_complete(self) -> None:
        """Handle successful analysis completion.

        Updates the UI after successful analysis, stops progress indication,
        displays results, and shows completion notification.

        :returns: None
        :rtype: None
        """
        self.progress_bar.stop()
        self.progress_bar.config(mode="determinate")
        self.progress_var.set(0)
        self.run_button.config(state=tk.NORMAL)

        # Update results panel
        self.results_panel.update_results(self.analyzer)

        # Update status
        stats = self.analyzer.get_statistics()
        self.status_var.set(
            f"Analysis complete - H-bonds: {stats['hydrogen_bonds']}, "
            f"X-bonds: {stats['halogen_bonds']}, π-interactions: {stats['pi_interactions']}"
        )

        messagebox.showinfo("Success", "Analysis completed successfully!")

    def _analysis_error(self, error_msg: str) -> None:
        """Handle analysis error.

        Updates the UI after analysis failure, stops progress indication,
        and displays error message to the user.

        :param error_msg: Error message to display
        :type error_msg: str
        :returns: None
        :rtype: None
        """
        self.progress_bar.stop()
        self.progress_bar.config(mode="determinate")
        self.progress_var.set(0)
        self.run_button.config(state=tk.NORMAL)
        self.status_var.set("Analysis failed")
        messagebox.showerror("Analysis Error", f"Analysis failed:\n{error_msg}")

    def _clear_results(self) -> None:
        """Clear analysis results.

        Clears all analysis results from the interface and resets
        the analyzer state.

        :returns: None
        :rtype: None
        """
        self.results_panel.clear_results()
        self.analyzer = None
        self.status_var.set("Results cleared")

    def _save_results(self) -> None:
        """Save analysis results to file.

        Displays a file dialog to save analysis results in text format.
        Requires completed analysis to function.

        :returns: None
        :rtype: None
        """
        if not self.analyzer:
            messagebox.showwarning("Warning", "No results to save. Run analysis first.")
            return

        filename = filedialog.asksaveasfilename(
            title="Save Results",
            defaultextension=".txt",
            filetypes=[
                ("Text files", "*.txt"),
                ("CSV files", "*.csv"),
                ("All files", "*.*"),
            ],
        )

        if filename:
            try:
                self._export_results_to_file(filename)
                messagebox.showinfo("Success", f"Results saved to {filename}")
                self.status_var.set(f"Results saved to {os.path.basename(filename)}")
            except Exception as e:
                messagebox.showerror("Error", f"Failed to save results:\n{str(e)}")

    def _export_results_to_file(self, filename: str) -> None:
        """Export results to a file.

        Writes complete analysis results to the specified file in
        human-readable text format.

        :param filename: Path to the output file
        :type filename: str
        :returns: None
        :rtype: None
        """
        with open(filename, "w") as f:
            f.write("HBAT Analysis Results\n")
            f.write("=" * 50 + "\n\n")

            if self.current_file:
                f.write(f"Input file: {self.current_file}\n\n")

            # Write summary
            stats = self.analyzer.get_statistics()
            f.write("Summary:\n")
            f.write(f"  Hydrogen bonds: {stats['hydrogen_bonds']}\n")
            f.write(f"  Halogen bonds: {stats['halogen_bonds']}\n")
            f.write(f"  π interactions: {stats['pi_interactions']}\n")
            f.write(f"  Total interactions: {stats['total_interactions']}\n\n")

            # Write detailed results
            f.write("Hydrogen Bonds:\n")
            f.write("-" * 30 + "\n")
            for hb in self.analyzer.hydrogen_bonds:
                f.write(f"{hb}\n")

            f.write("\nHalogen Bonds:\n")
            f.write("-" * 30 + "\n")
            for xb in self.analyzer.halogen_bonds:
                f.write(f"{xb}\n")

            f.write("\nπ Interactions:\n")
            f.write("-" * 30 + "\n")
            for pi in self.analyzer.pi_interactions:
                f.write(f"{pi}\n")

    def _export_all(self) -> None:
        """Export all results in multiple formats.

        Exports analysis results to a directory in multiple file formats
        for comprehensive data preservation.

        :returns: None
        :rtype: None
        """
        if not self.analyzer:
            messagebox.showwarning(
                "Warning", "No results to export. Run analysis first."
            )
            return

        directory = filedialog.askdirectory(title="Select Export Directory")
        if directory:
            try:
                base_name = (
                    os.path.splitext(os.path.basename(self.current_file))[0]
                    if self.current_file
                    else "hbat_results"
                )

                # Export text summary
                self._export_results_to_file(
                    os.path.join(directory, f"{base_name}_summary.txt")
                )

                messagebox.showinfo("Success", f"Results exported to {directory}")
                self.status_var.set("All results exported")
            except Exception as e:
                messagebox.showerror("Error", f"Failed to export results:\n{str(e)}")

    def _generate_charts(self) -> None:
        """Generate analysis charts.

        Creates visual charts and graphs from analysis results.
        Currently shows placeholder message for future implementation.

        :returns: None
        :rtype: None
        """
        if not self.analyzer:
            messagebox.showwarning(
                "Warning", "No results available. Run analysis first."
            )
            return

        messagebox.showinfo("Info", "Chart generation feature coming soon!")

    def _export_visualization(self) -> None:
        """Export visualization scripts.

        Exports scripts for external visualization tools.
        Currently shows placeholder message for future implementation.

        :returns: None
        :rtype: None
        """
        if not self.analyzer:
            messagebox.showwarning(
                "Warning", "No results available. Run analysis first."
            )
            return

        messagebox.showinfo("Info", "Visualization export feature coming soon!")

    def _reset_parameters(self) -> None:
        """Reset analysis parameters to defaults.

        Restores all analysis parameters to their default values
        as defined in the application constants.

        :returns: None
        :rtype: None
        """
        self.parameter_panel.reset_to_defaults()
        self.status_var.set("Parameters reset to defaults")

    def _show_about(self) -> None:
        """Show about dialog.

        Displays application information including version, authors,
        and institutional affiliation.

        :returns: None
        :rtype: None
        """
        about_text = """
HBAT - Hydrogen Bond Analysis Tool v2.0

A comprehensive tool for analyzing hydrogen bonds, halogen bonds, 
and X-H...π interactions in protein structures.

Original Authors:
Abhishek Tiwari & Sunil Kumar Panigrahi

Guide & Project Head:
Prof Gautam R. Desiraju
School of Chemistry, University of Hyderabad

Python conversion: 2025
        """
        messagebox.showinfo("About HBAT", about_text.strip())

    def _show_help(self) -> None:
        """Show help dialog.

        Displays basic usage instructions and guidance for using
        the HBAT GUI application.

        :returns: None
        :rtype: None
        """
        help_text = """
HBAT User Guide

1. Open a PDB file using File > Open PDB File
2. Adjust analysis parameters in the Parameters tab
3. Click 'Run Analysis' or press F5 to start analysis
4. View results in the Results panel
5. Save or export results using the File menu

For more detailed information, please refer to the documentation.
        """
        messagebox.showinfo("Help", help_text.strip())

    def _on_closing(self) -> None:
        """Handle window closing event.

        Handles application shutdown, checking for running analysis
        and prompting user confirmation if needed.

        :returns: None
        :rtype: None
        """
        if self.analysis_thread and self.analysis_thread.is_alive():
            result = messagebox.askyesno(
                "Confirm Exit", "Analysis is running. Are you sure you want to exit?"
            )
            if not result:
                return

        self.root.destroy()

    def run(self) -> None:
        """Start the GUI application.

        Enters the main GUI event loop to begin accepting user interactions.
        This method blocks until the application is closed.

        :returns: None
        :rtype: None
        """
        self.root.mainloop()
