# Part of Carburetor project
# Released under GPLv3+ License
# Danial Behzadi<dani.behzi@ubuntu.com>, 2020-2024.

"""
handle ui related stuff
"""

from gi.repository import Adw, Gdk, Gio, Gtk
from tractor import checks, control, db

from . import handler


class Builder(Gtk.Builder):
    """
    Make builder singleton
    """

    _instance = None

    def __new__(cls, *argv, **kwargs):
        if not cls._instance:
            cls._instance = super().__new__(cls, *argv, **kwargs)
        return cls._instance

    def __init__(self):
        if not hasattr(self, "_initialized"):
            super().__init__(scope_object_or_map=handler)
            prefix = "/io/frama/tractor/carburetor/gtk/"
            self.add_from_resource(prefix + "main.ui")
            self.add_from_resource(prefix + "logs.ui")
            self.add_from_resource(prefix + "preferences.ui")
            self.add_from_resource(prefix + "add-bridge.ui")
            self._initialized = True


def get(obj: str):
    """
    get object from ui
    """
    builder = Builder()
    return builder.get_object(obj)


def css() -> None:
    """
    apply css to ui
    """
    css_provider = Gtk.CssProvider()
    prefix = "/io/frama/tractor/carburetor/gtk/"
    css_provider.load_from_resource(prefix + "style.css")
    display = Gdk.Display.get_default()
    Gtk.StyleContext.add_provider_for_display(
        display, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER
    )


def notify(text: str) -> None:
    """
    show toast
    """
    overlay = get("ToastOverlay")
    toast = Adw.Toast()
    toast.set_title(text)
    overlay.add_toast(toast)


def set_main_icon(state: str) -> None:
    """
    Set the main window icon
    """
    page = get("MainPage")
    spinner_paintable = Adw.SpinnerPaintable()
    page.set_paintable(spinner_paintable)
    match state:
        case "load":
            spinner_paintable.set_widget(page)
        case "dead":
            page.set_icon_name("process-stop-symbolic")
        case "stop":
            page.set_icon_name("channel-insecure-symbolic")
        case "run":
            page.set_icon_name("channel-secure-symbolic")


def set_description(text: str) -> None:
    """
    set description on main page
    """
    page = get("MainPage")
    page.set_description(text)


def set_progress_bar(percentage: int) -> None:
    """
    set progressbar percentage
    """
    progress_bar = get("ProgressBar")
    fraction = float(percentage) / 100
    progress_bar.set_fraction(fraction)


def add_to_terminal(line: str) -> None:
    """
    add line to termianl in sidebar overlay
    """
    terminal_text = get("LogsTextView")
    buffer = terminal_text.get_buffer()
    buffer.insert(buffer.get_end_iter(), f"\n{line}\n")


def set_to_stopped() -> None:
    """
    set status to stopped
    """
    page = get("MainPage")
    set_main_icon("stop")
    page.set_title(_("Stopped"))
    page.set_description(_('Press the "Connect" button to start\n'))
    button = get("ConnectButton")
    text_start = _("_Connect")
    style = button.get_style_context()
    style.remove_class("destructive-action")
    style.add_class("suggested-action")
    button.set_label(text_start)
    button.set_action_name("app.connect")
    button = get("NewIDButton")
    button.set_visible(False)
    button = get("CheckConnectionButton")
    button.set_visible(False)
    window = get("MainWindow")
    window.set_hide_on_close(False)
    dconf = Gio.Settings.new("org.tractor")
    dconf.reset("PID")


def set_to_running(app) -> None:
    """
    set status to connected
    """
    page = get("MainPage")
    get_listener = control.get_listener
    set_main_icon("run")
    page.set_title(_("Running"))
    socks_port = _("Socks Port")
    dns_port = _("DNS Port")
    http_port = _("HTTP Port")
    try:
        page.set_description(
            f"{socks_port}: {get_listener('socks')[1]}\n"
            f"{dns_port}: {get_listener('dns')[1]}\n"
            f"{http_port}: {get_listener('httptunnel')[1]}"
        )
    except ValueError:
        set_to_stopped()
        return None
    button = get("ConnectButton")
    text_stop = _("_Disconnect")
    style = button.get_style_context()
    style.remove_class("suggested-action")
    style.add_class("destructive-action")
    button.set_label(text_stop)
    button.set_action_name("app.connect")
    button = get("NewIDButton")
    button.set_visible(True)
    button = get("CheckConnectionButton")
    button.set_visible(True)
    window = get("MainWindow")
    window.set_hide_on_close(True)
    # Show notification if window is not focused
    window = get("MainWindow")
    if not (window.is_active() and window.is_visible()):
        notification = Gio.Notification()
        notification.set_category("network.connected")
        notification.set_default_action("app.show")
        notification.set_title(_("Carburetor is connected"))
        if db.get_val("auto-set"):
            notification.set_body(_("Proxy has been set too"))
        else:
            notification.set_body(
                _("You may want to use the ports or toggle the proxy now")
            )
            notification.add_button(_("Toggle Proxy"), "app.toggle-proxy")
        app.send_notification("connected", notification)
    return None


def set_run_status(app) -> None:
    """
    set status of conection
    """
    if checks.running():
        set_to_running(app)
    else:
        set_to_stopped()
    set_progress_bar(0)


def set_pluginrow_sensivity(row=None) -> None:
    """
    set row sensitive if a pluggable transport is set
    """
    if not row:
        row = get("PluginRow")
    bridgetype = db.get_val("bridge-type")
    if bridgetype not in ["none", "vanilla"]:
        row.set_sensitive(True)
    else:
        row.set_sensitive(False)


def setup_pluginbutton(button=None) -> None:
    """
    set plugin button label and chooser
    """
    if not button:
        button = get("PluginButton")
    chooser = get("PluginChooser")
    filename = Gio.File.new_for_path(db.get_val("pluggable-transport"))
    if filename.query_exists():
        basename = filename.get_basename()
        chooser.set_initial_file(filename)
    else:
        basename = _("None")
    button.set_label(basename)
    button.chooser = chooser


def find_parent(widget, class_name):
    """
    return first parent of a widget that is an instance of the class.
    """
    parent = widget.get_parent()
    while parent:
        if isinstance(parent, class_name):
            return parent
        parent = parent.get_parent()
    return None


def bind_window_settings(window: Adw.ApplicationWindow) -> None:
    """
    Bind window to settings from gsettings
    """
    window.settings = Gio.Settings(schema_id="io.frama.tractor.carburetor")
    window.settings.bind(
        "width", window, "default-width", Gio.SettingsBindFlags.DEFAULT
    )
    window.settings.bind(
        "height", window, "default-height", Gio.SettingsBindFlags.DEFAULT
    )
    window.settings.bind(
        "is-maximized", window, "maximized", Gio.SettingsBindFlags.DEFAULT
    )
    window.settings.bind(
        "is-fullscreen", window, "fullscreened", Gio.SettingsBindFlags.DEFAULT
    )
