#!/usr/bin/env python

__author__ = "Abhinav Sarkar <abhinav@abhinavsarkar.net>"
__version__ = "0.1"
__license__ = "GNU Lesser General Public License"

from base import LastfmBase
from lazylist import lazylist
import playlist

class User(LastfmBase):
    """A class representing an user."""
    def init(self,
                 api,
                 name = None,
                 url = None,
                 image = None,
                 stats = None):
        if not isinstance(api, Api):
            raise LastfmInvalidParametersError("api reference must be supplied as an argument")
        self.__api = api
        self.__name = name
        self.__url = url
        self.__image = image
        self.__stats = stats and Stats(
                             subject = self,
                             match = stats.match,
                             weight = stats.weight
                            )
        self.__lirary = User.Library(api, self)

    @property
    def name(self):
        """name of the user"""
        return self.__name

    @property
    def url(self):
        """url of the user's page"""
        return self.__url

    @property
    def image(self):
        """image of the user"""
        return self.__image

    @property
    def stats(self):
        """stats for the user"""
        return self.__stats

    @LastfmBase.cachedProperty
    def events(self):
        params = {'method': 'user.getevents', 'user': self.name}
        data = self.__api._fetchData(params).find('events')

        return [
                Event.createFromData(self.__api, e)
                for e in data.findall('event')
                ]
        
    def getPastEvents(self,
                      limit = None):
        params = {'method': 'user.getpastevents', 'user': self.name}
        if limit is not None:
            params.update({'limit': limit})
                
        @lazylist
        def gen(lst):
            data = self.__api._fetchData(params).find('events')
            totalPages = int(data.attrib['totalPages'])
            
            @lazylist
            def gen2(lst, data):
                for e in data.findall('event'):
                    yield Event.createFromData(self.__api, e)
            
            for e in gen2(data):
                yield e
            
            for page in xrange(2, totalPages+1):
                params.update({'page': page})
                data = self.__api._fetchData(params).find('events')
                for e in gen2(data):
                    yield e            
        return gen()
        
    @LastfmBase.cachedProperty
    def pastEvents(self):
        return self.getPastEvents()

    def getFriends(self,
                   limit = None):
        params = {'method': 'user.getfriends', 'user': self.name}
        if limit is not None:
            params.update({'limit': limit})
        data = self.__api._fetchData(params).find('friends')
        return [
            User(
                self.__api,
                subject = self,
                name = u.findtext('name'),
                image = dict([(i.get('size'), i.text) for i in u.findall('image')]),
                url = u.findtext('url'),
            )
            for u in data.findall('user')
        ]


    @LastfmBase.cachedProperty
    def friends(self):
        """friends of the user"""
        return self.getFriends()
        
    def getNeighbours(self, limit = None):
        params = {'method': 'user.getneighbours', 'user': self.name}
        if limit is not None:
            params.update({'limit': limit})
        data = self.__api._fetchData(params).find('neighbours')
        return [
                User(
                    self.__api,
                    subject = self,
                    name = u.findtext('name'),
                    image = {'medium': u.findtext('image')},
                    url = u.findtext('url'),
                    stats = Stats(
                                  subject = u.findtext('name'),
                                  match = float(u.findtext('match')),
                                  ),
                )
                for u in data.findall('user')
            ]

    @LastfmBase.cachedProperty
    def neighbours(self):
        """neighbours of the user"""
        return self.getNeighbours()
    
    @LastfmBase.topProperty("neighbours")
    def nearestNeighbour(self):
        """nearest neightbour of the user"""
        pass
    
    @LastfmBase.cachedProperty
    def playlists(self):
        """playlists of the user"""
        params = {'method': 'user.getPlaylists', 'user': self.name}
        data = self.__api._fetchData(params).find('playlists')
        return [
                User.Playlist(
                              self.__api,
                              id = int(p.findtext('id')),
                              title = p.findtext('title'),
                              date = datetime(*(
                                                time.strptime(
                                                              p.findtext('date').strip(),
                                                              '%Y-%m-%dT%H:%M:%S'
                                                              )[0:6])
                              ),
                              size = int(p.findtext('size')),
                              creator = self
                              )
                for p in data.findall('playlist')
                ]

    @LastfmBase.cachedProperty
    def lovedTracks(self):
        params = {'method': 'user.getlovedtracks', 'user': self.name}
        data = self.__api._fetchData(params).find('lovedtracks')
        return [
                Track(
                    self.__api,
                    subject = self,
                    name = t.findtext('name'),
                    artist = Artist(
                        self.__api,
                        subject = self,
                        name = t.findtext('artist/name'),
                        mbid = t.findtext('artist/mbid'),
                        url = t.findtext('artist/url'),
                    ),
                    mbid = t.findtext('mbid'),
                    image = dict([(i.get('size'), i.text) for i in t.findall('image')]),
                    lovedOn = datetime(*(
                        time.strptime(
                            t.findtext('date').strip(),
                            '%d %b %Y, %H:%M'
                            )[0:6])
                        )
                    )
                for t in data.findall('track')
                ]
        
    def getRecentTracks(self, limit = None):
        params = {'method': 'user.getrecenttracks', 'user': self.name}
        data = self.__api._fetchData(params, no_cache = True).find('recenttracks')
        return [
                Track(
                      self.__api,
                      subject = self,
                      name = t.findtext('name'),
                      artist = Artist(
                                      self.__api,
                                      subject = self,
                                      name = t.findtext('artist'),
                                      mbid = t.find('artist').attrib['mbid'],
                                      ),
                      album = Album(
                                    self.__api,
                                    subject = self,
                                    name = t.findtext('album'),
                                    artist = Artist(
                                                    self.__api,
                                                    subject = self,
                                                    name = t.findtext('artist'),
                                                    mbid = t.find('artist').attrib['mbid'],
                                                    ),
                                    mbid = t.find('album').attrib['mbid'],
                                    ),
                      mbid = t.findtext('mbid'),
                      streamable = (t.findtext('streamable') == '1'),
                      url = t.findtext('url'),
                      image = dict([(i.get('size'), i.text) for i in t.findall('image')]),
                      playedOn = datetime(*(
                                           time.strptime(
                                                         t.findtext('date').strip(),
                                                         '%d %b %Y, %H:%M'
                                                         )[0:6])
                                           )
                      )
                      for t in data.findall('track')
                      ]

    @property
    def recentTracks(self):
        """recent tracks played by the user"""
        return self.getRecentTracks()

    @LastfmBase.topProperty("recentTracks")
    def mostRecentTrack(self):
        """most recent track played by the user"""
        pass

    def getTopAlbums(self, period = None):
        params = {'method': 'user.gettopalbums', 'user': self.name}
        if period is not None:
            params.update({'period': period})
        data = self.__api._fetchData(params).find('topalbums')

        return [
                Album(
                     self.__api,
                     subject = self,
                     name = a.findtext('name'),
                     artist = Artist(
                                     self.__api,
                                     subject = self,
                                     name = a.findtext('artist/name'),
                                     mbid = a.findtext('artist/mbid'),
                                     url = a.findtext('artist/url'),
                                     ),
                     mbid = a.findtext('mbid'),
                     url = a.findtext('url'),
                     image = dict([(i.get('size'), i.text) for i in a.findall('image')]),
                     stats = Stats(
                                   subject = a.findtext('name'),
                                   playcount = int(a.findtext('playcount')),
                                   rank = int(a.attrib['rank'])
                                   )
                     )
                for a in data.findall('album')
                ]

    @LastfmBase.cachedProperty
    def topAlbums(self):
        """overall top albums of the user"""
        return self.getTopAlbums()
        
    @LastfmBase.topProperty("topAlbums")
    def topAlbum(self):
        """overall top most album of the user"""
        pass

    def getTopArtists(self, period = None):
        params = {'method': 'user.gettopartists', 'user': self.name}
        if period is not None:
            params.update({'period': period})
        data = self.__api._fetchData(params).find('topartists')
        
        return [
                Artist(
                       self.__api,
                       subject = self,
                       name = a.findtext('name'),
                       mbid = a.findtext('mbid'),
                       stats = Stats(
                                     subject = a.findtext('name'),
                                     rank = a.attrib['rank'].strip() and int(a.attrib['rank']) or None,
                                     playcount = a.findtext('playcount') and int(a.findtext('playcount')) or None
                                     ),
                       url = a.findtext('url'),
                       streamable = (a.findtext('streamable') == "1"),
                       image = dict([(i.get('size'), i.text) for i in a.findall('image')]),
                       )
                for a in data.findall('artist')
                ]

    @LastfmBase.cachedProperty
    def topArtists(self):
        """top artists of the user"""
        return self.getTopArtists()
        
    @LastfmBase.topProperty("topArtists")
    def topArtist(self):
        """top artist of the user"""
        pass

    def getTopTracks(self, period = None):
        params = {'method': 'user.gettoptracks', 'user': self.name}
        if period is not None:
            params.update({'period': period})
        data = self.__api._fetchData(params).find('toptracks')
        return [
                Track(
                      self.__api,
                      subject = self,
                      name = t.findtext('name'),
                      artist = Artist(
                                      self.__api,
                                      subject = self,
                                      name = t.findtext('artist/name'),
                                      mbid = t.findtext('artist/mbid'),
                                      url = t.findtext('artist/url'),
                                      ),
                      mbid = t.findtext('mbid'),
                      stats = Stats(
                                    subject = t.findtext('name'),
                                    rank = t.attrib['rank'].strip() and int(t.attrib['rank']) or None,
                                    playcount = t.findtext('playcount') and int(t.findtext('playcount')) or None
                                    ),
                      streamable = (t.findtext('streamable') == '1'),
                      fullTrack = (t.find('streamable').attrib['fulltrack'] == '1'),
                      image = dict([(i.get('size'), i.text) for i in t.findall('image')]),
                      )
                for t in data.findall('track')
                ]

    @LastfmBase.cachedProperty
    def topTracks(self):
        """top tracks of the user"""
        return self.getTopTracks()
        
    @LastfmBase.topProperty("topTracks")
    def topTrack(self):
        """top track of the user"""
        return (len(self.topTracks) and self.topTracks[0] or None)

    def getTopTags(self, limit = None):
        params = {'method': 'user.gettoptags', 'user': self.name}
        if limit is not None:
            params.update({'limit': limit})
        data = self.__api._fetchData(params).find('toptags')
        return [
                Tag(
                    self.__api,
                    subject = self,
                    name = t.findtext('name'),
                    url = t.findtext('url'),
                    stats = Stats(
                                  subject = t.findtext('name'),
                                  count = int(t.findtext('count'))
                                  )
                    ) 
                for t in data.findall('tag')
                ]

    @LastfmBase.cachedProperty
    def topTags(self):
        """top tags of the user"""
        return self.getTopTags()
        
    @LastfmBase.topProperty("topTags")
    def topTag(self):
        """top tag of the user"""
        pass

    @LastfmBase.cachedProperty
    def weeklyChartList(self):
        params = {'method': 'user.getweeklychartlist', 'user': self.name}
        data = self.__api._fetchData(params).find('weeklychartlist')
        return [
                WeeklyChart.createFromData(self.__api, self, c)
                for c in data.findall('chart')
                ]
            
    def getWeeklyAlbumChart(self,
                             start = None,
                             end = None):
        params = {'method': 'user.getweeklyalbumchart', 'user': self.name}
        params = WeeklyChart._checkWeeklyChartParams(params, start, end)            
        data = self.__api._fetchData(params).find('weeklyalbumchart')   
        return WeeklyAlbumChart.createFromData(self.__api, self, data)

    @LastfmBase.cachedProperty
    def recentWeeklyAlbumChart(self):
        return self.getWeeklyAlbumChart()
    
    @LastfmBase.cachedProperty
    def weeklyAlbumChartList(self):
        wcl = list(self.weeklyChartList)
        wcl.reverse()
        @lazylist
        def gen(lst):
            for wc in wcl:
                try:
                    yield self.getWeeklyAlbumChart(wc.start, wc.end)
                except LastfmError:
                    pass
        return gen()

    def getWeeklyArtistChart(self,
                             start = None,
                             end = None):
        params = {'method': 'user.getweeklyartistchart', 'user': self.name}
        params = WeeklyChart._checkWeeklyChartParams(params, start, end)
        data = self.__api._fetchData(params).find('weeklyartistchart')   
        return WeeklyArtistChart.createFromData(self.__api, self, data)

    @LastfmBase.cachedProperty
    def recentWeeklyArtistChart(self):
        return self.getWeeklyArtistChart()
    
    @LastfmBase.cachedProperty
    def weeklyArtistChartList(self):
        wcl = list(self.weeklyChartList)
        wcl.reverse()
        @lazylist
        def gen(lst):
            for wc in wcl:
                try:
                    yield self.getWeeklyArtistChart(wc.start, wc.end)
                except LastfmError:
                    pass
        return gen()

    def getWeeklyTrackChart(self,
                             start = None,
                             end = None):
        params = {'method': 'user.getweeklytrackchart', 'user': self.name}
        params = WeeklyChart._checkWeeklyChartParams(params, start, end)
        data = self.__api._fetchData(params).find('weeklytrackchart')   
        return WeeklyTrackChart.createFromData(self.__api, self, data)

    @LastfmBase.cachedProperty
    def recentWeeklyTrackChart(self):
        return self.getWeeklyTrackChart()
    
    @LastfmBase.cachedProperty
    def weeklyTrackChartList(self):
        wcl = list(self.weeklyChartList)
        wcl.reverse()
        @lazylist
        def gen(lst):
            for wc in wcl:
                try:
                    yield self.getWeeklyTrackChart(wc.start, wc.end)
                except LastfmError:
                    pass
        return gen()
    
    def compare(self, other, limit = None):
        return Tasteometer.compare(self.__api,
                                   'user', 'user',
                                   self.name, other.name,
                                   limit)
    @property
    def library(self):
        return self.__lirary

    @staticmethod
    def hashFunc(*args, **kwds):
        try:
            return hash(kwds['name'])
        except KeyError:
            raise LastfmInvalidParametersError("name has to be provided for hashing")

    def __hash__(self):
        return self.__class__.hashFunc(name = self.name)

    def __eq__(self, other):
        return self.name == other.name

    def __lt__(self, other):
        return self.name < other.name

    def __repr__(self):
        return "<lastfm.User: %s>" % self.name
    
    class Playlist(playlist.Playlist):
        """A class representing a playlist belonging to the user."""
        def init(self, api, id, title, date, size, creator):
            super(User.Playlist, self).init(api, "lastfm://playlist/%s" % id)
            self.__id = id
            self.__title = title
            self.__date = date
            self.__size = size
            self.__creator = creator
            
        @property
        def id(self):
            return self.__id
    
        @property
        def title(self):
            return self.__title
    
        @property
        def date(self):
            return self.__date
    
        @property
        def size(self):
            return self.__size
    
        @property
        def creator(self):
            return self.__creator
        
        @staticmethod
        def hashFunc(*args, **kwds):
            try:
                return hash(kwds['id'])
            except KeyError:
                raise LastfmInvalidParametersError("id has to be provided for hashing")
            
        def __hash__(self):
            return self.__class__.hashFunc(id = self.id)
        
        def __repr__(self):
            return "<lastfm.User.Playlist: %s>" % self.title
        
    class Library(object):
        """A class representing the music library of the user."""
        def __init__(self, api, user):
            self.__api = api
            self.__user = user
            
        @property
        def user(self):
            return self.__user
            
        def getAlbums(self,
                      limit = None):
            params = {'method': 'library.getalbums', 'user': self.user.name}
            if limit is not None:
                params.update({'limit': limit})
            
            @lazylist
            def gen(lst):
                data = self.__api._fetchData(params).find('albums')
                totalPages = int(data.attrib['totalPages'])
                
                @lazylist
                def gen2(lst, data):
                    for a in data.findall('album'):
                        yield Album(
                                    self.__api,
                                    subject = self,
                                    name = a.findtext('name'),
                                    artist = Artist(
                                                    self.__api,
                                                    subject = self,
                                                    name = a.findtext('artist/name'),
                                                    mbid = a.findtext('artist/mbid'),
                                                    url = a.findtext('artist/url'),
                                                    ),
                                    mbid = a.findtext('mbid'),
                                    url = a.findtext('url'),
                                    image = dict([(i.get('size'), i.text) for i in a.findall('image')]),
                                    stats = Stats(
                                                  subject = a.findtext('name'),
                                                  playcount = int(a.findtext('playcount')),
                                                  )
                                    )
                               
                
                for a in gen2(data):
                    yield a
                
                for page in xrange(2, totalPages+1):
                    params.update({'page': page})
                    data = self.__api._fetchData(params).find('albums')
                    for a in gen2(data):
                        yield a            
            return gen()
            
        @LastfmBase.cachedProperty
        def albums(self):
            return self.getAlbums()

        def getArtists(self,
                       limit = None):
            params = {'method': 'library.getartists', 'user': self.user.name}
            if limit is not None:
                params.update({'limit': limit})
            
            @lazylist
            def gen(lst):
                data = self.__api._fetchData(params).find('artists')
                totalPages = int(data.attrib['totalPages'])
                
                @lazylist
                def gen2(lst, data):
                    for a in data.findall('artist'):
                        yield Artist(
                                     self.__api,
                                     subject = self,
                                     name = a.findtext('name'),
                                     mbid = a.findtext('mbid'),
                                     stats = Stats(
                                                   subject = a.findtext('name'),
                                                   playcount = a.findtext('playcount') and int(a.findtext('playcount')) or None,
                                                   tagcount = a.findtext('tagcount') and int(a.findtext('tagcount')) or None
                                                   ),
                                     url = a.findtext('url'),
                                     streamable = (a.findtext('streamable') == "1"),
                                     image = dict([(i.get('size'), i.text) for i in a.findall('image')]),
                                     )                               
                
                for a in gen2(data):
                    yield a
                
                for page in xrange(2, totalPages+1):
                    params.update({'page': page})
                    data = self.__api._fetchData(params).find('artists')
                    for a in gen2(data):
                        yield a            
            return gen()
            
        @LastfmBase.cachedProperty
        def artists(self):
            return self.getArtists()
        
        def getTracks(self,
                      limit = None):
            params = {'method': 'library.gettracks', 'user': self.user.name}
            if limit is not None:
                params.update({'limit': limit})
            
            @lazylist
            def gen(lst):
                data = self.__api._fetchData(params).find('tracks')
                totalPages = int(data.attrib['totalPages'])
                
                @lazylist
                def gen2(lst, data):
                    for t in data.findall('track'):
                        yield Track(
                                    self.__api,
                                    subject = self,
                                    name = t.findtext('name'),
                                    artist = Artist(
                                                    self.__api,
                                                    subject = self,
                                                    name = t.findtext('artist/name'),
                                                    mbid = t.findtext('artist/mbid'),
                                                    url = t.findtext('artist/url'),
                                                    ),
                                    mbid = t.findtext('mbid'),
                                    stats = Stats(
                                                  subject = t.findtext('name'),
                                                  playcount = t.findtext('playcount') and int(t.findtext('playcount')) or None,
                                                  tagcount = t.findtext('tagcount') and int(t.findtext('tagcount')) or None
                                                  ),
                                    streamable = (t.findtext('streamable') == '1'),
                                    fullTrack = (t.find('streamable').attrib['fulltrack'] == '1'),
                                    image = dict([(i.get('size'), i.text) for i in t.findall('image')]),
                                    )                            
                
                for t in gen2(data):
                    yield t
                
                for page in xrange(2, totalPages+1):
                    params.update({'page': page})
                    data = self.__api._fetchData(params).find('tracks')
                    for t in gen2(data):
                        yield t            
            return gen()
        
        @LastfmBase.cachedProperty
        def tracks(self):
            return self.getTracks()
        
        @staticmethod
        def hashFunc(*args, **kwds):
            try:
                return hash(kwds['user'])
            except KeyError:
                raise LastfmInvalidParametersError("user has to be provided for hashing")
    
        def __hash__(self):
            return self.__class__.hashFunc(user = self.user)
            
        def __repr__(self):
            return "<lastfm.User.Library: for user '%s'>" % self.user.name

from datetime import datetime
import time

from api import Api
from artist import Artist
from album import Album
from error import LastfmError, LastfmInvalidParametersError
from event import Event
from stats import Stats
from tag import Tag
from tasteometer import Tasteometer
from track import Track
from weeklychart import WeeklyChart, WeeklyAlbumChart, WeeklyArtistChart, WeeklyTrackChart