# -*- coding: utf-8 -*-
import helper.api
import helper.loghandler
import database.emby_db
import database.queries
from . import obj_ops
from . import kodi
from . import queries_videos
from . import artwork
from . import common

class Movies():
    def __init__(self, EmbyServer, embydb, videodb):
        self.LOG = helper.loghandler.LOG('EMBY.core.movies.Movies')
        self.EmbyServer = EmbyServer
        self.emby = embydb
        self.video = videodb
        self.emby_db = database.emby_db.EmbyDatabase(embydb.cursor)
        self.objects = obj_ops.Objects()
        self.item_ids = []
        self.Common = common.Common(self.emby_db, self.objects, self.EmbyServer)
        self.KodiDBIO = kodi.Kodi(videodb.cursor, self.EmbyServer.Utils)
        self.MoviesDBIO = MoviesDBIO(videodb.cursor)
        self.ArtworkDBIO = artwork.Artwork(videodb.cursor, self.EmbyServer.Utils)
        self.APIHelper = helper.api.API(self.EmbyServer.Utils)

    #If item does not exist, entry will be added.
    #If item exists, entry will be updated
    def movie(self, item, library):
        e_item = self.emby_db.get_item_by_id(item['Id'])
        library = self.Common.library_check(e_item, item, library)

        if not library:
            return False

        obj = self.objects.map(item, 'Movie')
        obj['Item'] = item
        obj['Library'] = library
        obj['LibraryId'] = library['Id']
        obj['LibraryName'] = library['Name']
        update = True
        StackedID = self.emby_db.get_stack(obj['PresentationKey']) or obj['Id']

        if str(StackedID) != obj['Id']:
            self.LOG.info("Skipping stacked movie %s [%s/%s]" % (obj['Title'], StackedID, obj['Id']))
            Movies(self.EmbyServer, self.emby, self.video).remove(StackedID)

        if e_item:
            obj['MovieId'] = e_item[0]
            obj['FileId'] = e_item[1]
            obj['PathId'] = e_item[2]

            if self.MoviesDBIO.get(*self.EmbyServer.Utils.values(obj, queries_videos.get_movie_obj)) is None:
                update = False
                self.LOG.info("MovieId %s missing from kodi. repairing the entry." % obj['MovieId'])
        else:
            update = False
            self.LOG.debug("MovieId %s not found" % obj['Id'])
            obj['MovieId'] = self.MoviesDBIO.create_entry()

        obj['Item']['MediaSources'][0] = self.objects.MapMissingData(obj['Item']['MediaSources'][0], 'MediaSources')
        obj['MediaSourceID'] = obj['Item']['MediaSources'][0]['Id']
        obj['Runtime'] = obj['Item']['MediaSources'][0]['RunTimeTicks']

        if obj['Item']['MediaSources'][0]['Path']:
            obj['Path'] = obj['Item']['MediaSources'][0]['Path']

            #don't use 3d movies as default
            if "3d" in self.EmbyServer.Utils.StringMod(obj['Item']['MediaSources'][0]['Path']):
                for DataSource in obj['Item']['MediaSources']:
                    if not "3d" in self.EmbyServer.Utils.StringMod(DataSource['Path']):
                        DataSource = self.objects.MapMissingData(DataSource, 'MediaSources')
                        obj['Path'] = DataSource['Path']
                        obj['MediaSourceID'] = DataSource['Id']
                        obj['Runtime'] = DataSource['RunTimeTicks']
                        break

        obj['Path'] = self.APIHelper.get_file_path(obj['Path'], item)
        obj['Genres'] = obj['Genres'] or []
        obj['Studios'] = [self.APIHelper.validate_studio(studio) for studio in (obj['Studios'] or [])]
        obj['People'] = obj['People'] or []
        obj['Genre'] = " / ".join(obj['Genres'])
        obj['Writers'] = " / ".join(obj['Writers'] or [])
        obj['Directors'] = " / ".join(obj['Directors'] or [])
        obj['Plot'] = self.APIHelper.get_overview(obj['Plot'], item)
        obj['Mpaa'] = self.APIHelper.get_mpaa(obj['Mpaa'], item)
        obj['Resume'] = self.APIHelper.adjust_resume((obj['Resume'] or 0) / 10000000.0)
        obj['Runtime'] = round(float((obj['Runtime'] or 0) / 10000000.0), 6)
        obj['People'] = self.APIHelper.get_people_artwork(obj['People'])
        obj['DateAdded'] = self.EmbyServer.Utils.convert_to_local(obj['DateAdded']).split('.')[0].replace('T', " ")
        obj['Premiered'] = self.EmbyServer.Utils.convert_to_local(obj['Year']) if not obj['Premiered'] else self.EmbyServer.Utils.convert_to_local(obj['Premiered']).replace(" ", "T").split('T')[0]
        obj['DatePlayed'] = None if not obj['DatePlayed'] else self.EmbyServer.Utils.convert_to_local(obj['DatePlayed']).split('.')[0].replace('T', " ")
        obj['PlayCount'] = self.APIHelper.get_playcount(obj['Played'], obj['PlayCount'])
        obj['Artwork'] = self.APIHelper.get_all_artwork(self.objects.map(item, 'Artwork'))
        obj['Video'] = self.APIHelper.video_streams(obj['Video'] or [], obj['Container'], item)
        obj['Audio'] = self.APIHelper.audio_streams(obj['Audio'] or [])
        obj['Streams'] = self.APIHelper.media_streams(obj['Video'], obj['Audio'], obj['Subtitles'])
        PathValid, obj = self.Common.get_path_filename(obj, "movies")

        if not PathValid:
            return "Invalid Filepath"

        self.trailer(obj)

        if obj['Countries']:
            self.MoviesDBIO.add_countries(*self.EmbyServer.Utils.values(obj, queries_videos.update_country_obj))

        tags = []
        tags.extend(obj['TagItems'] or obj['Tags'] or [])
        tags.append(obj['LibraryName'])

        if obj['Favorite']:
            tags.append('Favorite movies')

        obj['Tags'] = tags

        if update:
            self.movie_update(obj)
        else:
            self.movie_add(obj)

        self.KodiDBIO.update_path(*self.EmbyServer.Utils.values(obj, queries_videos.update_path_movie_obj))
        self.KodiDBIO.update_file(*self.EmbyServer.Utils.values(obj, queries_videos.update_file_obj))
        self.KodiDBIO.add_tags(*self.EmbyServer.Utils.values(obj, queries_videos.add_tags_movie_obj))
        self.KodiDBIO.add_genres(*self.EmbyServer.Utils.values(obj, queries_videos.add_genres_movie_obj))
        self.KodiDBIO.add_studios(*self.EmbyServer.Utils.values(obj, queries_videos.add_studios_movie_obj))
        self.KodiDBIO.add_playstate(*self.EmbyServer.Utils.values(obj, queries_videos.add_bookmark_obj))
        self.KodiDBIO.add_people(*self.EmbyServer.Utils.values(obj, queries_videos.add_people_movie_obj))
        self.KodiDBIO.add_streams(*self.EmbyServer.Utils.values(obj, queries_videos.add_streams_obj))
        self.ArtworkDBIO.add(obj['Artwork'], obj['MovieId'], "movie")
        self.item_ids.append(obj['Id'])

        if "StackTimes" in obj:
            self.KodiDBIO.add_stacktimes(*self.EmbyServer.Utils.values(obj, queries_videos.add_stacktimes_obj))

        return not update

    #Add object to kodi
    def movie_add(self, obj):
        obj = self.Common.Streamdata_add(obj, False)
        obj['RatingType'] = "default"
        obj['RatingId'] = self.KodiDBIO.create_entry_rating()
        self.KodiDBIO.add_ratings(*self.EmbyServer.Utils.values(obj, queries_videos.add_rating_movie_obj))

        if obj['CriticRating'] is not None:
            obj['CriticRating'] = float(obj['CriticRating'] / 10.0)
            self.KodiDBIO.add_ratings(*self.EmbyServer.Utils.values(dict(obj, RatingId=self.KodiDBIO.create_entry_rating(), RatingType="tomatometerallcritics", Rating=obj['CriticRating']), queries_videos.add_rating_movie_obj))

        obj['Unique'] = self.MoviesDBIO.create_entry_unique_id()
        self.MoviesDBIO.add_unique_id(*self.EmbyServer.Utils.values(obj, queries_videos.add_unique_id_movie_obj))

        for provider in obj['UniqueIds'] or {}:
            unique_id = obj['UniqueIds'][provider]
            provider = provider.lower()

            if provider != 'imdb':
                temp_obj = dict(obj, ProviderName=provider, UniqueId=unique_id, Unique=self.MoviesDBIO.create_entry_unique_id())
                self.MoviesDBIO.add_unique_id(*self.EmbyServer.Utils.values(temp_obj, queries_videos.add_unique_id_movie_obj))

        obj['PathId'] = self.KodiDBIO.add_path(*self.EmbyServer.Utils.values(obj, queries_videos.add_path_obj))
        obj['FileId'] = self.KodiDBIO.add_file(*self.EmbyServer.Utils.values(obj, queries_videos.add_file_obj))

        if self.EmbyServer.Utils.Settings.userRating:
            self.MoviesDBIO.add(*self.EmbyServer.Utils.values(obj, queries_videos.add_movie_obj))
        else:
            self.MoviesDBIO.add_nouserrating(*self.EmbyServer.Utils.values(obj, queries_videos.add_movie_nouserrating_obj))

        self.emby_db.add_reference(*self.EmbyServer.Utils.values(obj, database.queries.add_reference_movie_obj))
        self.LOG.info("ADD movie [%s/%s/%s] %s: %s" % (obj['PathId'], obj['FileId'], obj['MovieId'], obj['Id'], obj['Title']))

    #Update object to kodi
    def movie_update(self, obj):
        obj = self.Common.Streamdata_add(obj, True)
        obj['RatingType'] = "default"
        obj['RatingId'] = self.KodiDBIO.get_rating_id(*self.EmbyServer.Utils.values(obj, queries_videos.get_rating_movie_obj))
        self.KodiDBIO.update_ratings(*self.EmbyServer.Utils.values(obj, queries_videos.update_rating_movie_obj))

        if obj['CriticRating'] is not None:
            obj['CriticRating'] = float(obj['CriticRating'] / 10.0)
            temp_obj = dict(obj, RatingType="tomatometerallcritics", Rating=obj['CriticRating'])
            temp_obj['RatingId'] = self.KodiDBIO.get_rating_id(*self.EmbyServer.Utils.values(temp_obj, queries_videos.get_rating_movie_obj))
            self.KodiDBIO.update_ratings(*self.EmbyServer.Utils.values(temp_obj, queries_videos.update_rating_movie_obj))

        self.KodiDBIO.remove_unique_ids(*self.EmbyServer.Utils.values(obj, queries_videos.delete_unique_ids_movie_obj))
        obj['Unique'] = self.MoviesDBIO.create_entry_unique_id()
        self.MoviesDBIO.add_unique_id(*self.EmbyServer.Utils.values(obj, queries_videos.add_unique_id_movie_obj))

        for provider in obj['UniqueIds'] or {}:
            unique_id = obj['UniqueIds'][provider]
            provider = provider.lower()

            if provider != 'imdb':
                temp_obj = dict(obj, ProviderName=provider, UniqueId=unique_id, Unique=self.MoviesDBIO.create_entry_unique_id())
                self.MoviesDBIO.add_unique_id(*self.EmbyServer.Utils.values(temp_obj, queries_videos.add_unique_id_movie_obj))

        if self.EmbyServer.Utils.Settings.userRating:
            self.MoviesDBIO.update(*self.EmbyServer.Utils.values(obj, queries_videos.update_movie_obj))
        else:
            self.MoviesDBIO.update_nouserrating(*self.EmbyServer.Utils.values(obj, queries_videos.update_movie_nouserrating_obj))

        self.emby_db.update_reference(*self.EmbyServer.Utils.values(obj, database.queries.update_reference_obj))
        self.LOG.info("UPDATE movie [%s/%s/%s] %s: %s" % (obj['PathId'], obj['FileId'], obj['MovieId'], obj['Id'], obj['Title']))

    def trailer(self, obj):
        try:
            if obj['LocalTrailer']:
                trailer = self.EmbyServer.API.get_local_trailers(obj['Id'])

                if self.EmbyServer.Utils.direct_path:
                    obj['Trailer'] = self.APIHelper.get_file_path(trailer[0]['Path'], trailer)
                    obj['Trailer'] = self.EmbyServer.Utils.StringDecode(obj['Trailer'])
                else:
                    obj['Trailer'] = "plugin://plugin.video.emby-next-gen/trailer?id=%s&mode=play" % trailer[0]['Id']
            elif obj['Trailer']:
                obj['Trailer'] = "plugin://plugin.video.youtube/play/?video_id=%s" % obj['Trailer'].rsplit('=', 1)[1]
        except Exception as error:
            self.LOG.error("Failed to get trailer: %s" % error)
            obj['Trailer'] = None

    #If item does not exist, entry will be added.
    #If item exists, entry will be updated.
    #Process movies inside boxset.
    #Process removals from boxset.
    def boxset(self, item):
        e_item = self.emby_db.get_item_by_id(item['Id'])
        obj = self.objects.map(item, 'Boxset')
        obj['Overview'] = self.APIHelper.get_overview(obj['Overview'], item)
        obj['Checksum'] = obj['Etag']

        if e_item:
            obj['SetId'] = e_item[0]
            self.MoviesDBIO.update_boxset(*self.EmbyServer.Utils.values(obj, queries_videos.update_set_obj))
        else:
            self.LOG.debug("SetId %s not found" % obj['Id'])
            obj['SetId'] = self.MoviesDBIO.add_boxset(*self.EmbyServer.Utils.values(obj, queries_videos.add_set_obj))

        self.boxset_current(obj)
        obj['Artwork'] = self.APIHelper.get_all_artwork(self.objects.map(item, 'Artwork'))

        for movie in obj['Current']:
            temp_obj = dict(obj)
            temp_obj['Movie'] = movie
            temp_obj['MovieId'] = obj['Current'][temp_obj['Movie']]
            self.MoviesDBIO.remove_from_boxset(*self.EmbyServer.Utils.values(temp_obj, queries_videos.delete_movie_set_obj))
            self.emby_db.update_parent_id(*self.EmbyServer.Utils.values(temp_obj, database.queries.delete_parent_boxset_obj))
            self.LOG.info("DELETE from boxset [%s] %s: %s" % (temp_obj['SetId'], temp_obj['Title'], temp_obj['MovieId']))

        self.ArtworkDBIO.add(obj['Artwork'], obj['SetId'], "set")
        self.emby_db.add_reference(*self.EmbyServer.Utils.values(obj, database.queries.add_reference_boxset_obj))
        self.LOG.info("UPDATE boxset [%s] %s" % (obj['SetId'], obj['Title']))
        return True

    #Add or removes movies based on the current movies found in the boxset
    def boxset_current(self, obj):
        try:
            current = self.emby_db.get_item_id_by_parent_id(*self.EmbyServer.Utils.values(obj, database.queries.get_item_id_by_parent_boxset_obj))
            movies = dict(current)
        except ValueError:
            movies = {}

        obj['Current'] = movies

        for all_movies in self.EmbyServer.API.get_movies_by_boxset(obj['Id']):
            for movie in all_movies['Items']:
                temp_obj = dict(obj)
                temp_obj['Title'] = movie['Name']
                temp_obj['Id'] = movie['Id']
                Data = self.emby_db.get_item_by_id(*self.EmbyServer.Utils.values(temp_obj, database.queries.get_item_obj))

                if Data:
                    temp_obj['MovieId'] = Data[0]
                else:
                    self.LOG.info("Failed to process %s to boxset." % temp_obj['Title'])
                    continue

                if temp_obj['Id'] not in obj['Current']:
                    self.MoviesDBIO.set_boxset(*self.EmbyServer.Utils.values(temp_obj, queries_videos.update_movie_set_obj))
                    self.emby_db.update_parent_id(*self.EmbyServer.Utils.values(temp_obj, database.queries.update_parent_movie_obj))
                    self.LOG.info("ADD to boxset [%s/%s] %s: %s to boxset" % (temp_obj['SetId'], temp_obj['MovieId'], temp_obj['Title'], temp_obj['Id']))
                else:
                    obj['Current'].pop(temp_obj['Id'])

    #Special function to remove all existing boxsets
    def boxsets_reset(self):
        boxsets = self.emby_db.get_items_by_media('set')
        for boxset in boxsets:
            self.remove(boxset[0])

    #This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
    #Poster with progress bar
    def userdata(self, item):
        e_item = self.emby_db.get_item_by_id(item['Id'])
        obj = self.objects.map(item, 'MovieUserData')
        obj['Item'] = item

        if e_item:
            obj['MovieId'] = e_item[0]
            obj['FileId'] = e_item[1]
        else:
            return

        obj = self.Common.Streamdata_add(obj, True)
        obj['Resume'] = self.APIHelper.adjust_resume((obj['Resume'] or 0) / 10000000.0)
        obj['Runtime'] = round(float((obj['Runtime'] or 0) / 10000000.0), 6)
        obj['PlayCount'] = self.APIHelper.get_playcount(obj['Played'], obj['PlayCount'])

        if obj['DatePlayed']:
            obj['DatePlayed'] = self.EmbyServer.Utils.convert_to_local(obj['DatePlayed']).split('.')[0].replace('T', " ")

        if obj['Favorite']:
            self.KodiDBIO.get_tag(*self.EmbyServer.Utils.values(obj, queries_videos.get_tag_movie_obj))
        else:
            self.KodiDBIO.remove_tag(*self.EmbyServer.Utils.values(obj, queries_videos.delete_tag_movie_obj))

        self.LOG.debug("New resume point %s: %s" % (obj['Id'], obj['Resume']))
        self.KodiDBIO.add_playstate(*self.EmbyServer.Utils.values(obj, queries_videos.add_bookmark_obj))
        self.emby_db.update_reference(*self.EmbyServer.Utils.values(obj, database.queries.update_reference_obj))
        self.LOG.info("USERDATA movie [%s/%s] %s: %s" % (obj['FileId'], obj['MovieId'], obj['Id'], obj['Title']))

    #Remove movieid, fileid, emby reference.
    #Remove artwork, boxset
    def remove(self, item_id):
        e_item = self.emby_db.get_item_by_id(item_id)
        obj = {'Id': item_id}

        if e_item:
            obj['KodiId'] = e_item[0]
            obj['FileId'] = e_item[1]
            obj['Media'] = e_item[4]
        else:
            return

        self.ArtworkDBIO.delete(obj['KodiId'], obj['Media'])

        if obj['Media'] == 'movie':
            self.MoviesDBIO.delete(*self.EmbyServer.Utils.values(obj, queries_videos.delete_movie_obj))
        elif obj['Media'] == 'set':
            for movie in self.emby_db.get_item_by_parent_id(*self.EmbyServer.Utils.values(obj, database.queries.get_item_by_parent_movie_obj)):
                temp_obj = dict(obj)
                temp_obj['MovieId'] = movie[1]
                temp_obj['Movie'] = movie[0]
                self.MoviesDBIO.remove_from_boxset(*self.EmbyServer.Utils.values(temp_obj, queries_videos.delete_movie_set_obj))
                self.emby_db.update_parent_id(*self.EmbyServer.Utils.values(temp_obj, database.queries.delete_parent_boxset_obj))

            self.MoviesDBIO.delete_boxset(*self.EmbyServer.Utils.values(obj, queries_videos.delete_set_obj))

        self.emby_db.remove_item(item_id)
        self.LOG.info("DELETE %s [%s/%s] %s" % (obj['Media'], obj['FileId'], obj['KodiId'], obj['Id']))

class MoviesDBIO():
    def __init__(self, cursor):
        self.cursor = cursor

    def create_entry_unique_id(self):
        self.cursor.execute(queries_videos.create_unique_id)
        return self.cursor.fetchone()[0] + 1

    def create_entry(self):
        self.cursor.execute(queries_videos.create_movie)
        return self.cursor.fetchone()[0] + 1

    def create_entry_set(self):
        self.cursor.execute(queries_videos.create_set)
        return self.cursor.fetchone()[0] + 1

    def create_entry_country(self):
        self.cursor.execute(queries_videos.create_country)
        return self.cursor.fetchone()[0] + 1

    def get(self, *args):
        self.cursor.execute(queries_videos.get_movie, args)
        Data = self.cursor.fetchone()

        if Data:
            return Data[0]

        return None

    def add(self, *args):
        self.cursor.execute(queries_videos.add_movie, args)

    def add_nouserrating(self, *args):
        self.cursor.execute(queries_videos.add_movie_nouserrating, args)

    def update(self, *args):
        self.cursor.execute(queries_videos.update_movie, args)

    def update_nouserrating(self, *args):
        self.cursor.execute(queries_videos.update_movie_nouserrating, args)

    def delete(self, kodi_id, file_id):
        self.cursor.execute(queries_videos.delete_movie, (kodi_id,))
        self.cursor.execute(queries_videos.delete_file, (file_id,))

    # Add the provider id, imdb, tvdb
    def add_unique_id(self, *args):
        self.cursor.execute(queries_videos.add_unique_id, args)

    def add_countries(self, countries, *args):
        for country in countries:
            self.cursor.execute(queries_videos.update_country, (self.get_country(country),) + args)

    def add_country(self, *args):
        country_id = self.create_entry_country()
        self.cursor.execute(queries_videos.add_country, (country_id,) + args)
        return country_id

    def get_country(self, *args):
        self.cursor.execute(queries_videos.get_country, args)
        Data = self.cursor.fetchone()

        if Data:
            return Data[0]

        return self.add_country(*args)

    def add_boxset(self, *args):
        set_id = self.create_entry_set()
        self.cursor.execute(queries_videos.add_set, (set_id,) + args)
        return set_id

    def update_boxset(self, *args):
        self.cursor.execute(queries_videos.update_set, args)

    def set_boxset(self, *args):
        self.cursor.execute(queries_videos.update_movie_set, args)

    def remove_from_boxset(self, *args):
        self.cursor.execute(queries_videos.delete_movie_set, args)

    def delete_boxset(self, *args):
        self.cursor.execute(queries_videos.delete_set, args)
