import subprocess

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject

from sublime.server import Server
from sublime.config import ServerConfiguration
from sublime.ui.common import EditFormDialog, IconButton


class EditServerDialog(EditFormDialog):
    entity_name: str = 'Server'
    initial_size = (450, 250)
    text_fields = [
        ('Name', 'name', False),
        ('Server address', 'server_address', False),
        ('Local network address', 'local_network_address', False),
        ('Local network SSID', 'local_network_ssid', False),
        ('Username', 'username', False),
        ('Password', 'password', True),
    ]
    boolean_fields = [
        ('Browse by tags', 'browse_by_tags'),
        ('Sync enabled', 'sync_enabled'),
        ('Do not verify certificate', 'disable_cert_verify'),
    ]

    def __init__(self, *args, **kwargs):
        test_server = Gtk.Button(label='Test Connection to Server')
        test_server.connect('clicked', self.on_test_server_clicked)

        open_in_browser = Gtk.Button(label='Open in Browser')
        open_in_browser.connect('clicked', self.on_open_in_browser_clicked)

        self.extra_buttons = [(test_server, None), (open_in_browser, None)]

        super().__init__(*args, **kwargs)

    def on_test_server_clicked(self, event):
        # Instantiate the server.
        server_address = self.data['server_address'].get_text()
        server = Server(
            name=self.data['name'].get_text(),
            hostname=server_address,
            username=self.data['username'].get_text(),
            password=self.data['password'].get_text(),
            disable_cert_verify=self.data['disable_cert_verify'].get_active(),
        )

        # Try to ping, and show a message box with whether or not it worked.
        try:
            server.ping()
            dialog = Gtk.MessageDialog(
                transient_for=self,
                message_type=Gtk.MessageType.INFO,
                buttons=Gtk.ButtonsType.OK,
                text='Connection to server successful.',
            )
            dialog.format_secondary_markup(
                f'Connection to {server_address} successful.')
        except Exception as err:
            dialog = Gtk.MessageDialog(
                transient_for=self,
                message_type=Gtk.MessageType.ERROR,
                buttons=Gtk.ButtonsType.OK,
                text='Connection to server unsuccessful.',
            )
            dialog.format_secondary_markup(
                f'Connection to {server_address} resulted in the following '
                f'error:\n\n{err}')

        dialog.run()
        dialog.destroy()

    def on_open_in_browser_clicked(self, event):
        subprocess.call(['xdg-open', self.data['server_address'].get_text()])


class ConfigureServersDialog(Gtk.Dialog):
    __gsignals__ = {
        'server-list-changed':
        (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (object, )),
        'connected-server-changed':
        (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (object, )),
    }

    def __init__(self, parent, config):
        Gtk.Dialog.__init__(
            self,
            title='Configure Servers',
            transient_for=parent,
            flags=0,
            add_buttons=(),
        )

        self.server_configs = config.servers
        self.selected_server_index = config.current_server
        self.set_default_size(500, 300)

        # Flow box to hold the server list and the buttons.
        flowbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)

        # Server List
        self.server_list = Gtk.ListBox(activate_on_single_click=False)
        self.server_list.connect(
            'selected-rows-changed', self.server_list_on_selected_rows_changed)
        self.server_list.connect('row-activated', self.on_server_list_activate)
        flowbox.pack_start(self.server_list, True, True, 10)

        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        # Tuples: (button, action function, pack side, requires_selection)
        # Add all of the buttons to the button box.
        self.buttons = [
            (
                IconButton(
                    'document-edit-symbolic',
                    label='Edit...',
                    relief=True,
                ), lambda e: self.on_edit_clicked(e, False), 'start', True),
            (
                IconButton(
                    'list-add-symbolic',
                    label='Add...',
                    relief=True,
                ), lambda e: self.on_edit_clicked(e, True), 'start', False),
            (
                IconButton(
                    'list-remove-symbolic',
                    label='Remove',
                    relief=True,
                ), self.on_remove_clicked, 'start', True),
            (
                IconButton(
                    'window-close-symbolic',
                    label='Close',
                    relief=True,
                ), lambda _: self.close(), 'end', False),
            (
                IconButton(
                    'network-transmit-receive-symbolic',
                    label='Connect',
                    relief=True,
                ), self.on_connect_clicked, 'end', True),
        ]
        for button_cfg in self.buttons:
            btn, action, pack_end, requires_selection = button_cfg

            if pack_end == 'end':
                button_box.pack_end(btn, False, True, 5)
            else:
                button_box.pack_start(btn, False, True, 5)

            btn.connect('clicked', action)

        flowbox.pack_end(button_box, False, False, 0)

        # Add the flowbox to the dialog and show the dialog.
        content_area = self.get_content_area()
        content_area.pack_start(flowbox, True, True, 10)

        self.show_all()
        self.refresh_server_list()
        self.server_list_on_selected_rows_changed(None)

    def refresh_server_list(self):
        # Clear out the list.
        for el in self.server_list:
            self.server_list.remove(el)

        # Add all of the rows for each of the servers.
        for i, config in enumerate(self.server_configs):
            box = Gtk.Box()
            image = Gtk.Image(margin=5)
            if i == self.selected_server_index:
                image.set_from_icon_name(
                    'network-transmit-receive-symbolic',
                    Gtk.IconSize.SMALL_TOOLBAR,
                )

            box.add(image)

            server_name_label = Gtk.Label(label=config.name)
            server_name_label.set_halign(Gtk.Align.START)
            box.add(server_name_label)
            self.server_list.add(box)

        # Show them, and select the current server.
        self.show_all()
        if (self.selected_server_index is not None
                and self.selected_server_index >= 0):
            self.server_list.select_row(
                self.server_list.get_row_at_index(self.selected_server_index))

    def on_remove_clicked(self, event):
        selected = self.server_list.get_selected_row()
        if selected:
            del self.server_configs[selected.get_index()]
            self.refresh_server_list()
            self.emit('server-list-changed', self.server_configs)

    def on_edit_clicked(self, event, add):
        if add:
            dialog = EditServerDialog(self)
        else:
            selected_index = self.server_list.get_selected_row().get_index()
            dialog = EditServerDialog(
                self, self.server_configs[selected_index])

        result = dialog.run()
        if result == Gtk.ResponseType.OK:
            # Create a new server configuration to use.
            new_config = ServerConfiguration(
                name=dialog.data['name'].get_text(),
                server_address=dialog.data['server_address'].get_text(),
                local_network_address=dialog.data['local_network_address']
                .get_text(),
                local_network_ssid=dialog.data['local_network_ssid'].get_text(
                ),
                username=dialog.data['username'].get_text(),
                password=dialog.data['password'].get_text(),
                browse_by_tags=dialog.data['browse_by_tags'].get_active(),
                sync_enabled=dialog.data['sync_enabled'].get_active(),
                disable_cert_verify=dialog.data['disable_cert_verify']
                .get_active(),
            )

            if add:
                self.server_configs.append(new_config)
            else:
                self.server_configs[selected_index] = new_config

            self.refresh_server_list()
            self.emit('server-list-changed', self.server_configs)

        dialog.destroy()

    def on_server_list_activate(self, *args):
        self.on_connect_clicked(None)

    def on_connect_clicked(self, event):
        selected_index = self.server_list.get_selected_row().get_index()
        self.emit('connected-server-changed', selected_index)
        self.close()

    def server_list_on_selected_rows_changed(self, event):
        # Update the state of the buttons depending on whether or not a row is
        # selected in the server list.
        has_selection = self.server_list.get_selected_row()

        for button, *_, requires_selection in self.buttons:
            button.set_sensitive(not requires_selection or has_selection)
