import re
import xbmc
from math import pi, sin

from lib.providers import base
from lib.providers.base import AbstractImageProvider, build_key_error, cache, ProviderError
from lib.libs import mediatypes
from lib.libs.addonsettings import settings
from lib.libs.pykodi import json, UTF8JSONDecoder
from lib.libs.utils import SortedDisplay

class TheTVDBProvider(AbstractImageProvider):
    name = SortedDisplay('thetvdb.com', 'TheTVDB.com')
    mediatype = mediatypes.TVSHOW
    contenttype = 'application/vnd.thetvdb.v2.1.0'

    apiurl = 'https://api.thetvdb.com/series/%s/images/query'
    loginurl = 'https://api.thetvdb.com/login'
    imageurl_base = 'https://www.thetvdb.com/banners/'

    artmap = {'fanart': 'fanart', 'poster': 'poster', 'season': mediatypes.SEASON + '.%s.poster',
        'seasonwide': mediatypes.SEASON + '.%s.banner', 'series': 'banner'}

    def get_data(self, mediaid, arttype, language):
        result = cache.cacheFunction(self._get_data, mediaid, arttype, language)
        return result if result != 'Empty' else None

    def _get_data(self, mediaid, arttype, language):
        if not settings.get_apikey('tvdb'):
            raise build_key_error('tvdb')
        self.log('uncached', xbmc.LOGINFO)
        getparams = {'params': {'keyType': arttype}, 'headers': {'Accept-Language': language}}
        response = self.doget(self.apiurl % mediaid, **getparams)
        return 'Empty' if response is None else json.loads(response.text, cls=UTF8JSONDecoder)

    def _get_rating(self, image):
        if image['ratingsInfo']['count']:
            info = image['ratingsInfo']
            rating = info['average']
            if info['count'] < 5:
                # Reweigh ratings, decrease difference from 5
                rating = 5 + (rating - 5) * sin(info['count'] / pi)
            return SortedDisplay(rating, '{0:.1f} stars'.format(info['average']))
        else:
            return SortedDisplay(5, 'Not rated')

    def get_images(self, uniqueids, types=None):
        if not settings.get_apienabled('tvdb'):
            return {}
        if types is not None and not self.provides(types):
            return {}
        mediaid = get_mediaid(uniqueids)
        if not mediaid:
            return {}
        result = {}
        languages = base.languages
        # Useful fanart can be hidden by the language filter, try a few of the most frequently used
        flanguages = ['en', 'de', 'fr', 'es', 'ru']
        flanguages.extend(lang for lang in languages if lang not in flanguages)
        for arttype in self.artmap:
            if types and not typematches(self.artmap[arttype], types):
                continue
            arttype_error = False
            for language in languages if arttype != 'fanart' else flanguages:
                generaltype = self.artmap[arttype]
                data = self.get_data(mediaid, arttype, language)
                if not data:
                    continue
                isseason = arttype.startswith('season')
                if not isseason:
                    if generaltype not in result:
                        result[generaltype] = []
                for image in data['data']:
                    if not image.get('fileName') or isseason and not image.get('subKey'):
                        continue
                    ntype = generaltype
                    if isseason:
                        try:
                            int(image['subKey'])
                        except ValueError:
                            if arttype_error:
                                continue
                            arttype_error = True
                            self.log("Provider returned unexpected content and '{0}' ".format(arttype) +
                                    "artwork could not be processed:\n" +
                                    "expected a season number but got '{0}'".format(image['subKey']),
                                xbmc.LOGWARNING)
                            continue
                        ntype = ntype % image['subKey']
                        if ntype not in result:
                            result[ntype] = []
                    resultimage = {'provider': self.name}
                    resultimage['url'] = self.imageurl_base + image['fileName']
                    resultimage['preview'] = self.imageurl_base + (image['thumbnail'] or '_cache/' + image['fileName'])
                    resultimage['language'] = language if shouldset_imagelanguage(image) else None
                    resultimage['rating'] = self._get_rating(image)
                    if arttype in ('series', 'seasonwide'):
                        resultimage['size'] = SortedDisplay(758, '758x140')
                    elif arttype == 'season':
                        resultimage['size'] = SortedDisplay(1000, '680x1000')
                    else:
                        resultimage['size'] = parse_sortsize(image, arttype)
                    result[ntype].append(resultimage)
        return result

    def login(self):
        response = self.getter.session.post(self.loginurl, json={'apikey': settings.get_apikey('tvdb')},
            headers={'Content-Type': 'application/json', 'User-Agent': settings.useragent}, timeout=15)
        if response is not None and response.status_code == 401:
            raise build_key_error('tvdb')
        response.raise_for_status()
        if not response or not response.headers['Content-Type'].startswith('application/json'):
            raise ProviderError("Provider returned unexpected content")
        self.getter.session.headers['authorization'] = 'Bearer %s' % response.json()['token']
        return True

    def provides(self, types):
        types = set(x if not x.startswith('season.') else re.sub(r'[\d]', '%s', x) for x in types)
        return any(x in types for x in self.artmap.values())

def parse_sortsize(image, arttype):
    try:
        sortsize = int(image['resolution'].split('x')[0 if arttype != 'poster' else 1])
    except (ValueError, IndexError):
        sortsize = 0
    return SortedDisplay(sortsize, image['resolution'])

def shouldset_imagelanguage(image):
    if image['keyType'] == 'series':
        return image['subKey'] != 'blank'
    elif image['keyType'] == 'fanart':
        return image['subKey'] == 'text'
    return True

def typematches(arttype, types):
    return any(x for x in types if arttype == (x if not x.startswith('season.') else re.sub(r'[\d]', '%s', x)))

def get_mediaid(uniqueids):
    for source in ('tvdb', 'unknown'):
        if source in uniqueids:
            return uniqueids[source]
