import tkinter as tk
from tkinter import ttk

from thonny import get_runner, get_workbench
from thonny.config_ui import ConfigurationPage
from thonny.ui_utils import create_string_var


class BackendDetailsConfigPage(ConfigurationPage):
    def should_restart(self):
        raise NotImplementedError()


class OnlyTextConfigurationPage(BackendDetailsConfigPage):
    def __init__(self, master, text):
        super().__init__(master)
        label = ttk.Label(self, text=text)
        label.grid()

    def should_restart(self):
        return False


class BackendConfigurationPage(ConfigurationPage):
    def __init__(self, master):
        ConfigurationPage.__init__(self, master)

        self._backend_specs_by_desc = {
            spec.description: spec for spec in get_workbench().get_backends().values()
        }
        self._conf_pages = {}
        self._current_page = None

        current_backend_name = get_workbench().get_option("run.backend_name")
        try:
            current_backend_desc = get_workbench().get_backends()[current_backend_name].description
        except KeyError:
            current_backend_desc = ""

        self._combo_variable = create_string_var(current_backend_desc)

        label = ttk.Label(
            self, text=_("Which interpreter or device should Thonny use for running your code?")
        )
        label.grid(row=0, column=0, columnspan=2, sticky=tk.W)

        sorted_backend_specs = sorted(
            self._backend_specs_by_desc.values(), key=lambda x: x.sort_key
        )

        self._combo = ttk.Combobox(
            self,
            exportselection=False,
            textvariable=self._combo_variable,
            values=[spec.description for spec in sorted_backend_specs],
        )

        self._combo.grid(row=1, column=0, columnspan=2, sticky=tk.NSEW, pady=(0, 10))
        self._combo.state(["!disabled", "readonly"])

        self.labelframe = ttk.LabelFrame(self, text=" " + _("Details") + " ")
        self.labelframe.grid(row=2, column=0, sticky="nsew")
        self.labelframe.columnconfigure(0, weight=1)
        self.labelframe.rowconfigure(0, weight=1)

        self.columnconfigure(0, weight=1)
        self.rowconfigure(2, weight=1)

        self._combo_variable.trace("w", self._backend_changed)
        self._backend_changed()

    def _backend_changed(self, *args):
        backend_desc = self._combo_variable.get()

        if backend_desc == "":
            if self._current_page is not None:
                self._current_page.grid_forget()
            return

        page = self._get_conf_page(backend_desc)

        if page != self._current_page:

            if self._current_page is not None:
                self._current_page.grid_forget()

            page.grid(sticky="nsew", padx=10, pady=5)
            self._current_page = page

    def _get_conf_page(self, backend_desc):
        if backend_desc not in self._conf_pages:
            cp_constructor = self._backend_specs_by_desc[backend_desc].config_page_constructor
            if isinstance(cp_constructor, str):
                self._conf_pages[backend_desc] = OnlyTextConfigurationPage(
                    self.labelframe, cp_constructor
                )
            else:
                assert issubclass(cp_constructor, ConfigurationPage)
                self._conf_pages[backend_desc] = cp_constructor(self.labelframe)

        return self._conf_pages[backend_desc]

    def apply(self):
        if self._current_page is None:
            return None

        result = self._current_page.apply()

        if result is False:
            return False

        backend_desc = self._combo_variable.get()
        backend_name = self._backend_specs_by_desc[backend_desc].name
        get_workbench().set_option("run.backend_name", backend_name)

        if getattr(self._combo_variable, "modified") or self._current_page.should_restart():
            get_runner().restart_backend(False)

        return None


def load_plugin() -> None:
    def select_device():
        get_workbench().show_options("interpreter")

    get_workbench().add_configuration_page(
        "interpreter", _("Interpreter"), BackendConfigurationPage, 20
    )
    get_workbench().add_command(
        "select_interpreter", "run", _("Select interpreter") + "...", select_device, group=1
    )
