# A cover viewer plugin

import copy
import xml.parsers.expat
import urllib
import gtk, gtk.glade
import pygtk
import threading
import os
import ConfigParser

COVER_PATH = os.path.expanduser("~/.covers/")
DB_FILE = os.path.expanduser("~/.covers/covers.db")

__name = 'Cover Viewer'
__version = '0.01'
__author = "Natan 'whatah' Zohar"
__blurb = 'Cover Viewer is a plugin that automatically searches and downloads album art, via Amazon, and displays it in a new window.'
# Remove everything inside parantheses or brackets within a string
def remove_parantheses(string):
    open = False
    ret = ''
    for char in string:
        if char == '(' or char == '[' or char == '{':
            open = True
        if not open:
            ret += char
        if char == ')' or char == ']' or char == '}':
            open = False
    return ret
    
class Result:
    asin = ''
    productname = ''
    catalog = ''
    artists = []
    releasedate = ''
    manafacturer = ''
    imageurlsmall = ''
    imageurlmedium = ''
    imageurllarge = ''

class Parser:
    results = []
    result = Result()
    attr = ''
    def start_element(self, name, attrs):
        pass
    def end_element(self, name):
        if name.lower() == 'details':
            result_open = False
            self.results.append(copy.deepcopy(self.result))
        setattr(self.result, name.lower(), self.attr)
    def char_data(self, data):
        self.attr = data

class Coverviewer:

    # called when we load the plugin. we get passed the mainWindow, plugin menu
    # item as well as a connection to mpd.
    def _init(self, data, get):
        if not os.path.exists(os.path.expanduser(COVER_PATH)):
            print 'Creating coverart directory'
            os.mkdir(COVER_PATH)
        self.xml, self.pluginMenu, self.pympd, self.base_dir, parser = data
        self.readConfigParser(DB_FILE)
        self.buildPluginMenu(self.pluginMenu)
        self.buildInfoWindow()
        self.urlfile = urllib.URLopener()
        self.r = Parser()
        self.CVurl = "http://xml.amazon.com/onca/xml3?t=webservices-20&dev-t=1SR4P88NA1BQ5RZVWK02"
        self.displayedImage = None

    # plugin configure dialog.
    def _conf(self, hasDialog=None):
        return False
    
    # update plugin, data is of type mpdclient.Status
    def _spin(self, data, songChanged=False):
        if songChanged and self.dialogWindow.get_property('visible'):
            self.browseCovers(None)
        pass

    # called on plugin unloaded
    def _unload(self):
        self.saveConfigParser(DB_FILE)
        self.pluginMenuEntry.destroy()
        self.dialogWindow.destroy()
        return

    def readConfigParser(self, filename):
        self.configParser = ConfigParser.ConfigParser()
        self.configParser.read(filename)

    def saveConfigParser(self, filename):
        self.configParser.write(open(filename, 'w'))

    # callback for find similar artists in menu
    def browseCovers(self, obj):
        song = self.pympd.getCurrentSong()
        if not song == False:
            self.dialogWindow.show()
            if song.artist.strip() == '' or song.album.strip() == '':
                self.noImage()
                return
            search_term = song.artist + '-' + song.album
            image_path = "%s%s.jpg" % (COVER_PATH, search_term)

            if self.configParser.has_section(song.artist) and self.configParser.has_option(song.artist, song.album):
                path = self.configParser.get(song.artist, song.album).strip('"')
                if os.path.exists(path):
                    image_path = path
                else:
                    self.noImage()
            
            if os.path.exists(image_path):
                thread = threading.Thread(target=self.dispImage, args=[None, image_path])
                if not self.configParser.has_section(song.artist):
                    self.configParser.add_section(song.artist)
                self.configParser.set(song.artist, song.album, '"%s"'%image_path)
                thread.start()
            else:
                thread =threading.Thread(target=self.OpenURL, args=[song.artist, song.album])
                thread.start()
        

    def OpenURL(self, artist, album):
        search_term = artist + ' - ' + album
        search_term = remove_parantheses(search_term)
        file = self.urlfile.open("%s&KeywordSearch=%s&mode=music&type=lite&page=1&f=xml" % (self.CVurl, search_term.replace(' ', '%20')))
        lines = file.readlines()
        data = ''
        for line in lines:
            data += line
        #make a new parser for ourselves
        self.newParser()
        self.parser.Parse(data)
        file.close()
        self.urlfile.close()

        if len(self.r.results) > 0:
            self.dispImage(self.r.results[0].imageurllarge, None, artist, album)
        else:
            self.noImage()
    
    def dispImage(self, imageurl=None, imagepath=None, artist=None, album=None):
        if not artist == None and not album == None:
            search_term = artist + '-' + album
        if not imageurl == None:
            image_name = imageurl[imageurl.rfind('/')+1:]
            image_path = "%s%s.jpg" % (COVER_PATH, search_term)
            if not self.configParser.has_section(artist):
                self.configParser.add_section(artist)
            self.configParser.set(artist, album, '"%s"'%image_path) #Cache the cover into our database.
        if not imagepath == None:
            image_path = imagepath
        if self.displayedImage == image_path:
            return
        if os.path.exists(image_path):
            pass
        else:
            self.urlfile.retrieve(imageurl, image_path)[0]
        gtk.threads_enter()
        self.image.set_from_file(None)
        self.image.set_from_file(image_path)
        self.image.show()
        self.noimage.hide()
        gtk.threads_leave()
        self.displayedImage = image_path

    def noImage(self):
        gtk.threads_enter()
        self.image.hide()
        self.noimage.show()
        gtk.threads_leave()
        

    def newParser(self):
        self.r.results = []
        self.parser = xml.parsers.expat.ParserCreate()
        self.parser.StartElementHandler = self.r.start_element
        self.parser.EndElementHandler = self.r.end_element
        self.parser.CharacterDataHandler = self.r.char_data
    
    
    # Init Functions
    def buildPluginMenu(self, pluginMenu):
        self.pluginMenuEntry = gtk.MenuItem('Cover Viewer')
        pluginSubMenu = gtk.Menu()
        pluginSubMenu.set_title('Coverviewer')
        menuEntry = gtk.MenuItem('Show Album Covers')
        menuEntry.connect('activate', self.browseCovers)
        pluginSubMenu.append(menuEntry)
        self.pluginMenuEntry.set_submenu(pluginSubMenu)
        self.pluginMenuEntry.show_all()
        pluginMenu.append(self.pluginMenuEntry)

    def buildInfoWindow(self):
        self.xml = gtk.glade.XML("%s/glade/coverviewer.glade"%self.base_dir)
        self.dialogWindow = self.xml.get_widget("dialog1")
        self.image = self.xml.get_widget("image1")
        self.noimage = self.xml.get_widget("vbox2")

        dic = {"on_dialog_closed" : self.close_dialog }
        self.xml.signal_autoconnect(dic)

    def close_dialog(self, obj, data):
        self.dialogWindow.hide()
        return True
