import mpdlib2
import threading

def fillSong(song):
    if not song.has_key('artist'):
     	song['artist'] = ''
    if not song.has_key('title'):
        song['title'] = ''
    if not song.has_key('album'):
        song['album'] = ''
    if not song.has_key('track'):
        song['track'] = ''

class LockedDoer:
    def __init__(self, object):
        self.object = object
        self.lock = threading.Lock()

    def __getattr__(self, cmd):
        try:
            getattr(self.object, cmd)
            return lambda *args:self.do(getattr(self.object, cmd), args)
        except:
            self.lock.acquire()
            try:
                ret = lambda *args:getattr(self.object.mpd.do, cmd)(*args)
            except:
                ret = None
            self.lock.release()
            return ret

    def do(self, cmd, args):
        self.lock.acquire()
        try:
            ret = cmd(*args)
        except Exception, e:
            self.lock.release()
            raise e
        self.lock.release()
        return ret
    

class PyMpdController:
    def __init__(self):
        self.mpd = None
        self.do = LockedDoer(self)
        self.plist = None
        self.playlistid = None

    #updated
    # connect in mpdclient2 takes keyword arguments.
    def _connect(self, mpd_host, mpd_port, mpd_password=None):
        try:
            self.mpd = mpdlib2.connect(host=mpd_host, port=mpd_port, password=mpd_password)

            return True
        except:
            return False

    #updated
    # should work without adjustments.

    def getStats(self):
        return self.mpd.do.stats()

    #updated
    # list all songs by artist xor album.
    def listallSongs(self, artists, albums):
    	songs = []
        extend = songs.extend
        if artists == []:
            artists = None
    	if not albums or albums == []:
    	    if not artists or artists == []:
                songs = self.mpd.do.listallinfo()
            else:
                [extend(self.mpd.do.find("artist", artist)) for artist in artists]
#    	        extend(self.mpd.do.find("artist", artist))
    	else:
            index = 0
            insert = songs.insert
            for album in albums:
                for song in self.mpd.doit.find('album', album):
                    if artists is None or song.artist in artists:
                        insert(index, song)
                        index+=1

#    	    [extend(self.mpd.do.find("album", album)) for album in albums]
#    	         extend(self.mpd.do.find("album", album))
#    	for song in songs:
#    	    fillSong(song)
    	return songs
        #return self.mpd.listallSongs(artists, albums)

    # may or may not work
    def getPlaylistNames(self):
    	listing = self.mpd.do.lsinfo()
    	playlists = []
    	for item in listing:
    	    if item.has_key('playlist'):
    	         playlists.extend([item.playlist])
    	return playlists
#        return self.mpd.getPlaylistNames()

    #updated
    #looks like it should be fine for pympd. We have to make ints ints and so on.
    #return a Status type
    def status(self):
    	status = self.mpd.do.status()
    	if status.has_key('time'):
    	     time = status.time.split(':')
             try:
                 status['elapsedTime'] = int(time[0])
                 status['totalTime'] = int(time[1])
             except IndexError:
                 pass
    	for x in ['random', 'repeat', 'volume', 'xfade', 'song']:
            try:
                status[x] = int(getattr(status, x))
            except ValueError, e:
                status[x] = -1
#        status['random'] = int(status.random)
#    	status['repeat'] = int(status.repeat)
#    	status['volume'] = int(status.volume)
#    	status['xfade'] = int(status.xfade)
    	if not status.has_key('songid'):
    	     status['songid'] = -1
    	if not status.has_key('updating_db'):
    	     status['updating_db'] = 0
    	return status
#        return self.mpd.do.status()

    #updated
    # return Songs type, should work in pympd. TODO:we need to make sure all
    # the basic tags are here. 
    def playlist(self):
        playlistid = self.mpd.do.status().playlist
        if self.playlistid == playlistid:
            return self.plist
        songs = self.mpd.do.playlistinfo()
        self.playlistid = playlistid
        self.plist = list(songs)
#        for song in songs:
#	     fillSong(song)
#        for song in songs:
#            yield song
#        return songs
        return self.plist

# returns a (list, list) of directories in cur dir and files in cur dir.
    def lsInfo(self, directory=None):
        if not directory == None:
            contents = self.mpd.do.lsinfo(str(directory))
        else:
            contents = self.mpd.do.lsinfo()
#        files = []
#        subdirs = []
#        for item in contents:
#            if item.type == 'file':
#                files.insert(0, item)
#            elif item.type == 'directory':
#                subdirs.insert(0,item)
#            else:
#                print "Unknown type"
#                
#        return subdirs, files
        return contents

# not really needed.
    def playlistChanges(self, playlist):
        return self.mpd.playlistChanges(playlist)

    def listall(self):
        return self.mpd.do.listall()

    #updated
    # read songs from a playlist by playlist name (mpd 0.12)
#updated
    def listallSongsInPlaylist(self, playlist):
        return self.mpd.do.playlistinfo(playlist)


    # need the .list() command to be implemented to use (ask mackstann)
#updated
    def listallArtists(self):
    	artists = []
        extend = artists.extend
        [extend([item.artist]) for item in self.mpd.do.list('artist')]
#        for item in self.mpd.do.list('artist'):
#             artists.extend([item.artist])
        return artists

    # same as listallArtists
#updated
    def listallAlbums(self, artists):
    	albums = []
        if artists == None:
            for item in self.mpd.do.list('album'):
                albums.extend([item.album])
        else:
            if type(artists) == list:
                for artist in artists:
                    for item in self.mpd.do.list('album', artist):
                        albums.extend([item.album])
            else:
                for item in self.mpd.do.list('album', artists):
                    album.extend([item.album])
        return albums

#updated
    # returns a Song type.
    def listSongInPlaylist(self, songID):
        song = self.mpd.do.playlistid(int(songID))[0]
        fillSong(song)
        return song

#updated
    # get current song in playlist's info.
    def getCurrentSong(self):
        status = self.status()
    	if status.state == 'stop':
    	     return False
    	else:
    	     song = self.mpd.do.currentsong()
             fillSong(song)
             return song

#updated
    def volume(self, new_vol):
        return self.mpd.do.setvol(new_vol)

#updated
    def add(self, songs):
#    	self.mpd.send.command_list_begin()
        for song in songs:
                self.mpd.do.add(song)
#        	self.mpd.send.add(song)
#        self.mpd.do.command_list_end()
        return

#updated
    def clear(self):
        return self.mpd.do.clear()

#updated
    def deleteid(self, songIDs):
    	for songID in songIDs:
       	     self.mpd.do.deleteid(int(songID))
        return


#updated
    def rm(self, playlist):
        return self.mpd.do.rm(playlist)

#updated
    def save(self, playlist):
        return self.mpd.do.save(playlist)
    
#updated
    def load(self, playlist):
        return self.mpd.do.load(playlist)

#updated
    def pause(self):
        return self.mpd.do.pause(1)

#updated
    def play(self, songID=None):
	if not songID == None:
	     songID = int(songID)
             return self.mpd.do.playid(songID)
        else:
             return self.mpd.do.play()

#updated
    def stop(self):
        return self.mpd.do.stop()

#updated
    def prev(self):
        return self.mpd.do.previous()

#updated
    def next(self):
        return self.mpd.do.next()

#updated
    def random(self):
        return self.mpd.do.random(not self.status().random)

#updated
    def repeat(self):
        return self.mpd.do.repeat(not self.status().repeat)

#updated
    def crossfade(self, seconds):
        return self.mpd.do.crossfade(seconds)

	# seek needs song, seconds, TODO:make sure to change this in pympd
#updated
    def seek(self, songID, seek_time):
        return self.mpd.do.seek(int(songID), int(seek_time))

#updated
    def update(self):
        return self.mpd.do.update()
    
#updated
    def _disconnect(self):
        try:
            self.mpd.close()
        except:
            pass


