from typing import Any, Callable, Dict, Optional, Set, Tuple

from gi.repository import Gdk, GdkPixbuf, GLib, GObject, Gtk, Pango, Handy

from .. import util
from ..common import IconButton, IconToggleButton, SpinnerImage, SpinnerPicture
from ...config import AppConfiguration
from ...util import resolve_path
from . import common

class MobileHandle(Gtk.EventBox):
    def __init__(self, state):
        super().__init__(above_child=False)
        self.get_style_context().add_class("background")

        self.state = state
        self.state.add_control(self)

        action_bar = Gtk.ActionBar()

        action_bar.pack_start(self.create_song_display())

        buttons = Gtk.Stack(transition_type=Gtk.StackTransitionType.CROSSFADE)

        close_buttons = Handy.Squeezer(halign=Gtk.Align.END, homogeneous=False)

        expanded_close_buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        expanded_close_buttons.pack_start(common.create_prev_button(self.state), False, False, 5)
        expanded_close_buttons.pack_start(common.create_play_button(self.state, large=False), False, False, 5)
        expanded_close_buttons.pack_start(common.create_next_button(self.state), False, False, 5)

        close_buttons.add(expanded_close_buttons)
        close_buttons.add(common.create_play_button(self.state, large=False, halign=Gtk.Align.END))

        buttons.add(close_buttons)

        open_buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, halign=Gtk.Align.END)

        self.play_queue_button = IconToggleButton(
            "view-list-symbolic",
            "Open play queue",
            relief=True,
            icon_size=Gtk.IconSize.LARGE_TOOLBAR,
        )

        def play_queue_button_toggled(*_):
            if self.play_queue_button.get_active() != self.state.play_queue_open:
                self.state.play_queue_open = self.play_queue_button.get_active()
        self.play_queue_button.connect("toggled", play_queue_button_toggled)

        def on_play_queue_open(*_):
            self.play_queue_button.set_active(self.state.play_queue_open)
        self.state.connect("notify::play-queue-open", on_play_queue_open)

        open_buttons.pack_start(self.play_queue_button, False, False, 5)

        self.menu_button = IconButton(
            "view-more-symbolic",
            "Menu",
            relief=True,
            icon_size=Gtk.IconSize.LARGE_TOOLBAR)
        open_buttons.pack_start(self.menu_button, False, False, 5)

        buttons.add(open_buttons)

        def flap_reveal(*_):
            if self.state.flap_open:
                buttons.set_visible_child(open_buttons)
            else:
                buttons.set_visible_child(close_buttons)

                self.state.play_queue_open = False
        self.state.connect("notify::flap-open", flap_reveal)

        action_bar.pack_end(buttons)

        self.add(action_bar)

    def create_song_display(self):
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, hexpand=True)
        box.set_property('width-request', 200)

        self.cover_art = SpinnerImage(
            image_name="player-controls-album-artwork",
            image_size=50,
        )
        box.pack_start(self.cover_art, False, False, 0)

        details_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)

        def make_label(name: str) -> Gtk.Label:
            return Gtk.Label(
                name=name,
                halign=Gtk.Align.START,
                xalign=0,
                use_markup=True,
                ellipsize=Pango.EllipsizeMode.END,
            )

        self.song_title = make_label("song-title")
        details_box.add(self.song_title)

        self.artist_name = make_label("artist-name")
        details_box.add(self.artist_name)

        box.pack_start(details_box, False, False, 5)

        return box

    def update(self, app_config: AppConfiguration, force: bool = False):
        if app_config.state.current_song is not None:
            self.song_title.set_markup(bleach.clean(app_config.state.current_song.title))
            # TODO (#71): use walrus once MYPY gets its act together
            album = app_config.state.current_song.album
            artist = app_config.state.current_song.artist
            if artist:
                self.artist_name.set_markup(bleach.clean(artist.name))
                self.artist_name.show()
            elif album:
                self.artist_name.set_markup(bleach.clean(album.name))
                self.artist_name.show()
            else:
                self.artist_name.set_markup("")
                self.artist_name.hide()
        else:
            # Clear out the cover art and song tite if no song
            self.song_title.set_markup("")
            self.artist_name.set_markup("")

    def set_cover_art(self, cover_art_filename: str, loading: bool):
        self.cover_art.set_from_file(cover_art_filename)
        self.cover_art.set_loading(loading)


class MobileFlap(Handy.Flap):
    def __init__(self, state):
        super().__init__(orientation=Gtk.Orientation.VERTICAL, fold_policy=Handy.FlapFoldPolicy.ALWAYS, vexpand=True, swipe_to_open=False)

        self.state = state
        self.state.add_control(self)

        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, vexpand=True)
        box.get_style_context().add_class("background")

        box.pack_start(Gtk.Separator(), False, False, 0)

        overlay = Gtk.Overlay(hexpand=True, vexpand=True, height_request=100)

        self.cover_art = SpinnerPicture(image_name="player-controls-album-artwork")
        overlay.add(self.cover_art)

        overlay_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        overlay.add_overlay(overlay_box)

        overlay_row_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        overlay_box.pack_end(overlay_row_box, False, False, 0)

        overlay_row_box.pack_start(common.create_label(self.state, "progress-label"), False, False, 5)
        overlay_row_box.pack_end(common.create_label(self.state, "duration-label"), False, False, 5)

        # Device
        self.device_button = IconButton(
            "chromecast-symbolic",
            "Show available audio output devices",
            icon_size=Gtk.IconSize.LARGE_TOOLBAR,
        )
        def on_device_click(_: Any):
            if self.device_popover.is_visible():
                self.device_popover.popdown()
            else:
                self.device_popover.popup()
                self.device_popover.show_all()
        self.device_button.connect("clicked", self.state.popup_devices)
        overlay_row_box.set_center_widget(self.device_button)

        self.device_popover = Gtk.PopoverMenu(modal=False, name="device-popover")
        self.device_popover.set_relative_to(self.device_button)

        device_popover_box = Gtk.Box(
            orientation=Gtk.Orientation.VERTICAL,
            name="device-popover-box",
        )
        device_popover_header = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)

        self.popover_label = Gtk.Label(
            label="<b>Devices</b>",
            use_markup=True,
            halign=Gtk.Align.START,
            margin=5,
        )
        device_popover_header.add(self.popover_label)

        refresh_devices = IconButton("view-refresh-symbolic", "Refresh device list")
        refresh_devices.set_action_name("app.refresh-devices")
        device_popover_header.pack_end(refresh_devices, False, False, 0)

        device_popover_box.add(device_popover_header)

        device_list_and_loading = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)

        self.device_list = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        device_list_and_loading.add(self.device_list)

        device_popover_box.pack_end(device_list_and_loading, True, True, 0)

        self.device_popover.add(device_popover_box)

        box.pack_start(overlay, True, True, 0)

        # Song Scrubber
        scrubber = Gtk.Scale(
            orientation=Gtk.Orientation.HORIZONTAL,
            adjustment=self.state.scrubber,
            name="song-scrubber",
            draw_value=False,
            restrict_to_fill_level=False)
        self.state.bind_property("scrubber-cache", scrubber, "fill-level")
        box.pack_start(scrubber, False, False, 0)

        buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)

        # Repeat button
        repeat_button = common.create_repeat_button(self.state, valign=Gtk.Align.CENTER, margin_left=10)
        buttons.pack_start(repeat_button, False, False, 0)

        center_buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)

        # Previous button
        center_buttons.add(common.create_prev_button(self.state, valign=Gtk.Align.CENTER))

        # Play button
        center_buttons.add(common.create_play_button(self.state))

        # Next button
        center_buttons.add(common.create_next_button(self.state, valign=Gtk.Align.CENTER))

        buttons.set_center_widget(center_buttons)

        # Shuffle button
        shuffle_button = common.create_shuffle_button(self.state, valign=Gtk.Align.CENTER, margin_right=10)
        buttons.pack_end(shuffle_button, False, False, 0)

        box.pack_start(buttons, False, False, 0)

        box.pack_start(Gtk.Box(), False, False, 5)

        self.set_content(box)

        play_queue_scrollbox = Gtk.ScrolledWindow(vexpand=True)

        play_queue_list = Gtk.TreeView(
            model=self.state.play_queue_store,
            reorderable=True,
            headers_visible=False,
        )
        play_queue_list.get_style_context().add_class("background")

        renderer = Gtk.CellRendererPixbuf()
        renderer.set_fixed_size(55, 60)
        column = Gtk.TreeViewColumn("", renderer)
        column.set_cell_data_func(renderer, common.filename_to_pixbuf)
        column.set_resizable(True)
        play_queue_list.append_column(column)

        renderer = Gtk.CellRendererText(markup=True, ellipsize=Pango.EllipsizeMode.END)
        column = Gtk.TreeViewColumn("", renderer, markup=2, sensitive=0)
        column.set_expand(True)
        play_queue_list.append_column(column)

        renderer = Gtk.CellRendererPixbuf(icon_name="view-more-symbolic")
        renderer.set_fixed_size(45, 60)
        view_more_column = Gtk.TreeViewColumn("", renderer)
        view_more_column.set_resizable(True)
        play_queue_list.append_column(view_more_column)

        renderer = Gtk.CellRendererPixbuf(icon_name="window-close-symbolic")
        renderer.set_fixed_size(45, 60)
        close_column = Gtk.TreeViewColumn("", renderer)
        close_column.set_resizable(True)
        play_queue_list.append_column(close_column)

        def on_play_queue_button_press(tree: Any, event: Gdk.EventButton) -> bool:
            if event.button == 1:
                clicked_path = tree.get_path_at_pos(event.x, event.y)[0]
                song_idx = clicked_path.get_indices()[0]

                tree_width = tree.get_allocation().width

                if event.x > tree_width - close_column.get_width():
                    self.state.emit("songs-removed", [song_idx])
                elif event.x > tree_width - close_column.get_width() - view_more_column.get_width():
                    common.show_play_queue_popover(self.state, play_queue_list, [clicked_path], event.x, event.y)
                else:
                    self.state.emit("song-clicked", song_idx, [m[-1] for m in self.state.play_queue_store], {"no_reshuffle": True})

                return True
            return False
        play_queue_list.connect("button-press-event", on_play_queue_button_press)

        play_queue_scrollbox.add(play_queue_list)
        self.set_flap(play_queue_scrollbox)

        def on_play_queue_open(*_):
            if not self.get_child_visible():
                self.set_reveal_flap(False)
                return

            self.set_reveal_flap(self.state.play_queue_open)
        self.state.connect("notify::play-queue-open", on_play_queue_open)


    def update(self, app_config: AppConfiguration, force: bool = False):
        pass

    def set_cover_art(self, cover_art_filename: str, loading: bool):
        self.cover_art.set_from_file(cover_art_filename)
        self.cover_art.set_loading(loading)
