# -*- coding: utf-8 -*-

#   This file is part of emesene.
#
#    Emesene 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 2 of the License, or
#    (at your option) any later version.
#
#    emesene 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 emesene; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
import os
import gtk
import socket
import urllib
import urllib2
import gobject
import re

import Plugin
import currentSong

import paths

COVERART_PATH = os.path.join(paths.HOME_DIR, '.covers', '')
BANSHEE_PATH = os.path.join(paths.HOME_DIR, '.cache/album-art','')

PLUGIN_DESCRIPTION = _( 'Show a personal message with the current song' \
    ' played in multiple players.' )
PLUGIN_AUTHORS = { 'Mariano Guerra' : 'luismarianoguerra gmail com' }
PLUGIN_WEBSITE = 'http://emesene.org'
PLUGIN_DISPLAYNAME = _( 'CurrentSong' )
PLUGIN_NAME = 'CurrentSong'

class MainClass( Plugin.Plugin ):
    '''Main plugin class'''
    description = PLUGIN_DESCRIPTION
    authors = PLUGIN_AUTHORS
    website = PLUGIN_WEBSITE
    displayName = PLUGIN_DISPLAYNAME
    name = PLUGIN_NAME
    
    def __init__( self, controller, msn ):
        '''Constructor'''
        
        Plugin.Plugin.__init__( self, controller, msn, 1)
        self.theme = controller.theme
        
        self.controller = controller
        
        self.description = PLUGIN_DESCRIPTION
        self.authors = PLUGIN_AUTHORS
        self.website = PLUGIN_WEBSITE
        self.displayName = PLUGIN_DISPLAYNAME
        self.name = PLUGIN_NAME
        
        self.configWindow = None
        self.statusLabel = None
        self.customConfigWidget = None
        self.usingDummy = True
        self.dictCommand = None
        
        self.Slash = controller.Slash
        self.config = controller.config
        self.config.readPluginConfig(self.name)
        
        self.setPlayer( self.config.getPluginValue( self.name, 'player', \
            'CurrentSong' ) )
        self.player.setStyle( self.config.getPluginValue( self.name, 'style', \
            '%title - %artist (%album)' ) )
        self.avatar = self.config.getPluginValue(self.name, 'avatar', \
            '0') == '1'
        self.remotefetch = self.config.getPluginValue(self.name, \
            'remotefetch', '0') == '1'
        
        self.updateCustomConf()
        self.timeout = 0
        self.mediaEnabledId = 0
        self.coverAvatar = Plugin.Function( self.getCoverAvatar, \
            self.setCoverAvatar )
        
    def start( self ):
        '''start the plugin'''
        if not os.path.isdir(COVERART_PATH):
            os.mkdir(COVERART_PATH)
        
        if not self.usingDummy:
            self.player.start()
        
        self.timeout = gobject.timeout_add_seconds(self.period, self.action)
        self.Slash.register('currentsong', self.currentSongHandler, \
            _('currentsong command'))
        self.Slash.register('np', self.nowplaying, \
            _('Send what you are listening to'))
        
        self.controller.mainWindow.userPanel.mediaButton.show()

        self.mediaEnabledId = self.controller.config.connect(
            'change::mediaEnabled', self.onMediaEnabled)
            
        self.enabled = True

        self.original_avatar = self.config.user['avatarPath']

        self.doNotSaveAvatar = False

        self.updateAvatarId = self.controller.connect('avatar-changed', self.updateAvatar)    

    def updateAvatar(self, controller, path=None):
        if path == None:
            path = self.config.user['avatarPath']

        if not self.doNotSaveAvatar:
            self.original_avatar = path

    def showHelp(self):
        msg = 'Help:'
        for (cmd, val) in self.dictCommand.iteritems():
            msg += '\n ' + str(cmd) + '\t' + str(val[1])
        return msg

    def playerChanged( self, combo ):
        newplayer = combo.get_active_text()
        self.setPlayer(newplayer)
        self.updateStatus()
        self.updateCustomConf() 
        self.dictCommand = self.player.dictCommand
        self.customConfigWidget.refresh(self.player.customConfig)

    def currentSongHandler(self, slashAction):
        name = None
        param = None
        
        try:
            cmd = slashAction.getParams().split(" ", 1)
            name = cmd[0]
            param = cmd[1]
        except:
            pass
        
        if name != None and self.dictCommand.has_key(name):
            retval, msg = self.dictCommand[name][0](param)
            if self.dictCommand[name][2]:
                slashAction.outputText( msg , retval )
            else:
                slashAction.outputText( msg )
        else:
            slashAction.outputText(self.showHelp())
    
    def nowplaying(self, slashAction):
        if self.player.cmd_Show(True)[0] == False:
            slashAction.outputText(_('Not playing'))
        else:
            slashAction.outputText(_('now playing: ')+ \
                self.player.cmd_Show(True)[1], True)

    def stop( self ):    
        '''stop the plugin'''
        
        if not self.usingDummy:
            self.player.stop()
        
        if self.timeout:
            gobject.source_remove(self.timeout)

        self.msn.changeCurrentMedia( '' )
        self.Slash.unregister('currentsong')
        self.Slash.unregister('np')
        
        self.controller.mainWindow.userPanel.mediaButton.hide()
        self.controller.mainWindow.userPanel.mediaButton.set_active(False)
        
        if self.avatar and self.original_avatar:
            self.controller.changeAvatar(self.original_avatar)

        self.controller.config.disconnect(self.mediaEnabledId)
        
        self.enabled = False

        self.controller.disconnect(self.updateAvatarId)

    def onMediaEnabled(self, config, value, oldvalue):
        '''Called when the media button is toggled'''
        # print "Current song plugin: onMediaEnabled ["+value+"]"
        
        self.action(True)

        if self.avatar and value == "0" and self.original_avatar:
            self.controller.changeAvatar(self.original_avatar)
       
    def action(self, force=False):
        if not self.enabled or not self.config.user['mediaEnabled']:
            return True
        
        if not self.usingDummy:
            try:
                check = self.player.check()
            except Exception, e:
                print 'Error in player.check():', e
                self.setPlayer(None)
                check = True
            
            # USEFUL for LAST.FM plug 
            # self.controller.emit('media-changed',self.player.getSongDict())
            # dx note: wtf?

            if force or check: 
                self.player.setStyle(self.config.getPluginValue( self.name, 'style', \
                '%title - %artist (%album)' ))
                currentsong = self.player.getCurrentSong()\
                    .decode("utf-8", "replace").encode("utf-8")
                sdict = self.player.getSongDict()
                
                self.msn.changeCurrentMedia( currentsong, sdict)
                if self.avatar:
                    self.coverAvatar(self.remotefetch)
        else:
            self.setPlayer( self.config.getPluginValue( self.name, 'player', \
                'CurrentSong' ) )

        return True

    def getCoverAvatar(self, remote):
        ''' get avatar cover art '''
        path = self.player.getCoverPath()
        if path and os.path.exists(path):
            #print "Current song plugin : getCoverPath : [" + path + "]"
            return path

        path = self.search_local_image()  
        if path and os.path.exists(path):
            #print "Current song plugin : getCoverPath : [" + path + "]"
            return path

        if remote:
            try:
                path = self.search_remote_image()
                #print "Current song plugin : getCoverPath : [" + path + "]"
                return path
            except:
                # handle connection errors
                pass

    def setCoverAvatar(self, path):
        ''' set avatar cover art '''
        if path != None and os.path.exists(path):
            self.doNotSaveAvatar = True
            self.controller.changeAvatar(path)
            self.doNotSaveAvatar = False
            return True
        if not self.player.isPlaying() or not self.player.isRunning():
            if self.original_avatar:
                self.doNotSaveAvatar = True
                self.controller.changeAvatar(self.original_avatar)
                self.doNotSaveAvatar = False
            return False
        return False

    def is_on_path(self, fname):
        for p in os.environ['PATH'].split(os.pathsep):
            if os.path.isfile(os.path.join(p, fname)):
                return True
        
    def getPluginNames( self ):
        return [ x for x in dir( currentSong ) \
                 if not x.startswith( "__" ) \
                 and x != 'CurrentSong' ]
        
    def setPlayer( self, name ):
        players = self.getPluginNames()     
        
        if name == _('Autodetect'):
            for n in players:
                if n.lower() != 'lastfm':
                    #print "Current song plugin: Trying " + n
                    if self._setPlayer(n, players) == 'loaded':
                        if not self.usingDummy and self.player.isRunning():
                            return
        else:
            self._setPlayer(name, players)
        
    def _setPlayer(self, name, players):
        '''try to set the player object to the name given by the name parameter'''

        self.playername = name
        
        error = '' 
        if name in players:
            try:
                self.player = getattr(currentSong, name)()
                self.usingDummy = False
                self.action()
            except Exception, e:
                #print "error initializing CurrentSong player:", e
                error = e
            else:
                self.player.log('info', _('Player: ') + name)
                status, message = self.player.getStatus()
                self.player.log(status and 'info' or 'error', \
                    'getStatus: ' + message)

                if not status:
                    self.player.status = 'error'
                
                self.dictCommand = self.player.dictCommand
                return 'loaded'
        self.player = currentSong.CurrentSong()
        self.usingDummy = True
        if error:
            return error
        
    def check( self ):
        '''check if everything is OK to start the plugin
        return a tuple whith a boolean and a message
        if OK -> ( True , 'some message' )
        else -> ( False , 'error message' )'''
        
        return (True, 'Ok')
   
    def updateCustomConf( self ):
        for key in self.player.customConfig.keys():
            self.player.customConfig[key] = \
                self.config.getPluginValue( self.name, \
                str(self.playername + "_" + key), \
                self.player.customConfig[key] )     

        self.player.updateConfig()

    def search_local_image(self):
        '''searches in the local covers cache
        returns None if no local image found'''
        
        artist = self.player.artist.encode('utf8')
        album = self.player.album.encode('utf8')
        
        bartist = re.sub("\W", "", artist).lower()
        balbum = re.sub("\W", "", album).lower()
        file_dsc = BANSHEE_PATH + bartist + '-' + balbum + '.jpg'
        if os.path.exists(file_dsc):
            return file_dsc
        
        file_dsc = COVERART_PATH + artist + '-' + album + '.jpg'
        if os.path.exists(file_dsc):
            return file_dsc
        return None

    def search_remote_image(self):

        imgfound = None

        artist = self.player.artist.encode('utf8')
        album = self.player.album.encode('utf8')
        
        if len(artist) == 0 and len(album) == 0:
            return None

        dest_filename = COVERART_PATH + artist + '-' + album + '.jpg'

        url = "http://www.albumart.org/index.php?srchkey=" + \
            urllib.quote_plus(artist) + "+" + urllib.quote_plus(album) + \
            "&itempage=1&newsearch=1&searchindex=Music"

        # This is useful to see if we're searching for the right song
        #print "Current song plugin: " + url

        albumart = urllib.urlopen(url).read()
        image_url = ""

        for line in albumart.split("\n"):
    
            if "http://www.albumart.org/images/zoom-icon.jpg" in line:
                image_url = line.partition('src="')[2].partition('"')[0]
    
            if image_url:
                urllib.urlretrieve(image_url, dest_filename)
                imgfound = dest_filename
                break

        return imgfound

    def configure( self ):
        '''display a configuration dialog'''

        style = self.config.getPluginValue(self.name, 'style', \
            '%title - %artist (%album)')
        player = self.config.getPluginValue(self.name, 'player', 'Amarok')
        avatar = self.config.getPluginValue(self.name, 'avatar', '0') == '1'
        remotefetch = self.config.getPluginValue(self.name, \
            'remotefetch', '0') == '1'

        #self.updateCustomConf()
        plugin_names = self.getPluginNames()
        plugin_names.insert(0, _('Autodetect'))
        l = []
        # name, optionType, label, description, value, options
        l.append(Plugin.Option('style', str, _('Display style'), \
            _('Change display style of the song you are listening, possible ' \
            'variables are %title, %artist and %album'), style))
        l.append(Plugin.Option('player', list, _('Music player'), \
            _('Select the music player that you are using'), player, \
            plugin_names, self.playerChanged))
        
        self.statusLabel = gtk.Label()
        status_timeout = gobject.timeout_add_seconds(2, self.updateStatus)
        self.updateStatus()
        logsButton = gtk.Button( _('Show logs') )
        logsButton.connect('clicked', self.showLogs)
        hbox = gtk.HBox()
        hbox.pack_start(self.statusLabel, True, True)
        hbox.pack_start(logsButton)
        status = Plugin.Option('status', gtk.Widget, '', '', hbox)
        l.append(status)

        l.append(Plugin.Option('avatar', bool, _('Change Avatar'), \
            _('Change Avatar'), avatar))
        l.append(Plugin.Option( 'remotefetch', bool, \
            _( 'Get cover art from web' ), _( 'Get cover art from web' ), \
            remotefetch ))
        
        custom = Plugin.Option('custom', dict, _('Custom config'), \
            _('Custom config'), None, self.player.customConfig)
        l.append(custom)

        self.configWindow = Plugin.ConfigWindow( _( 'Config Plugin' ), l )
        self.customConfigWidget = custom.widget
        response = self.configWindow.run()

        if status_timeout:
            # this stops the update status timeout
            gobject.source_remove(status_timeout)

        if response != None:
            if response.has_key( 'player' ):
                self.setPlayer( response[ 'player' ].value )
                self.config.setPluginValue( self.name, 'player', \
                    response[ 'player' ].value )
            if response.has_key( 'style' ):
                self.player.setStyle( response[ 'style' ].value )
                self.config.setPluginValue( self.name, 'style', \
                    response['style'].value )
            if response.has_key('avatar'):
                self.avatar = response['avatar'].value
                self.config.setPluginValue(self.name, 'avatar', \
                    str(int(response['avatar'].value)))
            if response.has_key('remotefetch'):
                self.remotefetch =  response[ 'remotefetch' ].value 
                self.config.setPluginValue( self.name, 'remotefetch', \
                    str(int(response[ 'remotefetch' ].value)))
            if response.has_key('custom'):
                self.player.customConfig = self.customConfigWidget.data
                for key in self.player.customConfig.keys():
                    self.config.setPluginValue(self.name, \
                        str(self.playername + "_" + key), \
                        self.player.customConfig[key])
                self.player.updateConfig()
        self.configWindow = None
        self.statusLabel = None
        return True

    def updateStatus( self ):
        '''updates the status label'''
        if hasattr(self.player, 'status'):
            status = self.player.status
        else:
            status = _('unknown')

        if self.statusLabel:
            self.statusLabel.set_markup(_('Status:') + ' <b>%s</b>' % status)
            return True
        else:
            return False
        
    def showLogs( self, button ):
        '''Display an error log window'''
        log = []
        if hasattr(self.player, '_log'):
            log = self.player._log
        
        win = gtk.Dialog( _('Logs'), self.configWindow, \
            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, \
            (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT) )
        win.set_default_size(300, 400)
        model = gtk.ListStore(str, str)
        tree = gtk.TreeView(model)
        crtkey = gtk.CellRendererText()
        crtvalue = gtk.CellRendererText()

        col1 = gtk.TreeViewColumn(_('Type'), crtkey, text=0)
        col2 = gtk.TreeViewColumn(_('Value'), crtvalue, text=1)
        tree.append_column(col1)
        tree.append_column(col2)
        for item in log:
            model.append(item)
        win.vbox.pack_start(tree)
        win.show_all()
        win.run()
        win.destroy()
