import os
import sys
import urllib
import xbmcvfs
from abc import ABCMeta
from contextlib import closing
import xml.etree.ElementTree as ET

if sys.version_info < (2, 7):
    from xml.parsers.expat import ExpatError as ParseError
else:
    from xml.etree.ElementTree import ParseError

from lib.libs import mediatypes
from lib.libs.utils import SortedDisplay, get_movie_path_list

NFO_FILE = 32003

class NFOFileAbstractProvider(object):
    __metaclass__ = ABCMeta
    name = SortedDisplay('file:nfo', NFO_FILE)

    def build_resultimage(self, url, title):
        if isinstance(url, unicode):
            url = url.encode('utf-8')
        if url.startswith('http'):
            url = urllib.quote(url, safe="%/:=&?~#+!$,;'@()*[]")
        resultimage = {'url': url, 'provider': self.name, 'preview': url}
        resultimage['title'] = '<{0}>'.format(title)
        resultimage['rating'] = SortedDisplay(0, '')
        resultimage['size'] = SortedDisplay(0, '')
        resultimage['language'] = 'xx'
        return resultimage

class NFOFileSeriesProvider(NFOFileAbstractProvider):
    mediatype = mediatypes.TVSHOW

    def get_exact_images(self, mediaitem):
        root = read_nfofile(mediaitem.file + 'tvshow.nfo')
        if root is None or root.find('art') is None:
            return {}

        artlistelement = root.find('art')
        result = {}
        for artelement in artlistelement:
            arttype = artelement.tag.lower()
            if arttype == 'season':
                num = artelement.get('num')
                if num is None:
                    continue
                try:
                    num = int(num)
                except ValueError:
                    continue
                if num < -1:
                    continue
                for seasonartelement in artelement:
                    url = seasonartelement.text.strip()
                    if seasonartelement.tag.isalnum() and url:
                        seasonarttype = 'season.{0}.{1}'.format(num, seasonartelement.tag.lower())
                        result[seasonarttype] = self.build_resultimage(url, seasonarttype)
            elif arttype.isalnum() and artelement.text.strip():
                result[arttype] = self.build_resultimage(artelement.text.strip(), arttype)
        return result

class NFOFileMovieProvider(NFOFileAbstractProvider):
    mediatype = mediatypes.MOVIE

    def get_exact_images(self, mediaitem):
        paths = get_movie_path_list(mediaitem.file)
        paths = [os.path.splitext(p)[0] + '.nfo' for p in paths]
        paths.append(os.path.dirname(paths[0]) + '/movie.nfo')

        artlist = None
        for nfopath in paths:
            root = read_nfofile(nfopath)
            if root is not None and root.find('art') is not None:
                artlist = root.find('art')
                break

        if artlist is None:
            return {}

        result = {}
        for artelement in artlist:
            arttype = artelement.tag.lower()
            url = artelement.text.strip()
            if arttype.isalnum() and url:
                result[arttype] = self.build_resultimage(url, arttype)
        return result

class NFOFileMovieSetProvider(NFOFileAbstractProvider):
    mediatype = mediatypes.MOVIESET

    def get_exact_images(self, mediaitem):
        path = mediaitem.file
        if os.path.basename(path):
            paths = [os.path.splitext(path)[0] + '.nfo', os.path.splitext(path)[0] + '/set.nfo']
        else:
            paths = [path + 'set.nfo']
        artlist = None
        for nfopath in paths:
            root = read_nfofile(nfopath)
            if root is not None and root.find('art') is not None:
                artlist = root.find('art')
                break

        if artlist is None:
            return {}

        result = {}
        for artelement in artlist:
            arttype = artelement.tag.lower()
            url = artelement.text.strip()
            if arttype.isalnum() and url:
                result[arttype] = self.build_resultimage(url, arttype)
        return result

class NFOFileEpisodeProvider(NFOFileAbstractProvider):
    mediatype = mediatypes.EPISODE

    def get_exact_images(self, mediaitem):
        root = read_nfofile(os.path.splitext(mediaitem.file)[0] + '.nfo')
        if root is None or root.find('art') is None:
            return {}

        result = {}
        artlistelement = root.find('art')
        for artelement in artlistelement:
            arttype = artelement.tag.lower()
            url = artelement.text.strip()
            if arttype.isalnum() and url:
                result[arttype] = self.build_resultimage(url, arttype)
        return result

class NFOFileMusicVideoProvider(NFOFileAbstractProvider):
    mediatype = mediatypes.MUSICVIDEO

    def get_exact_images(self, mediaitem):
        path = mediaitem.file
        artlist = None
        for nfopath in (os.path.splitext(path)[0] + '.nfo', os.path.dirname(path) + '/musicvideo.nfo'):
            root = read_nfofile(nfopath)
            if root is not None and root.find('art') is not None:
                artlist = root.find('art')
                break

        if artlist is None:
            return {}

        result = {}
        for artelement in artlist:
            arttype = artelement.tag.lower()
            url = artelement.text.strip()
            if arttype.isalnum() and url:
                result[arttype] = self.build_resultimage(url, arttype)
        return result

def read_nfofile(filename):
    if not xbmcvfs.exists(filename):
        return None
    with closing(xbmcvfs.File(filename)) as nfofile:
        try:
            return ET.parse(nfofile).getroot()
        except ParseError:
            pass
        # maybe it's all XML except the last line, like the wiki suggests for XML + URL
        nfofile.seek(0, 0)
        lines = nfofile.read().split('\n')
        while lines and not lines[-1].strip():
            del lines[-1] # Remove final blank lines
        if lines: # Remove the line that possibly contains the URL
            del lines[-1]
        if lines:
            try:
                return ET.XML('\n'.join(lines))
            except ParseError:
                pass