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

##################################################################################################

import logging
import urllib

import api
import common
import embydb_functions as embydb
import kodidb_functions as kodidb
from utils import window, settings, language as lang, catch_except

##################################################################################################

log = logging.getLogger("EMBY."+__name__)

##################################################################################################


class Movies(common.Items):


    def __init__(self, embycursor, kodicursor, pdialog=None):

        self.embycursor = embycursor
        self.emby_db = embydb.Embydb_Functions(self.embycursor)
        self.kodicursor = kodicursor
        self.kodi_db = kodidb.Kodidb_Functions(self.kodicursor)
        self.pdialog = pdialog

        self.new_time = int(settings('newvideotime'))*1000

        common.Items.__init__(self)

    def _get_func(self, item_type, action):

        if item_type == "Movie":
            actions = {
                'added': self.added,
                'update': self.add_update,
                'userdata': self.updateUserdata,
                'remove': self.remove
            }
        elif item_type == "BoxSet":
            actions = {
                'added': self.add_updateBoxset,
                'update': self.add_updateBoxset,
                'remove': self.remove
            }
        else:
            log.info("Unsupported item_type: %s", item_type)
            actions = {}

        return actions.get(action)

    def compare_all(self):
        # Pull the list of movies and boxsets in Kodi
        pdialog = self.pdialog
        views = self.emby_db.getView_byType('movies')
        views += self.emby_db.getView_byType('mixed')
        log.info("Media folders: %s", views)

        try:
            all_kodisets = dict(self.emby_db.get_checksum('BoxSet'))
        except ValueError:
            all_kodisets = {}

        try:
            all_kodimovies = dict(self.emby_db.get_checksum('Movie'))
        except ValueError:
            all_kodimovies = {}

        all_embymoviesIds = set()
        all_embyboxsetsIds = set()
        updatelist = []

        for view in views:

            if self.should_stop():
                return False

            # Get items per view
            viewId = view['id']
            viewName = view['name']

            if pdialog:
                pdialog.update(
                        heading=lang(29999),
                        message="%s %s..." % (lang(33026), viewName))

            all_embymovies = self.emby.getMovies(viewId, basic=True, dialog=pdialog)
            for embymovie in all_embymovies['Items']:

                if self.should_stop():
                    return False

                API = api.API(embymovie)
                itemid = embymovie['Id']
                all_embymoviesIds.add(itemid)


                if all_kodimovies.get(itemid) != API.get_checksum():
                    # Only update if movie is not in Kodi or checksum is different
                    updatelist.append(itemid)

            log.info("Movies to update for %s: %s", viewName, updatelist)
            embymovies = self.emby.getFullItems(updatelist)
            total = len(updatelist)
            del updatelist[:]

            if pdialog:
                pdialog.update(heading="Processing %s / %s items" % (viewName, total))

            self.added(embymovies, total, view)

        boxsets = self.emby.getBoxset(dialog=pdialog)
        embyboxsets = []

        if pdialog:
            pdialog.update(heading=lang(29999), message=lang(33027))

        for boxset in boxsets['Items']:

            if self.should_stop():
                return False

            # Boxset has no real userdata, so using etag to compare
            itemid = boxset['Id']
            all_embyboxsetsIds.add(itemid)

            if all_kodisets.get(itemid) != boxset['Etag']:
                # Only update if boxset is not in Kodi or boxset['Etag'] is different
                updatelist.append(itemid)
                embyboxsets.append(boxset)

        log.info("Boxsets to update: %s", updatelist)
        self.total = len(updatelist)

        if pdialog:
            pdialog.update(heading="Processing Boxsets / %s items" % total)

        self.count = 0
        for boxset in embyboxsets:
            # Process individual boxset
            if self.should_stop():
                return False
            self.title = boxset['Name']
            self.update_pdialog()
            self.add_updateBoxset(boxset)
            self.count += 1

        ##### PROCESS DELETES #####

        for kodimovie in all_kodimovies:
            if kodimovie not in all_embymoviesIds:
                self.remove(kodimovie)

        log.info("Movies compare finished.")

        for boxset in all_kodisets:
            if boxset not in all_embyboxsetsIds:
                self.remove(boxset)

        log.info("Boxsets compare finished.")

        return True


    def added(self, items, total=None, view=None):

        for item in super(Movies, self).added(items, total):
            if self.add_update(item, view):
                self.content_pop(item.get('Name', "unknown"))

    @catch_except()
    def add_update(self, item, view=None):
        # Process single movie
        kodicursor = self.kodicursor
        emby_db = self.emby_db
        artwork = self.artwork
        API = api.API(item)

        # If the item already exist in the local Kodi DB we'll perform a full item update
        # If the item doesn't exist, we'll add it to the database
        update_item = True
        itemid = item['Id']
        emby_dbitem = emby_db.getItem_byId(itemid)
        try:
            movieid = emby_dbitem[0]
            fileid = emby_dbitem[1]
            pathid = emby_dbitem[2]
            log.info("movieid: %s fileid: %s pathid: %s", movieid, fileid, pathid)

        except TypeError:
            update_item = False
            log.debug("movieid: %s not found", itemid)
            # movieid
            kodicursor.execute("select coalesce(max(idMovie),0) from movie")
            movieid = kodicursor.fetchone()[0] + 1

        else:
            # Verification the item is still in Kodi
            query = "SELECT * FROM movie WHERE idMovie = ?"
            kodicursor.execute(query, (movieid,))
            try:
                kodicursor.fetchone()[0]
            except TypeError:
                # item is not found, let's recreate it.
                update_item = False
                log.info("movieid: %s missing from Kodi, repairing the entry", movieid)

        if not view:
            # Get view tag from emby
            viewtag, viewid, mediatype = self.emby.getView_embyId(itemid)
            log.debug("View tag found: %s", viewtag)
        else:
            viewtag = view['name']
            viewid = view['id']

        # fileId information
        checksum = API.get_checksum()
        dateadded = API.get_date_created()
        userdata = API.get_userdata()
        playcount = userdata['PlayCount']
        dateplayed = userdata['LastPlayedDate']

        # item details
        people = API.get_people()
        writer = " / ".join(people['Writer'])
        director = " / ".join(people['Director'])
        genres = item['Genres']
        title = item['Name']
        plot = API.get_overview()
        shortplot = item.get('ShortOverview')
        tagline = API.get_tagline()
        votecount = item.get('VoteCount')
        rating = item.get('CommunityRating')
        year = item.get('ProductionYear')
        imdb = API.get_provider('Imdb')
        sorttitle = item['SortName']
        runtime = API.get_runtime()
        mpaa = API.get_mpaa()
        genre = " / ".join(genres)
        country = API.get_country()
        studios = API.get_studios()
        try:
            studio = studios[0]
        except IndexError:
            studio = None

        if item.get('LocalTrailerCount'):
            # There's a local trailer
            url = (
                "{server}/emby/Users/{UserId}/Items/%s/LocalTrailers?format=json"
                % itemid
            )
            result = self.do_url(url)
            try:
                trailer = "plugin://plugin.video.emby/trailer/?id=%s&mode=play" % result[0]['Id']
            except IndexError:
                log.info("Failed to process local trailer.")
                trailer = None
        else:
            # Try to get the youtube trailer
            try:
                trailer = item['RemoteTrailers'][0]['Url']
            except (KeyError, IndexError):
                trailer = None
            else:
                try:
                    trailer_id = trailer.rsplit('=', 1)[1]
                except IndexError:
                    log.info("Failed to process trailer: %s", trailer)
                    trailer = None
                else:
                    trailer = "plugin://plugin.video.youtube/play/?video_id=%s" % trailer_id


        ##### GET THE FILE AND PATH #####
        playurl = API.get_file_path()

        if "\\" in playurl:
            # Local path
            filename = playurl.rsplit("\\", 1)[1]
        else: # Network share
            filename = playurl.rsplit("/", 1)[1]

        if self.direct_path:
            # Direct paths is set the Kodi way
            if not self.path_validation(playurl):
                return False

            path = playurl.replace(filename, "")
            window('emby_pathverified', value="true")
        else:
            # Set plugin path and media flags using real filename
            path = "plugin://plugin.video.emby.movies/"
            params = {

                'filename': filename.encode('utf-8'),
                'id': itemid,
                'dbid': movieid,
                'mode': "play"
            }
            filename = "%s?%s" % (path, urllib.urlencode(params))


        ##### UPDATE THE MOVIE #####
        if update_item:
            log.info("UPDATE movie itemid: %s - Title: %s", itemid, title)

            # Update the movie entry
            if self.kodi_version > 16:
                query = ' '.join((

                    "UPDATE movie",
                    "SET c00 = ?, c01 = ?, c02 = ?, c03 = ?, c04 = ?, c05 = ?, c06 = ?,",
                        "c07 = ?, c09 = ?, c10 = ?, c11 = ?, c12 = ?, c14 = ?, c15 = ?,",
                        "c16 = ?, c18 = ?, c19 = ?, c21 = ?, premiered = ?",
                    "WHERE idMovie = ?"
                ))
                kodicursor.execute(query, (title, plot, shortplot, tagline, votecount, rating,
                                           writer, year, imdb, sorttitle, runtime, mpaa, genre,
                                           director, title, studio, trailer, country, year,
                                           movieid))
            else:
                query = ' '.join((

                    "UPDATE movie",
                    "SET c00 = ?, c01 = ?, c02 = ?, c03 = ?, c04 = ?, c05 = ?, c06 = ?,",
                        "c07 = ?, c09 = ?, c10 = ?, c11 = ?, c12 = ?, c14 = ?, c15 = ?,",
                        "c16 = ?, c18 = ?, c19 = ?, c21 = ?",
                    "WHERE idMovie = ?"
                ))
                kodicursor.execute(query, (title, plot, shortplot, tagline, votecount, rating,
                                           writer, year, imdb, sorttitle, runtime, mpaa, genre,
                                           director, title, studio, trailer, country, movieid))
            # Update the checksum in emby table
            emby_db.updateReference(itemid, checksum)

        ##### OR ADD THE MOVIE #####
        else:
            log.info("ADD movie itemid: %s - Title: %s", itemid, title)

            # Add path
            pathid = self.kodi_db.addPath(path)
            # Add the file
            fileid = self.kodi_db.addFile(filename, pathid)

            # Create the movie entry
            if self.kodi_version > 16:
                query = (
                    '''
                    INSERT INTO movie(
                        idMovie, idFile, c00, c01, c02, c03, c04, c05, c06, c07,
                        c09, c10, c11, c12, c14, c15, c16, c18, c19, c21, premiered)

                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    '''
                )
                kodicursor.execute(query, (movieid, fileid, title, plot, shortplot, tagline,
                                           votecount, rating, writer, year, imdb, sorttitle,
                                           runtime, mpaa, genre, director, title, studio, trailer,
                                           country, year))
            else:
                query = (
                    '''
                    INSERT INTO movie(
                        idMovie, idFile, c00, c01, c02, c03, c04, c05, c06, c07,
                        c09, c10, c11, c12, c14, c15, c16, c18, c19, c21)

                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    '''
                )
                kodicursor.execute(query, (movieid, fileid, title, plot, shortplot, tagline,
                                           votecount, rating, writer, year, imdb, sorttitle,
                                           runtime, mpaa, genre, director, title, studio, trailer,
                                           country))
            # Create the reference in emby table
            emby_db.addReference(itemid, movieid, "Movie", "movie", fileid, pathid, None,
                                 checksum, viewid)

        # Update the path
        query = ' '.join((

            "UPDATE path",
            "SET strPath = ?, strContent = ?, strScraper = ?, noUpdate = ?",
            "WHERE idPath = ?"
        ))
        kodicursor.execute(query, (path, "movies", "metadata.local", 1, pathid))

        # Update the file
        query = ' '.join((

            "UPDATE files",
            "SET idPath = ?, strFilename = ?, dateAdded = ?",
            "WHERE idFile = ?"
        ))
        kodicursor.execute(query, (pathid, filename, dateadded, fileid))

        # Process countries
        if 'ProductionLocations' in item:
            self.kodi_db.addCountries(movieid, item['ProductionLocations'], "movie")
        # Process cast
        people = artwork.get_people_artwork(item['People'])
        self.kodi_db.addPeople(movieid, people, "movie")
        # Process genres
        self.kodi_db.addGenres(movieid, genres, "movie")
        # Process artwork
        artwork.add_artwork(artwork.get_all_artwork(item), movieid, "movie", kodicursor)
        # Process stream details
        streams = API.get_media_streams()
        self.kodi_db.addStreams(fileid, streams, runtime)
        # Process studios
        self.kodi_db.addStudios(movieid, studios, "movie")
        # Process tags: view, emby tags
        tags = [viewtag]
        tags.extend(item['Tags'])
        if userdata['Favorite']:
            tags.append("Favorite movies")
        log.info("Applied tags: %s", tags)
        self.kodi_db.addTags(movieid, tags, "movie")
        # Process playstates
        resume = API.adjust_resume(userdata['Resume'])
        total = round(float(runtime), 6)
        self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)

        return True

    def add_updateBoxset(self, boxset):

        emby = self.emby
        emby_db = self.emby_db
        artwork = self.artwork

        boxsetid = boxset['Id']
        title = boxset['Name']
        checksum = boxset['Etag']
        emby_dbitem = emby_db.getItem_byId(boxsetid)
        try:
            setid = emby_dbitem[0]

        except TypeError:
            setid = self.kodi_db.createBoxset(title)

        # Process artwork
        artwork.add_artwork(artwork.get_all_artwork(boxset), setid, "set", self.kodicursor)

        # Process movies inside boxset
        current_movies = emby_db.getItemId_byParentId(setid, "movie")
        process = []
        try:
            # Try to convert tuple to dictionary
            current = dict(current_movies)
        except ValueError:
            current = {}

        # Sort current titles
        for current_movie in current:
            process.append(current_movie)

        # New list to compare
        for movie in emby.getMovies_byBoxset(boxsetid)['Items']:

            itemid = movie['Id']

            if not current.get(itemid):
                # Assign boxset to movie
                emby_dbitem = emby_db.getItem_byId(itemid)
                try:
                    movieid = emby_dbitem[0]
                except TypeError:
                    log.info("Failed to add: %s to boxset", movie['Name'])
                    continue

                log.info("New addition to boxset %s: %s", title, movie['Name'])
                self.kodi_db.assignBoxset(setid, movieid)
                # Update emby reference
                emby_db.updateParentId(itemid, setid)
            else:
                # Remove from process, because the item still belongs
                process.remove(itemid)

        # Process removals from boxset
        for movie in process:
            movieid = current[movie]
            log.info("Remove from boxset %s: %s", title, movieid)
            self.kodi_db.removefromBoxset(movieid)
            # Update emby reference
            emby_db.updateParentId(movie, None)

        # Update the reference in the emby table
        emby_db.addReference(boxsetid, setid, "BoxSet", mediatype="set", checksum=checksum)

    def updateUserdata(self, item):
        # This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
        # Poster with progress bar
        emby_db = self.emby_db
        API = api.API(item)

        # Get emby information
        itemid = item['Id']
        checksum = API.get_checksum()
        userdata = API.get_userdata()
        runtime = API.get_runtime()

        # Get Kodi information
        emby_dbitem = emby_db.getItem_byId(itemid)
        try:
            movieid = emby_dbitem[0]
            fileid = emby_dbitem[1]
            log.info("Update playstate for movie: %s fileid: %s", item['Name'], fileid)
        except TypeError:
            return

        # Process favorite tags
        if userdata['Favorite']:
            self.kodi_db.addTag(movieid, "Favorite movies", "movie")
        else:
            self.kodi_db.removeTag(movieid, "Favorite movies", "movie")

        # Process playstates
        playcount = userdata['PlayCount']
        dateplayed = userdata['LastPlayedDate']
        resume = API.adjust_resume(userdata['Resume'])
        total = round(float(runtime), 6)

        log.debug("%s New resume point: %s", itemid, resume)

        self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
        emby_db.updateReference(itemid, checksum)

    def remove(self, itemid):
        # Remove movieid, fileid, emby reference
        emby_db = self.emby_db
        kodicursor = self.kodicursor
        artwork = self.artwork

        emby_dbitem = emby_db.getItem_byId(itemid)
        try:
            kodiid = emby_dbitem[0]
            fileid = emby_dbitem[1]
            mediatype = emby_dbitem[4]
            log.info("Removing %sid: %s fileid: %s", mediatype, kodiid, fileid)
        except TypeError:
            return

        # Remove the emby reference
        emby_db.removeItem(itemid)
        # Remove artwork
        artwork.delete_artwork(kodiid, mediatype, kodicursor)

        if mediatype == "movie":
            # Delete kodi movie and file
            kodicursor.execute("DELETE FROM movie WHERE idMovie = ?", (kodiid,))
            kodicursor.execute("DELETE FROM files WHERE idFile = ?", (fileid,))

        elif mediatype == "set":
            # Delete kodi boxset
            boxset_movies = emby_db.getItem_byParentId(kodiid, "movie")
            for movie in boxset_movies:
                embyid = movie[0]
                movieid = movie[1]
                self.kodi_db.removefromBoxset(movieid)
                # Update emby reference
                emby_db.updateParentId(embyid, None)

            kodicursor.execute("DELETE FROM sets WHERE idSet = ?", (kodiid,))

        log.info("Deleted %s %s from kodi database", mediatype, itemid)