from gettext import gettext as _

from gi.repository import Gtk, Gio, GLib

from uberwriter import headerbars
from uberwriter.settings import Settings
from uberwriter.styled_window import StyledWindow


class PreviewRenderer:
    """Renders the preview according to the user selected mode."""

    # Must match the order/index defined in gschema.xml
    FULL_WIDTH = 0
    HALF_WIDTH = 1
    HALF_HEIGHT = 2
    WINDOWED = 3

    def __init__(
            self, main_window, content, editor, text_view, preview, mode_revealer, mode_button):
        self.main_window = main_window
        self.main_window.connect("delete-event", self.on_window_closed)
        self.content = content
        self.editor = editor
        self.text_view = text_view
        self.preview = preview
        self.mode_revealer = mode_revealer
        self.mode_button = mode_button
        self.mode_button.connect("clicked", self.show_mode_popover)

        self.settings = Settings.new()
        self.popover = None
        self.window = None
        self.headerbar = None

        self.mode = self.settings.get_enum("preview-mode")
        self.update_mode()

    def show(self, web_view):
        """Show the preview, depending on the currently selected mode."""

        # Windowed preview: create a window and show the preview in it.
        if self.mode == self.WINDOWED:
            # Create transient window of the main window.
            self.window = StyledWindow(application=self.main_window.get_application())
            self.window.connect("delete-event", self.on_window_closed)

            # Create a custom header bar and move the mode button there.
            headerbar = headerbars.PreviewHeaderbar()
            self.headerbar = headerbar.hb
            self.headerbar.set_title(_("Preview"))
            self.mode_button.get_style_context().remove_class("inline-button")
            self.mode_revealer.remove(self.mode_button)
            self.headerbar.pack_end(self.mode_button)
            self.window.set_titlebar(headerbar.hb_container)

            # Position it next to the main window.
            width, height = self.main_window.get_size()
            self.window.resize(width, height)
            x, y = self.main_window.get_position()
            if x is not None and y is not None:
                self.main_window.move(x, y)
                self.window.move(x + width + 16, y)

            # Add webview and show.
            self.window.add(web_view)
            self.window.show()

        else:
            self.preview.pack_start(web_view, True, True, 0)
            self.content.add(self.preview)

            # Full-width preview: swap editor with preview.
            if self.mode == self.FULL_WIDTH:
                self.content.remove(self.editor)

            # Half-width preview: set horizontal orientation and add the preview.
            # Ask for a minimum width that respects the editor's minimum requirements.
            elif self.mode == self.HALF_WIDTH:
                self.content.set_orientation(Gtk.Orientation.HORIZONTAL)
                self.content.set_size_request(self.text_view.get_min_width() * 2, -1)

            # Half-height preview: set vertical orientation and add the preview.
            # Ask for a minimum height that provides a comfortable experience.
            elif self.mode == self.HALF_HEIGHT:
                self.content.set_orientation(Gtk.Orientation.VERTICAL)
                self.content.set_size_request(-1, 768)

            else:
                raise ValueError("Unknown preview mode {}".format(self.mode))

        web_view.show()

    def hide(self, web_view):
        """Hide the preview, depending on the currently selected mode."""

        # Windowed preview: remove preview and destroy window.
        if self.mode == self.WINDOWED:
            self.main_window.present()
            self.headerbar.remove(self.mode_button)
            self.mode_button.get_style_context().add_class("inline-button")
            self.mode_revealer.add(self.mode_button)
            self.headerbar = None
            self.window.remove(web_view)
            self.window.destroy()
            self.window = None

        else:
            self.preview.remove(web_view)
            self.content.remove(self.preview)

            # Full-width preview: swap preview with editor.
            if self.mode == self.FULL_WIDTH:
                self.content.add(self.editor)

            # Half-width/height previews: remove preview and reset size requirements.
            elif self.mode == self.HALF_WIDTH or self.mode == self.HALF_HEIGHT:
                self.content.set_size_request(-1, -1)

            else:
                raise ValueError("Unknown preview mode {}".format(self.mode))

    def update_mode(self, web_view=None):
        """Update preview mode, adjusting the mode button and the preview itself."""

        mode = self.settings.get_enum("preview-mode")
        if web_view and mode != self.mode:
            self.hide(web_view)
            self.mode = mode
            self.show(web_view)
        else:
            self.mode = mode
        if self.mode_button:
            text = self.get_text_for_preview_mode(self.mode)
            self.mode_button.set_label(text)
        if self.popover:
            self.popover.popdown()

    def show_mode_popover(self, button):
        """Show preview mode popover."""

        self.mode_button.set_state_flags(Gtk.StateFlags.CHECKED, False)

        menu = Gio.Menu()
        modes = self.settings.props.settings_schema.get_key("preview-mode").get_range()[1]
        for i, mode in enumerate(modes):
            menu_item = Gio.MenuItem.new(self.get_text_for_preview_mode(i), None)
            menu_item.set_action_and_target_value("app.preview_mode", GLib.Variant.new_string(mode))
            menu.append_item(menu_item)
        self.popover = Gtk.Popover.new_from_model(button, menu)
        self.popover.connect('closed', self.on_popover_closed)
        self.popover.popup()

    def on_popover_closed(self, _popover):
        self.mode_button.unset_state_flags(Gtk.StateFlags.CHECKED)

        self.popover = None
        self.text_view.grab_focus()

    def on_window_closed(self, window, _event):
        preview_action = window.get_application().lookup_action("preview")
        preview_action.change_state(GLib.Variant.new_boolean(False))

    def get_text_for_preview_mode(self, mode):
        if mode == self.FULL_WIDTH:
            return _("Full-Width")
        elif mode == self.HALF_WIDTH:
            return _("Half-Width")
        elif mode == self.HALF_HEIGHT:
            return _("Half-Height")
        elif mode == self.WINDOWED:
            return _("Windowed")
        else:
            raise ValueError("Unknown preview mode {}".format(mode))
