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

import re

from core import jsontools, scrapertools
from core.item import InfoLabels
from platformcode import config, logger, platformtools


PY3 = False
if config.get_setting('PY3', default=''): PY3 = True

if PY3:
    import urllib as urllib2
    import urllib.error as urllib2
    import urllib.parse as urllib
    import urllib.request as urllib2
else:
    import urllib
    import urllib2


HOST = "https://api.thetvdb.com"
HOST_IMAGE = "https://thetvdb.com/banners/"


TOKEN = config.get_setting("tvdb_token", default="")
DEFAULT_LANG = "es"


DEFAULT_HEADERS = {
    'Content-Type': 'application/json',
    'Accept': 'application/json, application/vnd.thetvdb.v2.1.1',
    'Accept-Language': DEFAULT_LANG,
    'Authorization': 'Bearer ' + TOKEN,
}


# ~ Traducciones - Inicio
DICT_STATUS = {'Continuing': 'En emisión', 'Ended': 'Finalizada'}


DICT_GENRE = {
    'Action': 'Acción',
    'Adventure': 'Aventura',
    'Animation': 'Animación',
    'Children': 'Niños',
    'Comedy': 'Comedia',
    'Crime': 'Crimen',
    'Documentary': 'Documental',
    # 'Drama': 'Drama',
    'Family': 'Familiar',
    'Fantasy': 'Fantasía',
    'Food': 'Comida',
    'Game Show': 'Concurso',
    'Home and Garden': 'Hogar y Jardín',
    # 'Horror': 'Horror', 'Mini-Series': 'Mini-Series',
    'Mystery': 'Misterio',
    'News': 'Noticias',
    # 'Reality': 'Telerrealidad',
    'Romance': 'Romántico',
    'Science-Fiction': 'Ciencia-Ficción',
    'Soap': 'Telenovela',
    # 'Special Interest': 'Special Interest',
    'Sport': 'Deporte',
    # 'Suspense': 'Suspense',
    'Talk Show': 'Programa de Entrevistas',
    # 'Thriller': 'Thriller',
    'Travel': 'Viaje',
    # 'Western': 'Western'
}


DICT_MPAA = {'TV-Y': 'Público pre-infantil: niños menores de 6 años', 'TV-Y7': 'Público infantil: desde 7 años',
             'TV-G': 'Público general: sin supervisión familiar', 'TV-PG': 'Guía paterna: Supervisión paternal',
             'TV-14': 'Mayores de 14 años', 'TV-MA': 'Mayores de 17 años'}


otvdb_global = None


def find_and_set_infoLabels(item):
    logger.info()

    p_dialog = None
    if not item.contentSeason:
        p_dialog = platformtools.dialog_progress_bg("Buscando información de la serie", "Espere por favor...")

    global otvdb_global
    tvdb_result = None

    title = item.contentSerieName

    # ~ Si el titulo incluye el (año) se lo quitamos
    year = scrapertools.find_single_match(title, "^.+?\s*(\(\d{4}\))$")
    if year:
        title = title.replace(year, "").strip()
        item.infoLabels['year'] = year[1:-1]

    if not item.infoLabels.get("tvdb_id"):
        if not item.infoLabels.get("imdb_id"):
            otvdb_global = Tvdb(search=title, year=item.infoLabels['year'])
        else:
            otvdb_global = Tvdb(imdb_id=item.infoLabels.get("imdb_id"))

    elif not otvdb_global or otvdb_global.get_id() != item.infoLabels['tvdb_id']:
        otvdb_global = Tvdb(tvdb_id=item.infoLabels['tvdb_id'])

    if not item.contentSeason:
        p_dialog.update(50, "Buscando información de la serie", "Obteniendo resultados...")

    results, info_load = otvdb_global.get_list_results()

    if not item.contentSeason:
        p_dialog.update(100, "Buscando información de la serie", "Encontrados %s posibles coincidencias" % len(results))
        p_dialog.close()

    if len(results) > 1:
        logger.debug('TODO platformtools.show_video_info')
        # ~ tvdb_result = platformtools.show_video_info(results, item=item, scraper=Tvdb,
                          # ~ caption="[%s]: Selecciona la serie correcta" % title)
    elif len(results) > 0:
        tvdb_result = results[0]

    # ~ todo revisar
    if isinstance(item.infoLabels, InfoLabels):
        # ~ logger.debug("Es instancia de infoLabels")
        infoLabels = item.infoLabels
    else:
        # ~ logger.debug("NO ES instancia de infoLabels")
        infoLabels = InfoLabels()

    if tvdb_result:
        infoLabels['tvdb_id'] = tvdb_result['id']
        infoLabels['url_scraper'] = ["https://thetvdb.com/index.php?tab=series&id=%s" % infoLabels['tvdb_id']]
        if not info_load:
            if otvdb_global.get_id() != infoLabels['tvdb_id']:
                otvdb_global = Tvdb(tvdb_id=infoLabels['tvdb_id'])
            otvdb_global.get_images(infoLabels['tvdb_id'], image="poster")
            otvdb_global.get_images(infoLabels['tvdb_id'], image="fanart")
            otvdb_global.get_tvshow_cast(infoLabels['tvdb_id'])

        item.infoLabels = infoLabels
        set_infoLabels_item(item)

        return True

    else:
        item.infoLabels = infoLabels
        return False


def set_infoLabels_item(item):
    """
        Obtiene y fija (item.infoLabels) los datos extras de una serie, capitulo o pelicula.
        @param item: Objeto que representa un pelicula, serie o capitulo. El atributo infoLabels sera modificado
            incluyendo los datos extras localizados.
        @type item: Item


    """

    global otvdb_global

    def __leer_datos(otvdb_aux):
        item.infoLabels = otvdb_aux.get_infoLabels(item.infoLabels)
        if 'infoLabels' in item and 'thumbnail' in item.infoLabels:
            item.thumbnail = item.infoLabels['thumbnail']
        if 'infoLabels' in item and 'fanart' in item.infoLabels['fanart']:
            item.fanart = item.infoLabels['fanart']

    if 'infoLabels' in item and 'season' in item.infoLabels:
        try:
            int_season = int(item.infoLabels['season'])
        except ValueError:
            # ~ logger.debug("El numero de temporada no es valido")
            item.contentType = item.infoLabels['mediatype']
            return -1 * len(item.infoLabels)

        if not otvdb_global or \
                (item.infoLabels['tvdb_id'] and otvdb_global.get_id() != item.infoLabels['tvdb_id']) \
                or (otvdb_global.search_name and otvdb_global.search_name != item.infoLabels['tvshowtitle']):
            if item.infoLabels['tvdb_id']:
                otvdb_global = Tvdb(tvdb_id=item.infoLabels['tvdb_id'])
            else:
                otvdb_global = Tvdb(search=item.infoLabels['tvshowtitle'])

            __leer_datos(otvdb_global)

        if item.infoLabels['episode']:
            try:
                int_episode = int(item.infoLabels['episode'])
            except ValueError:
                # ~ logger.debug("El número de episodio (%s) no es valido" % repr(item.infoLabels['episode']))
                item.contentType = item.infoLabels['mediatype']
                return -1 * len(item.infoLabels)

            # ~ Tenemos numero de temporada y numero de episodio validos...
            # ~ ... buscar datos episodio
            item.infoLabels['mediatype'] = 'episode'

            lang = DEFAULT_LANG
            if otvdb_global.lang:
                lang = otvdb_global.lang

            page = 1
            _id = None
            while not _id:
                list_episodes = otvdb_global.list_episodes.get(page)
                if not list_episodes:
                    list_episodes = otvdb_global.get_list_episodes(otvdb_global.get_id(), page)
                    # ~ import threading
                    # ~ semaforo = threading.Semaphore(20)
                    # ~ l_hilo = list()

                    # ~ for e in list_episodes["data"]:
                        # ~ t = threading.Thread(target=otvdb_global.get_episode_by_id, args=(e["id"], lang, semaforo))
                        # ~ t.start()
                        # ~ l_hilo.append(t)

                    # ~ # esperar q todos los hilos terminen
                    # ~ for x in l_hilo:
                        # ~ x.join()

                for e in list_episodes['data']:
                    if e['airedSeason'] == int_season and e['airedEpisodeNumber'] == int_episode:
                        _id = e['id']
                        break

                _next = list_episodes['links']['next']
                if isinstance(_next, int):
                    page = _next
                else:
                    break

            data_episode = otvdb_global.get_info_episode(otvdb_global.get_id(), int_season, int_episode, lang, _id)
            # ~ logger.debug(data_episode)

            # ~ todo repasar valores que hay que insertar en infoLabels
            if data_episode:
                item.infoLabels['title'] = data_episode['episodeName']
                # ~ fix en casos que el campo desde la api era null--> None
                if data_episode["overview"] is not None:
                    item.infoLabels['plot'] = data_episode["overview"]

                thumb = data_episode.get('filename', "")
                if thumb != '':
                    item.thumbnail = HOST_IMAGE + thumb
                    item.infoLabels['episodio_imagen'] = item.thumbnail

                item.infoLabels["rating"] = data_episode.get("siteRating", "")
                item.infoLabels['director'] = ', '.join(sorted(data_episode.get('directors', [])))
                item.infoLabels['writer'] = ', '.join(sorted(data_episode.get("writers", [])))

                if data_episode["firstAired"]:
                    item.infoLabels['premiered'] = data_episode["firstAired"].split("-")[2] + "/" + \
                                                   data_episode["firstAired"].split("-")[1] + "/" + \
                                                   data_episode["firstAired"].split("-")[0]
                    item.infoLabels['aired'] = item.infoLabels['premiered']

                guest_stars = data_episode.get("guestStars", [])
                l_castandrole = item.infoLabels.get("castandrole", [])
                l_castandrole.extend([(p, '') for p in guest_stars])
                item.infoLabels['castandrole'] = l_castandrole

                return len(item.infoLabels)

        else:
            # ~ Tenemos numero de temporada valido pero no numero de episodio...
            # ~ ... buscar datos temporada
            # ~ No hay info de la temporada en tvdb, solamente el poster
            item.infoLabels['mediatype'] = 'season'
            data_season = otvdb_global.get_images(otvdb_global.get_id(), "season", int_season)

            if data_season and 'image_season_%s' % int_season in data_season:
                item.thumbnail = HOST_IMAGE + data_season['image_season_%s' % int_season][0]['fileName']
                item.infoLabels['thumbnail'] = item.thumbnail
                item.infoLabels['temporada_poster'] = item.thumbnail
                return len(item.infoLabels)

    # ~ Buscar...
    else:
        # ~ Busquedas por ID...
        if (not otvdb_global or otvdb_global.get_id() != item.infoLabels['tvdb_id']) and item.infoLabels['tvdb_id']:
            otvdb_global = Tvdb(tvdb_id=item.infoLabels['tvdb_id'])

        elif not otvdb_global and item.infoLabels['imdb_id']:
            otvdb_global = Tvdb(imdb_id=item.infoLabels['imdb_id'])

        elif not otvdb_global and item.infoLabels['zap2it_id']:
            otvdb_global = Tvdb(zap2it_id=item.infoLabels['zap2it_id'])

        # ~ No se ha podido buscar por ID... se hace por título
        if otvdb_global is None:
            otvdb_global = Tvdb(search=item.infoLabels['tvshowtitle'])

        if otvdb_global and otvdb_global.get_id():
            otvdb_global.get_images(otvdb_global.get_id(), image='poster')
            otvdb_global.get_images(otvdb_global.get_id(), image='fanart')
            __leer_datos(otvdb_global)
            # ~ La busqueda ha encontrado un resultado valido
            return len(item.infoLabels)


class Tvdb:
    def __init__(self, **kwargs):

        self.__check_token()

        self.result = {}
        self.list_results = []
        self.lang = ""
        self.search_name = kwargs['search'] = \
            re.sub('\[\\\?(B|I|COLOR)\s?[^\]]*\]', '', kwargs.get('search', ''))
        self.list_episodes = {}
        self.episodes = {}

        if kwargs.get('tvdb_id', ''):
            # ~ Busqueda por identificador tvdb
            self.__get_by_id(kwargs.get('tvdb_id', ''))
            if not self.list_results and config.get_setting('tvdb_retry_eng', default=True):
                from platformcode import platformtools
                platformtools.dialog_notification("No se ha encontrado en idioma '%s'" % DEFAULT_LANG, "Se busca en idioma 'en'", sound=False)
                self.__get_by_id(kwargs.get('tvdb_id', ''), "en")
                self.lang = "en"

        elif self.search_name:
            # ~ Busqueda por texto
            self.__search(kwargs.get('search', ''), kwargs.get('imdb_id', ''), kwargs.get('zap2it_id', ''))
            if not self.list_results and config.get_setting('tvdb_retry_eng', default=True):
                from platformcode import platformtools
                platformtools.dialog_notification("No se ha encontrado en idioma '%s'" % DEFAULT_LANG, "Se busca en idioma 'en'")
                self.__search(kwargs.get('search', ''), kwargs.get('imdb_id', ''), kwargs.get('zap2it_id', ''), "en")
                self.lang = "en"

        if not self.result:
            # ~ No hay resultados de la busqueda
            if kwargs.get('tvdb_id', ''):
                buscando = kwargs.get('tvdb_id', '')
            else:
                buscando = kwargs.get('search', '')
            msg = "La busqueda de %s no dio resultados." % buscando
            # ~ logger.debug(msg)

    @classmethod
    def __check_token(cls):
        if TOKEN == "":
            cls.__login()
        else:
            # ~ si la fecha no se corresponde con la actual llamamos a refresh_token, ya que el token expira en 24 horas
            from time import gmtime, strftime
            current_date = strftime("%Y-%m-%d", gmtime())

            if config.get_setting("tvdb_token_date", "") != current_date:
                # ~ si se ha renovado el token grabamos la nueva fecha
                if cls.__refresh_token():
                    config.set_setting("tvdb_token_date", current_date)

    @staticmethod
    def __login():
        global TOKEN

        apikey = "106B699FDC04301C"

        url = HOST + "/login"
        params = {"apikey": apikey}

        try:
            req = urllib2.Request(url, data=jsontools.dump(params), headers=DEFAULT_HEADERS)
            response = urllib2.urlopen(req)
            html = response.read()
            response.close()
        except Exception as ex:
            message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
            logger.error("error en: %s" % message)

        else:
            dict_html = jsontools.load(html)
            # ~ logger.debug("dict_html %s" % dict_html)

            if "token" in dict_html:
                token = dict_html["token"]
                DEFAULT_HEADERS["Authorization"] = "Bearer " + token

                TOKEN = config.set_setting("tvdb_token", token)

    @classmethod
    def __refresh_token(cls):
        global TOKEN
        is_success = False

        url = HOST + "/refresh_token"

        try:
            req = urllib2.Request(url, headers=DEFAULT_HEADERS)
            response = urllib2.urlopen(req)
            html = response.read()
            response.close()
        except urllib2.HTTPError as err:
            # ~ logger.error("err.code es %s" % err.code)
            # ~ si hay error 401 es que el token se ha pasado de tiempo y tenemos que volver a llamar a login
            if err.code == 401:
                cls.__login()
            else:
                raise
        except Exception as ex:
            message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
            logger.error("error en: %s" % message)

        else:
            dict_html = jsontools.load(html)
            # ~ logger.error("tokencito %s" % dict_html)
            if "token" in dict_html:
                token = dict_html["token"]
                DEFAULT_HEADERS["Authorization"] = "Bearer " + token
                TOKEN = config.set_setting("tvdb_token", token)
                is_success = True

        return is_success

    def get_info_episode(self, _id, season=1, episode=1, lang=DEFAULT_LANG, id_episode=None):
        """
        Devuelve los datos de un episodio.
        @param _id: identificador de la serie
        @type _id: str
        @param season: numero de temporada [por defecto = 1]
        @type season: int
        @param episode: numero de episodio [por defecto = 1]
        @type episode: int
        @param lang: codigo de idioma para buscar
        @type lang: str
        @param id_episode: codigo del episodio.
        @type id_episode: int
        @rtype: dict
        @return:
        "data": {
                    "id": 0,
                    "airedSeason": 0,
                    "airedEpisodeNumber": 0,
                    "episodeName": "string",
                    "firstAired": "string",
                    "guestStars": [
                        "string"
                    ],
                    "director": "string", # deprecated
                    "directors": [
                        "string"
                    ],
                    "writers": [
                        "string"
                    ],
                    "overview": "string",
                    "productionCode": "string",
                    "showUrl": "string",
                    "lastUpdated": 0,
                    "dvdDiscid": "string",
                    "dvdSeason": 0,
                    "dvdEpisodeNumber": 0,
                    "dvdChapter": 0,
                    "absoluteNumber": 0,
                    "filename": "string",
                    "seriesId": "string",
                    "lastUpdatedBy": "string",
                    "airsAfterSeason": 0,
                    "airsBeforeSeason": 0,
                    "airsBeforeEpisode": 0,
                    "thumbAuthor": 0,
                    "thumbAdded": "string",
                    "thumbWidth": "string",
                    "thumbHeight": "string",
                    "imdbId": "string",
                    "siteRating": 0,
                    "siteRatingCount": 0
                },
        "errors": {
            "invalidFilters": [
                "string"
            ],
            "invalidLanguage": "string",
            "invalidQueryParams": [
                "string"
            ]
        }
        """

        logger.info()
        if id_episode and self.episodes.get(id_episode):
            return self.episodes.get(id_episode)

        params = {"airedSeason": "%s" % season, "airedEpisode": "%s" % episode}

        try:
            params = urllib.urlencode(params)

            url = HOST + "/series/%s/episodes/query?%s" % (_id, params)
            DEFAULT_HEADERS["Accept-Language"] = lang
            # ~ logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))

            req = urllib2.Request(url, headers=DEFAULT_HEADERS)
            response = urllib2.urlopen(req)
            html = response.read()
            response.close()
        except Exception as ex:
            message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
            logger.error("error en: %s" % message)

        else:
            dict_html = jsontools.load(html)

            if "data" in dict_html and "id" in dict_html["data"][0]:
                self.get_episode_by_id(dict_html["data"][0]["id"], lang)
                return dict_html["data"][0]

    def get_list_episodes(self, _id, page=1):
        """
        Devuelve el listado de episodios de una serie.
        @param _id: identificador de la serie
        @type _id: str
        @param page: numero de pagina a buscar [por defecto = 1]
        @type page: int
        @rtype: dict
        @return:
        {
            "links": {
                "first": 0,
                "last": 0,
                "next": 0,
                "previous": 0
              },
            "data": [
                {
                    "absoluteNumber": 0,
                    "airedEpisodeNumber": 0,
                    "airedSeason": 0,
                    "dvdEpisodeNumber": 0,
                    "dvdSeason": 0,
                    "episodeName": "string",
                    "id": 0,
                    "overview": "string",
                    "firstAired": "string",
                    "lastUpdated": 0
                }
            ],
            "errors": {
                "invalidFilters": [
                  "string"
                ],
                "invalidLanguage": "string",
                "invalidQueryParams": [
                  "string"
                ]
            }
        }
        """

        logger.info()

        try:
            url = HOST + "/series/%s/episodes?page=%s" % (_id, page)
            # ~ logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))

            req = urllib2.Request(url, headers=DEFAULT_HEADERS)
            response = urllib2.urlopen(req)
            html = response.read()
            response.close()
        except Exception as ex:
            message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
            logger.error("error en: %s" % message)

        else:
            self.list_episodes[page] = jsontools.load(html)
            # ~ logger.info("dict_html %s" % self.list_episodes)

            return self.list_episodes[page]

    def get_episode_by_id(self, _id, lang=DEFAULT_LANG, semaforo=None):
        """
        Obtiene los datos de un episodio
        @param _id: identificador del episodio
        @type _id: str
        @param lang: código de idioma
        @param semaforo: semaforo para multihilos
        @type semaforo: threading.Semaphore
        @type lang: str
        @rtype: dict
        @return:
        {
            "data": {
                "id": 0,
                "airedSeason": 0,
                "airedEpisodeNumber": 0,
                "episodeName": "string",
                "firstAired": "string",
                "guestStars": [
                  "string"
                ],
                "director": "string",
                "directors": [
                  "string"
                ],
                "writers": [
                  "string"
                ],
                "overview": "string",
                "productionCode": "string",
                "showUrl": "string",
                "lastUpdated": 0,
                "dvdDiscid": "string",
                "dvdSeason": 0,
                "dvdEpisodeNumber": 0,
                "dvdChapter": 0,
                "absoluteNumber": 0,
                "filename": "string",
                "seriesId": "string",
                "lastUpdatedBy": "string",
                "airsAfterSeason": 0,
                "airsBeforeSeason": 0,
                "airsBeforeEpisode": 0,
                "thumbAuthor": 0,
                "thumbAdded": "string",
                "thumbWidth": "string",
                "thumbHeight": "string",
                "imdbId": "string",
                "siteRating": 0,
                "siteRatingCount": 0
            },
            "errors": {
            "invalidFilters": [
              "string"
            ],
            "invalidLanguage": "string",
            "invalidQueryParams": [
              "string"
            ]
            }
        }
        """

        if semaforo:
            semaforo.acquire()
        logger.info()

        url = HOST + "/episodes/%s" % _id

        try:
            DEFAULT_HEADERS["Accept-Language"] = lang
            # ~ logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))
            req = urllib2.Request(url, headers=DEFAULT_HEADERS)
            response = urllib2.urlopen(req)
            html = response.read()
            response.close()
        except Exception as ex:
            if isinstance(ex, urllib_error.HTTPError):
                logger.debug("code es %s " % ex.code)

            message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
            logger.error("error en: %s" % message)

        else:
            dict_html = jsontools.load(html)
            dict_html = dict_html.pop("data")

            # ~ logger.info("dict_html %s" % dict_html)
            self.episodes[_id] = dict_html

        if semaforo:
            semaforo.release()

    def __search(self, name, imdb_id, zap2it_id, lang=DEFAULT_LANG):
        """
        Busca una serie a través de una serie de parámetros.
        @param name: nombre a buscar
        @type name: str
        @param imdb_id: codigo identificativo de imdb
        @type imdb_id: str
        @param zap2it_id: codigo identificativo de zap2it
        @type zap2it_id: str
        @param lang: código de idioma
        @type lang: str

        data:{
          "aliases": [
            "string"
          ],
          "banner": "string",
          "firstAired": "string",
          "id": 0,
          "network": "string",
          "overview": "string",
          "seriesName": "string",
          "status": "string"
        }
        """

        logger.info()

        try:
            params = {}
            if name:
                params["name"] = name
            elif imdb_id:
                params["imdbId"] = imdb_id
            elif zap2it_id:
                params["zap2itId"] = zap2it_id

            params = urllib.urlencode(params)

            DEFAULT_HEADERS["Accept-Language"] = lang
            url = HOST + "/search/series?%s" % params
            # ~ logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))

            req = urllib2.Request(url, headers=DEFAULT_HEADERS)
            response = urllib2.urlopen(req)
            html = response.read()
            response.close()
        except Exception as ex:
            if isinstance(ex, urllib_error.HTTPError):
                logger.debug("code es %s " % ex.code)

            message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
            logger.error("error en: %s" % message)

        else:
            dict_html = jsontools.load(html)

            if "errors" in dict_html and "invalidLanguage" in dict_html["errors"]:
                # ~ no hay información en idioma por defecto
                return

            else:
                resultado = dict_html["data"]

                # ~ todo revisar
                if len(resultado) > 1:
                    index = 0
                else:
                    index = 0

                # ~ logger.debug("resultado %s" % resultado)
                self.list_results = resultado
                self.result = resultado[index]

    def __get_by_id(self, _id, lang=DEFAULT_LANG, from_get_list=False):
        """
        Obtiene los datos de una serie por identificador.
        @param _id: código de la serie
        @type _id: str
        @param lang: código de idioma
        @type lang: str
        @rtype: dict
        @return:
        {
        "data": {
            "id": 0,
            "seriesName": "string",
            "aliases": [
              "string"
            ],
            "banner": "string",
            "seriesId": 0,
            "status": "string",
            "firstAired": "string",
            "network": "string",
            "networkId": "string",
            "runtime": "string",
            "genre": [
              "string"
            ],
            "overview": "string",
            "lastUpdated": 0,
            "airsDayOfWeek": "string",
            "airsTime": "string",
            "rating": "string",
            "imdbId": "string",
            "zap2itId": "string",
            "added": "string",
            "siteRating": 0,
            "siteRatingCount": 0
        },
        "errors": {
            "invalidFilters": [
              "string"
            ],
            "invalidLanguage": "string",
            "invalidQueryParams": [
              "string"
            ]
            }
        }
        """

        logger.info()
        resultado = {}

        url = HOST + "/series/%s" % _id

        try:
            DEFAULT_HEADERS["Accept-Language"] = lang
            req = urllib2.Request(url, headers=DEFAULT_HEADERS)
            # ~ logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))

            response = urllib2.urlopen(req)
            html = response.read()
            response.close()
        except Exception as ex:
            if isinstance(ex, urllib_error.HTTPError):
                logger.debug("code es %s " % ex.code)

            message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
            logger.error("error en: %s" % message)

        else:
            dict_html = jsontools.load(html)

            if "errors" in dict_html and "invalidLanguage" in dict_html["errors"]:
                return {}
            else:
                resultado = dict_html["data"]
                if not resultado and from_get_list:
                    return self.__get_by_id(_id, "en")

                # ~ logger.debug("resultado %s" % dict_html)
                self.list_results = [resultado]
                self.result = resultado

        return resultado

    def get_images(self, _id, image="poster", season=1, lang="en"):
        """
        Obtiene un tipo de imagen para una serie para un idioma.
        @param _id: identificador de la serie
        @type _id: str
        @param image: codigo de busqueda, ["poster" (por defecto), "fanart", "season"]
        @type image: str
        @type season: numero de temporada
        @param lang: código de idioma para el que se busca
        @type lang: str
        @return: diccionario con el tipo de imagenes elegidas.
        @rtype: dict

        """

        logger.info()

        if self.result.get('image_season_%s' % season):
            return self.result['image_season_%s' % season]

        params = {}
        if image == "poster":
            params["keyType"] = "poster"
        elif image == "fanart":
            params["keyType"] = "fanart"
            params["subKey"] = "graphical"
        elif image == "season":
            params["keyType"] = "season"
            params["subKey"] = "%s" % season
            image += "_%s" % season

        try:
            params = urllib.urlencode(params)
            DEFAULT_HEADERS["Accept-Language"] = lang
            url = HOST + "/series/%s/images/query?%s" % (_id, params)
            # ~ logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))

            req = urllib2.Request(url, headers=DEFAULT_HEADERS)
            response = urllib2.urlopen(req)
            html = response.read()
            response.close()
        except Exception as ex:
            message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
            logger.error("error en: %s" % message)

            return {}

        else:
            dict_html = jsontools.load(html)

            dict_html["image_" + image] = dict_html.pop("data")
            self.result.update(dict_html)

            return dict_html

    def get_tvshow_cast(self, _id, lang=DEFAULT_LANG):
        """
        obtiene el casting de una serie
        @param _id: codigo de la serie
        @type _id: str
        @param lang: codigo idioma para buscar
        @type lang: str
        @return: diccionario con los actores
        @rtype: dict
        """

        logger.info()

        url = HOST + "/series/%s/actors" % _id
        DEFAULT_HEADERS["Accept-Language"] = lang
        # ~ logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))

        req = urllib2.Request(url, headers=DEFAULT_HEADERS)
        response = urllib2.urlopen(req)
        html = response.read()
        response.close()

        dict_html = jsontools.load(html)

        dict_html["cast"] = dict_html.pop("data")
        self.result.update(dict_html)

    def get_id(self):
        """
        @return: Devuelve el identificador Tvdb de la serie cargada o una cadena vacia en caso de que no
            hubiese nada cargado. Se puede utilizar este metodo para saber si una busqueda ha dado resultado o no.
        @rtype: str
        """

        return str(self.result.get('id', ""))

    def get_list_results(self):
        """
        Devuelve los resultados encontramos para una serie.
        @rtype: list
        @return: lista de resultados
        """

        logger.info()
        list_results = []

        # ~ TODO revisar condicion
        # ~ si tenemos un resultado y tiene seriesName, ya tenemos la info de la serie, no hace falta volver a buscar
        if len(self.list_results) == 1 and "seriesName" in self.result:
            list_results.append(self.result)
            info_load = True
        else:
            import threading
            semaforo = threading.Semaphore(20)
            l_hilo = list()
            r_list = list()

            def sub_thread(_id, i):
                semaforo.acquire()
                ret = self.__get_by_id(_id, DEFAULT_LANG, True)
                semaforo.release()
                r_list.append((ret, i))

            for index, e in enumerate(self.list_results):
                t = threading.Thread(target=sub_thread, args=(e["id"], index))
                t.start()
                l_hilo.append(t)

            for x in l_hilo:
                x.join()

            r_list.sort(key=lambda i: i[1])
            list_results = [ii[0] for ii in r_list]
            info_load = False
        return list_results, info_load

    def get_infoLabels(self, infoLabels=None, origen=None):
        """
        @param infoLabels: Informacion extra de la pelicula, serie, temporada o capitulo.
        @type infoLabels: dict
        @param origen: Diccionario origen de donde se obtiene los infoLabels, por omision self.result
        @type origen: dict
        @return: Devuelve la informacion extra obtenida del objeto actual. Si se paso el parametro infoLables, el valor
        devuelto sera el leido como parametro debidamente actualizado.
        @rtype: dict
        """

        # ~ TODO revisar
        if infoLabels:
           # ~ logger.debug("es instancia de infoLabels")
            ret_infoLabels = InfoLabels(infoLabels)
        else:
            # ~ logger.debug("NO ES instancia de infoLabels")
            ret_infoLabels = InfoLabels()
            # ~ fix
            ret_infoLabels['mediatype'] = 'tvshow'

        # ~ Iniciar listados
        l_castandrole = ret_infoLabels.get('castandrole', [])

        # ~ logger.debug("self.result %s" % self.result)

        if not origen:
            origen = self.result

        # ~ todo revisar
        # ~ if 'credits' in origen.keys():
        # ~     dic_origen_credits = origen['credits']
        # ~     origen['credits_cast'] = dic_origen_credits.get('cast', [])
        # ~     origen['credits_crew'] = dic_origen_credits.get('crew', [])
        # ~     del origen['credits']

        items = origen.items()

        for k, v in items:
            if not v:
                continue

            if k == 'overview':
                ret_infoLabels['plot'] = v

            elif k == 'runtime':
                ret_infoLabels['duration'] = int(v) * 60

            elif k == 'firstAired':
                ret_infoLabels['year'] = int(v[:4])
                ret_infoLabels['premiered'] = v.split("-")[2] + "/" + v.split("-")[1] + "/" + v.split("-")[0]

            # ~ todo revisar
            # ~ elif k == 'original_title' or k == 'original_name':
            # ~     ret_infoLabels['originaltitle'] = v

            elif k == 'siteRating':
                ret_infoLabels['rating'] = float(v)

            elif k == 'siteRatingCount':
                ret_infoLabels['votes'] = v

            elif k == 'status':
                # ~ se traduce los estados de una serie
                ret_infoLabels['status'] = DICT_STATUS.get(v, v)

            # ~ no soy partidario de poner la cadena como studio pero es como lo hace el scraper de manera genérica
            elif k == 'network':
                ret_infoLabels['studio'] = v

            elif k == 'image_poster':
                # ~ obtenemos la primera imagen de la lista
                ret_infoLabels['thumbnail'] = HOST_IMAGE + v[0]['fileName']

            elif k == 'image_fanart':
                # ~ obtenemos la primera imagen de la lista
                ret_infoLabels['fanart'] = HOST_IMAGE + v[0]['fileName']

            elif k == 'banner' and v != '':
                ret_infoLabels['banner'] = HOST_IMAGE + v

            # ~ no disponemos de la imagen de fondo
            # ~ elif k == 'banner':
            # ~     ret_infoLabels['fanart'] = HOST_IMAGE + v

            elif k == 'id':
                ret_infoLabels['tvdb_id'] = v

            elif k == 'imdbId':
                ret_infoLabels['imdb_id'] = v
                # ~ no se muestra
                # ~ ret_infoLabels['code'] = v

            elif k in "rating":
                # ~ traducimos la clasificación por edades (content rating system)
                ret_infoLabels['mpaa'] = DICT_MPAA.get(v, v)

            elif k in "genre":
                genre_list = ""
                for index, i in enumerate(v):
                    if index > 0:
                        genre_list += ", "

                    # ~ traducimos los generos
                    genre_list += DICT_GENRE.get(i, i)

                ret_infoLabels['genre'] = genre_list

            elif k == 'seriesName':  # ~ or k == 'name' or k == 'title':
                # ~ if len(origen.get('aliases', [])) > 0:
                # ~     ret_infoLabels['title'] = v + " " + origen.get('aliases', [''])[0]
                # ~ else:
                # ~     ret_infoLabels['title'] = v
                # ~ logger.info("el titulo es %s " % ret_infoLabels['title'])
                ret_infoLabels['title'] = v

            elif k == 'cast':
                dic_aux = dict((name, character) for (name, character) in l_castandrole)
                l_castandrole.extend([(p['name'], p['role']) for p in v if p['name'] not in dic_aux.keys()])

            else:
                # ~ logger.debug("Atributos no añadidos: %s=%s" % (k, v))
                pass

        # ~ Ordenar las listas y convertirlas en str si es necesario
        if l_castandrole:
            ret_infoLabels['castandrole'] = l_castandrole

        # ~ logger.debug("ret_infoLabels %s" % ret_infoLabels)

        return ret_infoLabels
