"""
MusicBrainz Tag Submitter -- Quod Libet plugin for submitting genre tags from
local files to MusicBrainz.

(C) 2010 by Lukas Lalinsky <lalinsky@gmail.com>

Preferences dialog from LastFMTagger:
(C) 2005-2007 by Eric Casteleijn <thisfred@gmail.com>
                 Joshua Kwan <joshk@triplehelix.org>,
                 Joe Wreschnig <piman@sacredchao.net>,

Licensed under GPLv2. See Quod Libet's COPYING for more information.
"""

import gtk
import gobject
import threading
import urllib
import urllib2
from Queue import Queue
from quodlibet import config
from quodlibet.plugins.events import EventPlugin

VERBOSE = False

def log(msg):
    if VERBOSE:
        print "[mbtagsubmit]", msg


class MusicBrainzTagSubmitter(EventPlugin):

    PLUGIN_ID = "MusicBrainzTagSubmitter"
    PLUGIN_NAME = "MusicBrainz Tag Submitter"
    PLUGIN_DESC = "Submit local genre tags to MusicBrainz"
    PLUGIN_ICON = gtk.STOCK_CONNECT
    PLUGIN_VERSION = "0.1"

    TAG = 'genre'
    SUBMIT_INTERVAL = 2 * 60 # submit full batch every 2 minutes
    SUBMIT_INTERVAL_FLUSH = 15 # submit anything lese every 30 minutes
    SUBMIT_BATCH_SIZE = 20

    WS_REALM = 'musicbrainz.org'
    WS_URL = 'http://musicbrainz.org/ws/1/tag/'

    __enabled = False
    needs_config = True

    def __init__(self):
        self.post_queue = Queue()
        self.post_thread = threading.Thread(None, self.post_data)
        self.post_thread.setDaemon(True)
        self.post_thread.start()
        self.submit_queue = {}
        self.submit_queue_o = []
        self.submit_queue_flush = 0
        gobject.timeout_add_seconds(self.SUBMIT_INTERVAL, self.submit_tags)
        self.__enabled = self.read_config()

    def read_config(self):
        try:
            username = config.get('plugins', 'mbtagsubmit_username')
            password = config.get('plugins', 'mbtagsubmit_password')
        except Exception:
            return False
        return self.prepare_opener(username, password)

    def prepare_opener(self, username, password):
        if not username or not password:
            self.opener = None
            return False
        password_mgr = urllib2.HTTPPasswordMgr()
        password_mgr.add_password(self.WS_REALM, self.WS_URL, username, password)
        auth_handler = urllib2.HTTPDigestAuthHandler(password_mgr)
        self.opener = urllib2.build_opener(auth_handler)
        return True

    def submit_tags(self):
        self.submit_queue_flush = (self.submit_queue_flush + 1) % self.SUBMIT_INTERVAL_FLUSH
        if not self.submit_queue:
            return True
        if len(self.submit_queue) < self.SUBMIT_BATCH_SIZE and self.submit_queue_flush > 0:
            return True
        batch = self.submit_queue_o[:self.SUBMIT_BATCH_SIZE]
        data = {}
        for i, trackid in enumerate(batch):
            tags = self.submit_queue.pop(trackid)
            data['entity.%d' % i] = 'track'
            data['id.%d' % i] = trackid.encode('utf-8')
            data['tags.%d' % i] = ','.join(tags).encode('utf-8')
        encoded_data = urllib.urlencode(data)
        self.submit_queue_o = self.submit_queue_o[self.SUBMIT_BATCH_SIZE:]
        log("Submitting %r" % (encoded_data,))
        self.post_queue.put(encoded_data)
        return True

    def post_data(self):
        while True:
            data = self.post_queue.get()
            try:
                self.opener.open(self.WS_URL, data)
            except urllib2.HTTPError, e:
                log('Submitting tags failed: %s' % (e,))
            self.post_queue.task_done()

    def process_song(self, song, reason):
        if 'musicbrainz_trackid' not in song or self.TAG not in song:
            return
        trackid = song('musicbrainz_trackid')
        tags = song.list(self.TAG)
        if trackid not in self.submit_queue:
            log("Adding %s (%r) to queue [%s]" % (song('~filename'), tags, reason))
            self.submit_queue_o.append(trackid)
        self.submit_queue[trackid] = tags

    def plugin_on_song_ended(self, song, skipped):
        if not self.__enabled:
            return
        if song is not None:
            self.process_song(song, 'ended')

    def plugin_on_changed(self, songs):
        if not self.__enabled:
            return
        for song in songs:
            self.process_song(song, 'changed')

    def PluginPreferences(self, parent):

        def changed(entry, key):
            config.set("plugins", "mbtagsubmit_" + key, entry.get_text())

        def destroyed(*args):
            self.__enabled = self.read_config()

        table = gtk.Table(6, 3)
        table.set_col_spacings(3)
        lt = gtk.Label(
            _("Please enter your MusicBrainz\nusername and password."))
        lu = gtk.Label(_("Username:"))
        lp = gtk.Label(_("Password:"))

        for l in [lt, lu, lp]:
            l.set_line_wrap(True)
            l.set_alignment(0.0, 0.5)
        table.attach(lt, 0, 2, 0, 1, xoptions=gtk.FILL | gtk.SHRINK)
        table.attach(lu, 0, 1, 1, 2, xoptions=gtk.FILL | gtk.SHRINK)
        table.attach(lp, 0, 1, 2, 3, xoptions=gtk.FILL | gtk.SHRINK)

        userent = gtk.Entry()
        pwent = gtk.Entry()
        pwent.set_visibility(False)
        pwent.set_invisible_char('*')

        table.set_border_width(6)
        try:
            userent.set_text(config.get("plugins", "mbtagsubmit_username"))
        except:
            pass
        try:
            pwent.set_text(config.get("plugins", "mbtagsubmit_password"))
        except:
            pass

        table.attach(userent, 1, 2, 1, 2, xoptions=gtk.FILL | gtk.SHRINK)
        table.attach(pwent, 1, 2, 2, 3, xoptions=gtk.FILL | gtk.SHRINK)
        pwent.connect('changed', changed, 'password')
        userent.connect('changed', changed, 'username')

        table.connect('destroy', destroyed)
        return table

