# coding: utf-8
#
# Graphical Asynchronous Music Player Client
#
# Copyright (C) 2015 Itaï BEN YAACOV
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


from gi.repository import GObject, Gtk, Gdk, GdkPixbuf

import xdg
import time
import asyncio
import ampd

from gampc import module
from gampc import plugin


class PixbufCache(dict):
    def __missing__(self, key):
        pixbuf = self.find_image(key)
        if pixbuf is not None:
            self[key] = pixbuf
        return pixbuf

    def find_image(self, key):
        for extension in ('.jpg', '.png', '.gif'):
            for name in xdg.BaseDirectory.load_data_paths('gampc', 'photos', key + extension):
                return GdkPixbuf.Pixbuf.new_from_file(name)

        for sep in (', ', ' y '):
            if sep in key:
                return self.find_images(key.split(sep))

        return None

    def find_images(self, names):
        pixbufs = [self[name] for name in names]
        if not all(pixbufs):
            return None
        width = height = 0
        for p in pixbufs:
            p.w = p.get_width()
            p.h = p.get_height()
            p.r = p.w / p.h
            height = max(height, 2 * p.h)
        for p in pixbufs:
            p.nw = height * p.r
            p.x = width
            width += p.nw
        pixbuf = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, False, 8, width, height)
        for p in pixbufs:
            p.composite(pixbuf, p.x, 0, p.nw, height, p.x, 0, height / p.h, height / p.h, GdkPixbuf.InterpType.BILINEAR, 255)
        return pixbuf


class Current(module.Module):
    title = _("Current Song")
    name = 'current'
    key = '0'

    FIELD_NAMES = ['Artist', 'Performer', 'Title', 'Date', 'Genre', 'Composer']

    size = GObject.property(type=int)

    def __init__(self, *args):
        super().__init__(*args)
        self.signals['check-resize'] = self.window_check_resize_cb

        self.pixbufs = PixbufCache()
        self.images = []

        builder = self.shared.build_ui('plugins/current')
        self.welcome = builder.get_object('welcome')
        self.current = builder.get_object('current')
        self.shared.ampd_server_properties.bind_property('current-song', self.welcome, 'visible', GObject.BindingFlags.SYNC_CREATE, lambda x, y: not y)
        self.shared.ampd_server_properties.bind_property('current-song', self.current, 'visible', GObject.BindingFlags.SYNC_CREATE, lambda x, y: bool(y))
        self.shared.ampd_server_properties.connect('notify::current-song', self.notify_opacity_cb)
        self.running_fader = None

        self.width = 0
        self.css_provider = Gtk.CssProvider.new()
        self.get_style_context().add_provider(self.css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
        self.connect('size-allocate', self.size_allocate_cb)
        self.bind_property('size', builder.get_object('image-icon'), 'pixel-size', GObject.BindingFlags(0), lambda x, y: y * 5)

        for field in self.FIELD_NAMES:
            label = builder.get_object('label-' + field.lower())
            label.field = field
            image = builder.get_object('image-' + field.lower())
            if image:
                image_label = builder.get_object('image-label-' + field.lower())
                if image_label:
                    label.bind_property('label', image_label, 'label', GObject.BindingFlags.SYNC_CREATE)
                    image.bind_property('visible', image_label, 'visible', GObject.BindingFlags.SYNC_CREATE)
                label.connect('notify::label', self.notify_label_cb, image)
                image.connect('size-allocate', self.image_size_allocate_cb)
                self.images.append(image)
            else:
                self.shared.ampd_server_properties.bind_property('current-song',
                                                                 label, 'visible',
                                                                 GObject.BindingFlags.SYNC_CREATE,
                                                                 lambda x, y, z: z in y,
                                                                 None, field)
            self.shared.ampd_server_properties.bind_property('current-song',
                                                             label, 'label',
                                                             GObject.BindingFlags.SYNC_CREATE,
                                                             lambda x, y, z: self.set_size() or y.get(z, ''),
                                                             None, field)

        self.add(builder.get_object('widget'))
        self.connect('map', self.__map_cb)
        self.connect('destroy', self.__destroy_cb)

    @staticmethod
    def __destroy_cb(self):
        if self.running_fader:
            self.running_fader.cancel()
            self.running_fader = None

    @staticmethod
    def __map_cb(self):
        self.width = 0
        self.set_size()

    def window_check_resize_cb(self, win):
        for image in self.images:
            image.clear()
            image.last_width = image.last_height = None

    def notify_label_cb(self, label, param, image):
        text = label.get_text()
        if not text or (label.field == 'Performer' and text == 'Instrumental'):
            label.get_parent().set_visible(False)
            return

        image.pixbuf = self.pixbufs[text]
        if image.pixbuf:
            image.last_width = image.last_height = None
            image.set_visible(True)
            label.set_visible(False)
        else:
            image.set_visible(False)
            label.set_visible(True)
        label.get_parent().set_visible(True)

    def notify_opacity_cb(self, *args):
        if self.running_fader:
            self.running_fader.cancel()
            self.running_fader = None
        if self.shared.ampd_server_properties.current_song:
            self.running_fader = self.fader()

    @ampd.task
    async def fader(self):
        START = 30
        DURATION = 3
        INTERVAL = 0.05

        if self.shared.dark:
            self.current.set_opacity(0)
            await asyncio.sleep(START)
            t0 = t1 = time.time()
            while t1 < t0 + DURATION:
                self.current.set_opacity((t1 - t0) / DURATION)
                await asyncio.sleep(INTERVAL)
                t1 = time.time()
        self.current.set_opacity(1)

    def do_button_press_event(self, event):
        if event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS and event.button == 1:
            self.win.action_toggle_fullscreen_cb()

    @staticmethod
    def size_allocate_cb(self, allocation):
        self.width = allocation.width
        self.set_size()

    def set_size(self):
        scale = 50.0
        song = self.shared.ampd_server_properties.current_song
        if song:
            scale += 3 * max(len(song.get('Artist', '')) - 20, len(song.get('Title', '')) - 20, 0)
        self.size = self.width / scale
        css = b'* { font-size: ' + str(self.size).encode() + b'px; }'
        self.css_provider.load_from_data(css)

    def image_size_allocate_cb(self, image, allocation):
        if self.width == 0 or image.last_width == allocation.width and image.last_height == allocation.height:
            return
        image.last_width = allocation.width
        image.last_height = allocation.height
        ratio = image.pixbuf.get_height() / image.pixbuf.get_width()
        if allocation.width * ratio < allocation.height:
            allocation.height = allocation.width * ratio
        else:
            allocation.width = allocation.height / ratio
        pixbuf = image.pixbuf.scale_simple(allocation.width, allocation.height, GdkPixbuf.InterpType.BILINEAR)
        image.set_from_pixbuf(pixbuf)


class CurrentExtension(plugin.Extension):
    modules = [Current]
