import os
import re
import platform
import tkinter as tk
import tkinter.filedialog

from tklib.tkutils import add_path, del_quote, search_executable_path
from tklib.tkutils import get_user_inifile_dir, desktop_name
from tklib.tkgui import tktkinter
from tkLauncherApp import tkLauncherApp


class tkLScriptApp(tkLauncherApp):
    """
    Hidden-Tk runner for Launcher scripts.

    Design goals:
    - keep Boot.ini compatibility
    - keep dialog commands usable
    - remove Launcher main-window/menu/button construction
    - reuse existing tkScript interpreter
    """

    def __init__(self, usage_str="", globals=None, locals=None):
        if platform.system() == "Windows":
            user_ini_dir = get_user_inifile_dir(env_vars=[], dir_names=["launcher"])
        else:
            user_ini_dir = get_user_inifile_dir(env_vars=["HOME"], dir_names=[".launcher", "launcher"])

        super().__init__(
            usage_str=usage_str,
            globals=globals or {},
            locals=locals or {},
            use_user_inifile=True,
            inifile_dirs=[user_ini_dir, "../../user", "user"],
            create_inidir=False,
        )

        try:
            self.root_window.withdraw()
        except Exception:
            pass

        self.window_title = "lscript"
        self.cur_sel = 0
        self.last_menu = ""
        self.last_ibutton = 0
        self.last_button_type = ""
        self.last_button_action = ""
        self.extra_scripts = []
        self.remove_scripts = []

        tkvars = self.tkvars.get_param_dict()
        if "sel_file" not in tkvars:
            tkvars["sel_file"] = tk.StringVar(master=self.root_window, value="")
        if "argument" not in tkvars:
            tkvars["argument"] = tk.StringVar(master=self.root_window, value="")
        if "submenu_buttons" not in tkvars:
            tkvars["submenu_buttons"] = []
        if "rbuttons" not in tkvars:
            tkvars["rbuttons"] = []
        if "tbuttons" not in tkvars:
            tkvars["tbuttons"] = []

        self._message_text = ""
        self._command_line = ""
        self._command_line2 = ""

        self._initialize_minimal_paths_and_config()

    def _initialize_minimal_paths_and_config(self):
        config = self.config
        self.env = os.environ

        v = self.env.get("tkProg_Root", None)
        if v:
            self.tkProg_Root = v
        else:
            self.tkProg_Root = os.path.abspath(os.path.join(self.script_dir, "../.."))

        os_name = platform.system()
        self.use_tooltip = os_name != "Linux"

        self.bin_path = os.path.join(self.tkProg_Root, "bin")
        self.tklib_Root = os.path.join(self.tkProg_Root, "tklib")
        self.pythonlib_path = os.environ.get("tklibPath", "")
        if not self.pythonlib_path:
            self.pythonlib_path = os.path.abspath(os.path.join(self.tkProg_Root, "tklib", "python"))
        self.perllib_path = os.path.join(self.tklib_Root, "Perl", "lib")
        self.tkprog_path = os.path.join(self.tkProg_Root, "tkprog")
        self.tkprog_X_path = os.path.abspath(os.path.join(self.script_dir, ".."))

        if os_name == "Windows":
            self.tkapp_path = os.path.join(self.tkProg_Root, "tkapp_Win")
        elif os_name == "Darwin":
            self.tkapp_path = os.path.join(self.tkProg_Root, "tkapp_Mac")
        else:
            self.tkapp_path = os.path.join(self.tkProg_Root, "tkapp_Linux")

        self.tkapp_open_path = os.path.join(self.tkProg_Root, "tkapp_open")
        self.tkapp_etc_path = os.path.join(self.tkProg_Root, "tkapp_etc")
        self.launcher_script_dir = os.path.join(self.script_dir, "scripts")
        self.internal_editor_path = os.path.join(self.tkprog_X_path, "editor", "editor.py")
        self.resource_dir = os.path.join(self.script_dir, "resource")

        add_path(self.bin_path)

        config.start_path = config.get("start_path", "") or ""
        config.shell_path = config.get("shell_path", "") or ""
        config.filer_path = config.get("filer_path", "") or ""
        config.editor_path = config.get("editor_path", "") or ""

        config.python3_path = config.get("python3_path", "") or search_executable_path(
            ["python3.exe", "python3", "python.exe", "python"], filename_only=True
        )
        config.python_path = config.python3_path
        config.pythonw_path = config.get("pythonw_path", "") or search_executable_path(
            ["pythonw.exe", "pythonw"], filename_only=True
        )
        config.perl_path = config.get("perl_path", "") or search_executable_path(
            ["perl", "perl.exe"], filename_only=True
        )

        config.vesta_path = config.get("vesta_path", "")
        if config.vesta_path == "":
            d = os.path.join(self.tkapp_path, "VESTA")
            if os.path.exists(d):
                config.vesta_path = os.path.join(d, "VESTA")

        config.lang = "en"
        config.debug = False
        config.print_level = 0
        config.confirm = False
        config.confirm_on_exit = False
        config.show_log = False

        for key in [
            "tkProg_Root", "tklib_Root", "pythonlib_path", "perllib_path",
            "tkprog_path", "tkprog_X_path", "internal_editor_path",
            "tkapp_path", "tkapp_open_path", "tkapp_etc_path",
            "script_dir", "script_path", "script_fullpath", "launcher_script_dir"
        ]:
            val = getattr(self, key, "")
            if val is not None:
                self.env[key] = str(val)

    def load_config_like_launcher(self):
        config = self.config
        try:
            config.read_parameters(path=config.inifile)
        except Exception:
            pass

        try:
            config.python_path = config.python3_path
        except Exception:
            pass

        editor_path = config.get("editor_path", "")
        if isinstance(editor_path, str) and re.search(r"code(\.exe)?$", editor_path):
            config.editor_path = "code"

        for key in list(config.__dict__.keys()):
            if "_path" in key:
                val = config.get(key, "")
                if val is not None:
                    self.env[key] = str(val)

    def set_var(self, var, val, set_env=False):
        var_l = var.lower()
        self.s_engine.vars[var_l] = val
        self.svars.get_param_dict()[var] = val
        if set_env:
            os.environ[var] = str(val)

    def show_message(self, message):
        self._message_text = str(message)
        self.print_warning(str(message))

    def set_message(self, message):
        self._message_text = str(message)
        self.print_warning(str(message))

    def set_command_line(self, cmd):
        self._command_line = str(cmd)
        self.print_warning(str(cmd))

    def set_command_line2(self, cmd):
        self._command_line2 = str(cmd)
        self.print_warning(str(cmd))

    def on_closing(self, do_confirm=False):
        try:
            self.root_window.quit()
            self.root_window.destroy()
        except Exception:
            pass
        return 0

    def show_message_dialog(self, message):
        message = del_quote(message)
        tktkinter.dialog_showinfo(self, title="Information", message=message)

    def show_error_dialog(self, message):
        message = del_quote(message)
        tktkinter.dialog_showerror(self, title="Error", message=message)

    def ask_yesno_dialog(self, message):
        message = del_quote(message)
        ret = tktkinter.dialog_yesno(self, title="Question", message=message)
        if not ret:
            raise RuntimeError("Cancelled by ask_yesno_dialog")
        return ret

    def ask_okcancel_dialog(self, message):
        message = del_quote(message)
        ret = tktkinter.dialog_okcancel(self, title="Question", message=message)
        if not ret:
            raise RuntimeError("Cancelled by ask_okcancel_dialog")
        return ret

    def get_open_file_name(self, args, allow_cancel=False):
        file_mask = args[0] if len(args) >= 1 else "*.*"
        initialdir = args[1] if len(args) >= 2 else "."
        title = args[2] if len(args) >= 3 else "Choose file"
        sel = tkinter.filedialog.askopenfilename(
            filetypes=[("Specified", file_mask), ("All", "*.*")],
            initialdir=initialdir,
            title=title,
        )
        if not sel and not allow_cancel:
            raise RuntimeError("Cancelled by get_open_file_name")
        self.tkvars.get_param_dict()["sel_file"].set(sel or "")
        self.set_var("o", sel or "", set_env=False)
        return sel or ""

    def get_save_file_name(self, args, allow_cancel=False):
        file_mask = args[0] if len(args) >= 1 else "*.*"
        initialdir = args[1] if len(args) >= 2 else "."
        title = args[2] if len(args) >= 3 else "Save file"
        sel = tkinter.filedialog.asksaveasfilename(
            filetypes=[("Specified", file_mask), ("All", "*.*")],
            initialdir=initialdir,
            title=title,
        )
        if not sel and not allow_cancel:
            raise RuntimeError("Cancelled by get_save_file_name")
        self.tkvars.get_param_dict()["sel_file"].set(sel or "")
        self.set_var("o", sel or "", set_env=False)
        return sel or ""

    def get_open_dir_name(self, args, allow_cancel=False):
        initialdir = args[0] if len(args) >= 1 else "."
        title = args[1] if len(args) >= 2 else "Choose directory"
        sel = tkinter.filedialog.askdirectory(initialdir=initialdir, title=title)
        if not sel and not allow_cancel:
            raise RuntimeError("Cancelled by get_open_dir_name")
        self.tkvars.get_param_dict()["sel_file"].set(sel or "")
        self.set_var("o", sel or "", set_env=False)
        return sel or ""

    def run_boot_sections(self, script_files=None):
        script_files = script_files or self.s_engine.script_files
        cparams = self.get_params()

        def _safe_exec(section):
            try:
                self.s_engine.execute(self, cparams, section, idx=None, action=None, i_mouse_button="1", script_files=script_files)
            except Exception as e:
                self.print_warning(f"Warning: boot section {section} failed: {e}")

        _safe_exec("[Boot]")

        os_name = platform.system()
        _safe_exec(f"[Boot-{os_name}]")

        try:
            desktop = desktop_name()
            if desktop:
                _safe_exec(f"[Boot-{desktop}]")
        except Exception:
            pass
