#!/usr/bin/env python

__author__ = "Abhinav Sarkar <abhinav@abhinavsarkar.net>"
__version__ = "0.2"
__license__ = "GNU Lesser General Public License"
__package__ = "lastfm.mixin"

from lastfm.util import lazylist, logging
from lastfm.decorators import cached_property

def chartable(chart_types):
    def wrapper(cls):
        @cached_property
        def weekly_chart_list(self):
            """
            a list of available weekly charts for this group
            @rtype: L{list} of L{WeeklyChart}
            """
            from lastfm.chart import WeeklyChart
            params = self._default_params(
                {'method': '%s.getWeeklyChartList' % self.__class__.__name__.lower()})
            data = self._api._fetch_data(params).find('weeklychartlist')
            return [
                    WeeklyChart.create_from_data(self._api, self, c)
                    for c in data.findall('chart')
                    ]
    
        @cached_property
        def monthly_chart_list(self):
            from lastfm.chart import MonthlyChart
            return MonthlyChart.get_chart_list(self)
        
        def _default_params(self, extra_params = None):
            if extra_params is not None:
                return extra_params
            else:
                return {}
        
        def get_weekly_album_chart(self, start = None, end = None):
            """
            Get an album chart for the group, for a given date range.
            If no date range is supplied, it will return the most 
            recent album chart for the group. 
            
            @param start:    the date at which the chart should start from (optional)
            @type start:     C{datetime.datetime}
            @param end:      the date at which the chart should end on (optional)
            @type end:       C{datetime.datetime}
            
            @return:         an album chart for the group
            @rtype:          L{WeeklyAlbumChart}
            
            @raise InvalidParametersError: Both start and end parameter have to be either
                                           provided or not provided. Providing only one of
                                           them will raise an exception.
            """
            from lastfm.chart import WeeklyChart, WeeklyAlbumChart
            params = self._default_params(
                {'method': '%s.getWeeklyAlbumChart' % self.__class__.__name__.lower()})
            params = WeeklyChart._check_chart_params(params, self, start, end)
            data = self._api._fetch_data(params).find('weeklyalbumchart')
            return WeeklyAlbumChart.create_from_data(self._api, self, data)
    
        @cached_property
        def recent_weekly_album_chart(self):
            """
            most recent album chart for the group
            @rtype: L{WeeklyAlbumChart}
            """
            return self.get_weekly_album_chart()
    
        @cached_property
        def weekly_album_chart_list(self):
            """
            a list of all album charts for this group in reverse-chronological
            order. (that means 0th chart is the most recent chart)
            @rtype: L{lazylist} of L{WeeklyAlbumChart}
            """
            wcl = list(self.weekly_chart_list)
            wcl.reverse()
            @lazylist
            def gen(lst):
                for wc in wcl:
                    try:
                        yield self.get_weekly_album_chart(wc.start, wc.end)
                    except LastfmError as ex:
                        logging.log_silenced_exceptions(ex)
            return gen()
        
        def get_monthly_album_chart(self, start = None, end = None):
            from lastfm.chart import MonthlyAlbumChart
            return MonthlyAlbumChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_monthly_album_chart(self):
            return self.get_monthly_album_chart()
        
        @cached_property
        def monthly_album_chart_list(self):
            mcl = list(self.monthly_chart_list)
            mcl.reverse()
            @lazylist
            def gen(lst):
                for mc in mcl:
                    try:
                        yield self.get_monthly_album_chart(mc.start, mc.end)
                    except LastfmError as ex:
                        logging.log_silenced_exceptions(ex)
            return gen()
    
        def get_quaterly_album_chart(self, start = None, end = None):
            from lastfm.chart import QuaterlyAlbumChart
            return QuaterlyAlbumChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_quaterly_album_chart(self):
            return self.get_quaterly_album_chart()
        
        def get_half_yearly_album_chart(self, start = None, end = None):
            from lastfm.chart import HalfYearlyAlbumChart
            return HalfYearlyAlbumChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_half_yearly_album_chart(self):
            return self.get_half_yearly_album_chart()
        
        def get_yearly_album_chart(self, start = None, end = None):
            from lastfm.chart import YearlyAlbumChart
            return YearlyAlbumChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_yearly_album_chart(self):
            return self.get_yearly_album_chart()
    
        def get_weekly_artist_chart(self, start = None, end = None):
            """
            Get an artist chart for the group, for a given date range.
            If no date range is supplied, it will return the most 
            recent artist chart for the group. 
            
            @param start:    the date at which the chart should start from (optional)
            @type start:     C{datetime.datetime}
            @param end:      the date at which the chart should end on (optional)
            @type end:       C{datetime.datetime}
            
            @return:         an artist chart for the group
            @rtype:          L{WeeklyArtistChart}
            
            @raise InvalidParametersError: Both start and end parameter have to be either
                                           provided or not provided. Providing only one of
                                           them will raise an exception.
            """
            from lastfm.chart import WeeklyChart, WeeklyArtistChart
            params = self._default_params(
                {'method': '%s.getWeeklyArtistChart' % self.__class__.__name__.lower()})
            params = WeeklyChart._check_chart_params(params, self, start, end)
            data = self._api._fetch_data(params).find('weeklyartistchart')
            return WeeklyArtistChart.create_from_data(self._api, self, data)
    
        @cached_property
        def recent_weekly_artist_chart(self):
            """
            most recent artist chart for the group
            @rtype: L{WeeklyArtistChart}
            """
            return self.get_weekly_artist_chart()
    
        @cached_property
        def weekly_artist_chart_list(self):
            """
            a list of all artist charts for this group in reverse-chronological
            order. (that means 0th chart is the most recent chart)
            @rtype: L{lazylist} of L{WeeklyArtistChart}
            """
            wcl = list(self.weekly_chart_list)
            wcl.reverse()
            @lazylist
            def gen(lst):
                for wc in wcl:
                    try:
                        yield self.get_weekly_artist_chart(wc.start, wc.end)
                    except LastfmError as ex:
                        logging.log_silenced_exceptions(ex)
            return gen()
        
        def get_monthly_artist_chart(self, start = None, end = None):
            from lastfm.chart import MonthlyArtistChart
            return MonthlyArtistChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_monthly_artist_chart(self):
            return self.get_monthly_artist_chart()
        
        @cached_property
        def monthly_artist_chart_list(self):
            mcl = list(self.monthly_chart_list)
            mcl.reverse()
            @lazylist
            def gen(lst):
                for mc in mcl:
                    try:
                        yield self.get_monthly_artist_chart(mc.start, mc.end)
                    except LastfmError as ex:
                        logging.log_silenced_exceptions(ex)
            return gen()
    
        def get_quaterly_artist_chart(self, start = None, end = None):
            from lastfm.chart import QuaterlyArtistChart
            return QuaterlyArtistChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_quaterly_artist_chart(self):
            return self.get_quaterly_artist_chart()
        
        def get_half_yearly_artist_chart(self, start = None, end = None):
            from lastfm.chart import HalfYearlyArtistChart
            return HalfYearlyArtistChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_half_yearly_artist_chart(self):
            return self.get_half_yearly_artist_chart()
        
        def get_yearly_artist_chart(self, start = None, end = None):
            from lastfm.chart import YearlyArtistChart
            return YearlyArtistChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_yearly_artist_chart(self):
            return self.get_yearly_artist_chart()
    
        def get_weekly_track_chart(self, start = None, end = None):
            """
            Get a track chart for the group, for a given date range.
            If no date range is supplied, it will return the most 
            recent artist chart for the group. 
            
            @param start:    the date at which the chart should start from (optional)
            @type start:     C{datetime.datetime}
            @param end:      the date at which the chart should end on (optional)
            @type end:       C{datetime.datetime}
            
            @return:         a track chart for the group
            @rtype:          L{WeeklyTrackChart}
            
            @raise InvalidParametersError: Both start and end parameter have to be either
                                           provided or not provided. Providing only one of
                                           them will raise an exception.
            """
            from lastfm.chart import WeeklyChart, WeeklyTrackChart
            params = self._default_params(
                {'method': '%s.getWeeklyTrackChart' % self.__class__.__name__.lower()})
            params = WeeklyChart._check_chart_params(params, self, start, end)
            data = self._api._fetch_data(params).find('weeklytrackchart')
            return WeeklyTrackChart.create_from_data(self._api, self, data)
    
        @cached_property
        def recent_weekly_track_chart(self):
            """
            most recent track chart for the group
            @rtype: L{WeeklyTrackChart}
            """
            return self.get_weekly_track_chart()
    
        @cached_property
        def weekly_track_chart_list(self):
            """
            a list of all track charts for this group in reverse-chronological
            order. (that means 0th chart is the most recent chart)
            @rtype: L{lazylist} of L{WeeklyTrackChart}
            """
            wcl = list(self.weekly_chart_list)
            wcl.reverse()
            @lazylist
            def gen(lst):
                for wc in wcl:
                    try:
                        yield self.get_weekly_track_chart(wc.start, wc.end)
                    except LastfmError as ex:
                        logging.log_silenced_exceptions(ex)
            return gen()
        
        def get_monthly_track_chart(self, start = None, end = None):
            from lastfm.chart import MonthlyTrackChart
            return MonthlyTrackChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_monthly_track_chart(self):
            return self.get_monthly_track_chart()
        
        @cached_property
        def monthly_track_chart_list(self):
            mcl = list(self.monthly_chart_list)
            mcl.reverse()
            @lazylist
            def gen(lst):
                for mc in mcl:
                    try:
                        yield self.get_monthly_track_chart(mc.start, mc.end)
                    except LastfmError as ex:
                        logging.log_silenced_exceptions(ex)
            return gen()
    
        def get_quaterly_track_chart(self, start = None, end = None):
            from lastfm.chart import QuaterlyTrackChart
            return QuaterlyTrackChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_quaterly_track_chart(self):
            return self.get_quaterly_track_chart()
        
        def get_half_yearly_track_chart(self, start = None, end = None):
            from lastfm.chart import HalfYearlyTrackChart
            return HalfYearlyTrackChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_half_yearly_track_chart(self):
            return self.get_half_yearly_track_chart()
        
        def get_yearly_track_chart(self, start = None, end = None):
            from lastfm.chart import YearlyTrackChart
            return YearlyTrackChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_yearly_track_chart(self):
            return self.get_yearly_track_chart()
    
        def get_weekly_tag_chart(self, start = None, end = None):
            """
            Get a tag chart for the group, for a given date range.
            If no date range is supplied, it will return the most 
            recent tag chart for the group. 
            
            @param start:    the date at which the chart should start from (optional)
            @type start:     C{datetime.datetime}
            @param end:      the date at which the chart should end on (optional)
            @type end:       C{datetime.datetime}
            
            @return:         a tag chart for the group
            @rtype:          L{WeeklyTagChart}
            
            @raise InvalidParametersError: Both start and end parameter have to be either
                                           provided or not provided. Providing only one of
                                           them will raise an exception.
                                           
            @note: This method is a composite method. It is not provided directly by the
                   last.fm API. It uses other methods to collect the data, analyzes it and
                   creates a chart. So this method is a little heavy to call, as it does
                   mulitple calls to the API. 
            """
            from lastfm.chart import WeeklyChart, WeeklyTagChart
            WeeklyChart._check_chart_params({}, self, start, end)
            return WeeklyTagChart.create_from_data(self._api, self, start, end)
    
        @cached_property
        def recent_weekly_tag_chart(self):
            """
            most recent tag chart for the group
            @rtype: L{WeeklyTagChart}
            """
            return self.get_weekly_tag_chart()
    
        @cached_property
        def weekly_tag_chart_list(self):
            """
            a list of all tag charts for this group in reverse-chronological
            order. (that means 0th chart is the most recent chart)
            @rtype: L{lazylist} of L{WeeklyTagChart}
            """
            wcl = list(self.weekly_chart_list)
            wcl.reverse()
            @lazylist
            def gen(lst):
                for wc in wcl:
                    try:
                        yield self.get_weekly_tag_chart(wc.start, wc.end)
                    except LastfmError as ex:
                        logging.log_silenced_exceptions(ex)
            return gen()
        
        def get_monthly_tag_chart(self, start = None, end = None):
            from lastfm.chart import MonthlyTagChart
            return MonthlyTagChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_monthly_tag_chart(self):
            return self.get_monthly_tag_chart()
        
        @cached_property
        def monthly_tag_chart_list(self):
            mcl = list(self.monthly_chart_list)
            mcl.reverse()
            @lazylist
            def gen(lst):
                for mc in mcl:
                    try:
                        yield self.get_monthly_tag_chart(mc.start, mc.end)
                    except LastfmError as ex:
                        logging.log_silenced_exceptions(ex)
            return gen()
        
        def get_quaterly_tag_chart(self, start = None, end = None):
            from lastfm.chart import QuaterlyTagChart
            return QuaterlyTagChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_quaterly_tag_chart(self):
            return self.get_quaterly_tag_chart()
        
        def get_half_yearly_tag_chart(self, start = None, end = None):
            from lastfm.chart import HalfYearlyTagChart
            return HalfYearlyTagChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_half_yearly_tag_chart(self):
            return self.get_half_yearly_tag_chart()
        
        def get_yearly_tag_chart(self, start = None, end = None):
            from lastfm.chart import YearlyTagChart
            return YearlyTagChart.create_from_data(self, start, end)
    
        @cached_property
        def recent_yearly_tag_chart(self):
            return self.get_yearly_tag_chart()
        
        cls.weekly_chart_list = weekly_chart_list
        cls.monthly_chart_list = monthly_chart_list
        
        if not hasattr(cls, '_default_params'):
            cls._default_params = _default_params
        
        if not hasattr(cls, '_mixins'):
            cls._mixins = []
        cls._mixins.extend(['weekly_chart_list', 'monthly_chart_list'])
        
        method_names = [
            'get_weekly_%s_chart', 'recent_weekly_%s_chart', 'weekly_%s_chart_list',
            'get_monthly_%s_chart', 'recent_monthly_%s_chart', 'monthly_%s_chart_list',
            'get_quaterly_%s_chart', 'recent_quaterly_%s_chart',
            'get_half_yearly_%s_chart', 'recent_half_yearly_%s_chart',
            'get_yearly_%s_chart', 'recent_yearly_%s_chart'
        ]    
        for chart_type in chart_types:
            for method_name in method_names:
                setattr(cls, method_name % chart_type, locals()[method_name % chart_type])
                cls._mixins.append(method_name % chart_type)
        
        return cls
    return wrapper

from lastfm.error import LastfmError
    