# -*- coding: utf-8 -*-
# --------------------------------------------------------------------------------
# Item is the object we use for representing data 
# --------------------------------------------------------------------------------

#from builtins import str
from future.builtins import object
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int

if PY3:
    #from future import standard_library
    #standard_library.install_aliases()
    import urllib.parse as urllib                               # It is very slow in PY2. In PY3 it is native
else:
    import urllib                                               # We use the native of PY2 which is faster
from core.scrapertools import unescape

import base64
import copy

from core import jsontools as json


class InfoLabels(dict):
    def __str__(self):
        return self.tostring(separador=',\r\t')

    def __setitem__(self, name, value):
        if name in ["season", "episode"]:
            # we force int () in season and episode
            try:
                super(InfoLabels, self).__setitem__(name, int(value))
            except:
                pass

        elif name in ['IMDBNumber', 'imdb_id']:
            # For compatibility we have to save the value in the three fields
            super(InfoLabels, self).__setitem__('IMDBNumber', str(value))
            # super(InfoLabels, self).__setitem__('code', value)
            super(InfoLabels, self).__setitem__('imdb_id', str(value))

        elif name == "mediatype" and value not in ["list", "movie", "tvshow", "season", "episode", "music", "undefined"]:
            super(InfoLabels, self).__setitem__('mediatype', 'list')

        elif name in ['tmdb_id', 'tvdb_id', 'noscrap_id']:
            super(InfoLabels, self).__setitem__(name, str(value))
        else:
            super(InfoLabels, self).__setitem__(name, value)

    # Python 2.4
    def __getitem__(self, key):
        try:
            return super(InfoLabels, self).__getitem__(key)
        except:
            return self.__missing__(key)

    def __missing__(self, key):
        """
        Valores por defecto en caso de que la clave solicitada no exista.
        El parametro 'default' en la funcion obj_infoLabels.get(key,default) tiene preferencia sobre los aqui definidos.
        """
        if key in ['rating']:
            # Key example q returns a str formatted as float by default
            return '0.0'

        elif key == 'code':
            code = []
            # Add imdb_id to the code list
            if 'imdb_id' in list(super(InfoLabels, self).keys()) and super(InfoLabels, self).__getitem__('imdb_id'):
                code.append(super(InfoLabels, self).__getitem__('imdb_id'))

            # Complete with the rest of the codes
            for scr in ['tmdb_id', 'tvdb_id', 'noscrap_id']:
                if scr in list(super(InfoLabels, self).keys()) and super(InfoLabels, self).__getitem__(scr):
                    value = "%s%s" % (scr[:-2], super(InfoLabels, self).__getitem__(scr))
                    code.append(value)

            # Option to add a code of the random type
            if not code:
                import time
                value = time.strftime("%Y%m%d%H%M%S", time.gmtime())
                code.append(value)
                super(InfoLabels, self).__setitem__('noscrap_id', value)

            return code

        elif key == 'mediatype':
            # "list", "movie", "tvshow", "season", "episode"
            if 'tvshowtitle' in list(super(InfoLabels, self).keys()) \
                    and super(InfoLabels, self).__getitem__('tvshowtitle') != "":
                if 'episode' in list(super(InfoLabels, self).keys()) and super(InfoLabels, self).__getitem__('episode') != "":
                    return 'episode'

                if 'episodeName' in list(super(InfoLabels, self).keys()) \
                        and super(InfoLabels, self).__getitem__('episodeName') != "":
                    return 'episode'

                if 'season' in list(super(InfoLabels, self).keys()) and super(InfoLabels, self).__getitem__('season') != "":
                    return 'season'
                else:
                    return 'tvshow'

            elif 'title' in list(super(InfoLabels, self).keys()) and super(InfoLabels, self).__getitem__('title') != "":
                return 'movie'

            else:
                return 'list'

        else:
            # The rest of the keys return empty strings by default
            return ""

    def tostring(self, separador=', '):
        ls = []
        dic = dict(list(super(InfoLabels, self).items()))

        for i in sorted(dic.items()):
            i_str = str(i)[1:-1]
            if isinstance(i[0], str):
                old = i[0] + "',"
                new = i[0] + "':"
            else:
                old = str(i[0]) + ","
                new = str(i[0]) + ":"
            ls.append(i_str.replace(old, new, 1))

        return "{%s}" % separador.join(ls)


class Item(object):
    def __init__(self, **kwargs):
        """
        Item initialization
        """

        # Creamos el atributo infoLabels
        self.__dict__["infoLabels"] = InfoLabels()
        if "infoLabels" in kwargs:
            if isinstance(kwargs["infoLabels"], dict):
                self.__dict__["infoLabels"].update(kwargs["infoLabels"])
            del kwargs["infoLabels"]

        if "parentContent" in kwargs:
            self.set_parent_content(kwargs["parentContent"])
            del kwargs["parentContent"]

        kw = copy.copy(kwargs)
        for k in kw:
            if k in ["contentTitle", "contentPlot", "contentSerieName", "show", "contentType", "contentEpisodeTitle",
                     "contentSeason", "contentEpisodeNumber", "contentThumbnail", "plot", "duration", "contentQuality",
                     "quality", "year"]:
                self.__setattr__(k, kw[k])
                del kwargs[k]

        self.__dict__.update(kwargs)
        self.__dict__ = self.toutf8(self.__dict__)

    def __contains__(self, m):
        """
        Check if an attribute exists in the item
        """
        return m in self.__dict__

    def __setattr__(self, name, value):
        """
        Function called when modifying any attribute of the item, modifies some attributes based on the modified data.
        """
        if PY3: name = self.toutf8(name)
        value = self.toutf8(value)
        if name == "__dict__":
            for key in value:
                self.__setattr__(key, value[key])
            return

        # We decode the HTML entities
        if name in ["title", "plot", "fulltitle", "contentPlot", "contentTitle"]:
            value = self.decode_html(value)

        # By modifying any of these attributes content...
        if name in ["contentTitle", "contentPlot", "plot", "contentSerieName", "contentType", "contentEpisodeTitle",
                    "contentSeason", "contentEpisodeNumber", "contentThumbnail", "show", "contentQuality", "quality", "year"]:
            # ...and update infoLables
            if name == "contentTitle":
                self.__dict__["infoLabels"]["title"] = value
            elif name == "contentPlot" or name == "plot":
                self.__dict__["infoLabels"]["plot"] = value
            elif name == "contentSerieName" or name == "show":
                self.__dict__["infoLabels"]["tvshowtitle"] = value
            elif name == "contentType":
                self.__dict__["infoLabels"]["mediatype"] = value
            elif name == "contentEpisodeTitle":
                self.__dict__["infoLabels"]["episodeName"] = value
            elif name == "contentSeason":
                self.__dict__["infoLabels"]["season"] = value
            elif name == "contentEpisodeNumber":
                self.__dict__["infoLabels"]["episode"] = value
            elif name == "contentThumbnail":
                self.__dict__["infoLabels"]["thumbnail"] = value
            elif name == "contentQuality" or name == "quality":
                self.__dict__["infoLabels"]["quality"] = value
            elif name == "year":
                self.__dict__["infoLabels"]["year"] = value

        elif name == "duration":
            # String q represents the duration of the video in seconds
            self.__dict__["infoLabels"]["duration"] = str(value)

        elif name == "viewcontent" and value not in ["files", "movies", "tvshows", "seasons", "episodes"]:
            super(Item, self).__setattr__("viewcontent", "files")

        # When assigning a value to infoLables
        elif name == "infoLabels":
            if isinstance(value, dict):
                value_defaultdict = InfoLabels(value)
                self.__dict__["infoLabels"] = value_defaultdict

        else:
            super(Item, self).__setattr__(name, value)

    def __getattr__(self, name):
        """
        Returns the default values ​​in case the requested attribute does not exist in the item
        """
        if name.startswith("__"):
            return super(Item, self).__getattribute__(name)

        # default value for folder
        if name == "folder":
            return True

        # default value for contentChannel
        elif name == "contentChannel":
            return "list"

        # default value for viewcontent
        elif name == "viewcontent":
            # we try to fix it according to the type of content...
            if self.__dict__["infoLabels"]["mediatype"] == 'movie':
                viewcontent = 'movies'
            elif self.__dict__["infoLabels"]["mediatype"] in ["tvshow", "season", "episode"]:
                viewcontent = "episodes"
            else:
                viewcontent = "files"

            self.__dict__["viewcontent"] = viewcontent
            return viewcontent

        # values ​​saved in infoLabels
        elif name in ["contentTitle", "contentPlot", "contentSerieName", "show", "contentType", "contentEpisodeTitle",
                      "contentSeason", "contentEpisodeNumber", "contentThumbnail", "plot", "duration",
                      "contentQuality", "quality"]:
            if name == "contentTitle":
                return self.__dict__["infoLabels"]["title"]
            elif name == "contentPlot" or name == "plot":
                return self.__dict__["infoLabels"]["plot"]
            elif name == "contentSerieName" or name == "show":
                return self.__dict__["infoLabels"]["tvshowtitle"]
            elif name == "contentType":
                ret = self.__dict__["infoLabels"]["mediatype"]
                if ret == 'list' and self.__dict__.get("fulltitle", None):  # backward compatibility
                    ret = 'movie'
                    self.__dict__["infoLabels"]["mediatype"] = ret
                return ret
            elif name == "contentEpisodeTitle":
                return self.__dict__["infoLabels"]["episodeName"]
            elif name == "contentSeason":
                return self.__dict__["infoLabels"]["season"]
            elif name == "contentEpisodeNumber":
                return self.__dict__["infoLabels"]["episode"]
            elif name == "contentThumbnail":
                return self.__dict__["infoLabels"]["thumbnail"]
            elif name == "contentQuality" or name == "quality":
                return self.__dict__["infoLabels"]["quality"]
            else:
                return self.__dict__["infoLabels"][name]

        # default value for all other attributes
        else:
            return ""

    def __str__(self):
        return '\r\t' + self.tostring('\r\t')

    def __eq__(self, other):
        if type(other) == Item:
            return self.__dict__ == other.__dict__
        else:
            return False

    def set_parent_content(self, parentContent):
        """
        Fill the contentDetails fields with the information of the item "parent"
        @param parentContent: item father
        @type parentContent: item
        """
        # Check that parentContent is an Item
        if not type(parentContent) == type(self):
            return
        # Copy all the attributes that start with "content" and are declared and the infoLabels
        for attr in parentContent.__dict__:
            if attr.startswith("content") or attr == "infoLabels":
                self.__setattr__(attr, parentContent.__dict__[attr])

    def tostring(self, separator=", "):
        """
        Generate a text string with the item's data for the log
        Use: logger.info(item.tostring())
        @param separator: string to be used as a separator
        @type separator: str
        '"""
        dic = self.__dict__.copy()

        # We add the content fields... if they have any value
        for key in ["contentTitle", "contentPlot", "contentSerieName", "contentEpisodeTitle",
                    "contentSeason", "contentEpisodeNumber", "contentThumbnail"]:
            value = self.__getattr__(key)
            if value:
                dic[key] = value

        if 'mediatype' in self.__dict__["infoLabels"]:
            dic["contentType"] = self.__dict__["infoLabels"]['mediatype']

        ls = []
        for var in sorted(dic):
            if isinstance(dic[var], str):
                valor = "'%s'" % dic[var]
            elif isinstance(dic[var], InfoLabels):
                if separator == '\r\t':
                    valor = dic[var].tostring(',\r\t\t')
                else:
                    valor = dic[var].tostring()
            elif PY3 and isinstance(dic[var], bytes):
                valor = "'%s'" % dic[var].decode('utf-8')
            else:
                valor = str(dic[var])

            if PY3 and isinstance(var, bytes):
                var = var.decode('utf-8')
            ls.append(var + "= " + valor)

        return separator.join(ls)

    def tourl(self):
        """
        Generate a text string with the item data to create a url, to re-generate the Item use item.fromurl ().

        Use: url = item.tourl()
        """
        dump = json.dump(self.__dict__).encode("utf8")
        # if empty dict
        if not dump:
            # set a str to avoid b64encode fails
            dump = "".encode("utf8")
        return str(urllib.quote(base64.b64encode(dump)))

    def fromurl(self, url):
        """
        Generate an item from a text string. The string can be created by the tourl () function or have
        the old format: plugin: //plugin.video.kod/? channel = ... (+ other parameters)
        Use: item.fromurl("string")

        @param url: url
        @type url: str
        """
        if "?" in url:
            url = url.split("?")[1]
        decoded = False
        try:
            str_item = base64.b64decode(urllib.unquote(url))
            json_item = json.load(str_item, object_hook=self.toutf8)
            if json_item is not None and len(json_item) > 0:
                self.__dict__.update(json_item)
                decoded = True
        except:
            pass

        if not decoded:
            url = urllib.unquote_plus(url)
            dct = dict([[param.split("=")[0], param.split("=")[1]] for param in url.split("&") if "=" in param])
            self.__dict__.update(dct)
            self.__dict__ = self.toutf8(self.__dict__)

        if 'infoLabels' in self.__dict__ and not isinstance(self.__dict__['infoLabels'], InfoLabels):
            self.__dict__['infoLabels'] = InfoLabels(self.__dict__['infoLabels'])

        return self

    def tojson(self, path=""):
        from core import filetools
        """
        Create a JSON from the item, to save favorite files, download list, etc....
        If a path is specified, it saves it in the specified path, if not, it returns the string json
        Applications: item.tojson(path="path\archivo\json.json")
                      file.write(item.tojson())

        @param path: path
        @type path: str
        """
        if path:
            #open(path, "wb").write(json.dump(self.__dict__))
            res = filetools.write(path, json.dump(self.__dict__))
        else:
            return json.dump(self.__dict__)

    def fromjson(self, json_item=None, path=""):
        from core import filetools
        """
        Generate an item from a JSON file
        If a path is specified, it directly reads the file, if not, it reads the passed text string.
        Applications: item = Item().fromjson(path="path\archivo\json.json")
                      item = Item().fromjson("Cadena de texto json")

        @param json_item: item
        @type json_item: json
        @param path: path
        @type path: str
        """
        if path:
            if filetools.exists(path):
                #json_item = open(path, "rb").read()
                json_item = filetools.read(path)
            else:
                json_item = {}

        if json_item is None:
            json_item = {}

        item = json.load(json_item, object_hook=self.toutf8)
        self.__dict__.update(item)

        if 'infoLabels' in self.__dict__ and not isinstance(self.__dict__['infoLabels'], InfoLabels):
            self.__dict__['infoLabels'] = InfoLabels(self.__dict__['infoLabels'])

        return self

    def clone(self, **kwargs):
        """
        Generate a new item by cloning the current item
        Applications: NewItem = item.clone()
                      NuewItem = item.clone(title="New Title", action = "New Action")
        """
        newitem = copy.deepcopy(self)
        if "infoLabels" in kwargs:
            kwargs["infoLabels"] = InfoLabels(kwargs["infoLabels"])
        for kw in kwargs:
            newitem.__setattr__(kw, kwargs[kw])
        newitem.__dict__ = newitem.toutf8(newitem.__dict__)

        return newitem

    @staticmethod
    def decode_html(value):
        """
        Decode the HTML entities
        @param value: value to decode
        @type value: str
        """
        try:
            unicode_title = unicode(value, "utf8", "ignore")
            return unescape(unicode_title).encode("utf8")
        except:
            if PY3 and isinstance(value, bytes):
                value = value.decode("utf8")
            return value

    def toutf8(self, *args):
        """
        Pass the item to utf8
        """
        if len(args) > 0:
            value = args[0]
        else:
            value = self.__dict__

        if isinstance(value, unicode):
            value = value.encode("utf8")
            if PY3: value = value.decode("utf8")
            return value

        elif not PY3 and isinstance(value, str):
            return unicode(value, "utf8", "ignore").encode("utf8")

        elif PY3 and isinstance(value, bytes):
            return value.decode("utf8")

        elif isinstance(value, list):
            for x, key in enumerate(value):
                value[x] = self.toutf8(value[x])
            return value

        elif isinstance(value, dict):
            newdct = {}
            for key in value:
                value_unc = self.toutf8(value[key])
                key_unc = self.toutf8(key)
                #if isinstance(key, unicode):
                #    key = key.encode("utf8")

                newdct[key_unc] = value_unc

            if len(args) > 0:
                if isinstance(value, InfoLabels):
                    return InfoLabels(newdct)
                else:
                    return newdct

        else:
            return value
