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

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

import logging
from datetime import datetime

import api
import embydb_functions as embydb
import musicutils
import _kodi_music
from _common import Items, catch_except
from utils import window, settings, language as lang

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

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

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


class Music(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 = _kodi_music.KodiMusic(self.kodicursor)
        self.pdialog = pdialog

        self.new_time = int(settings('newmusictime'))*1000
        self.directstream = settings('streamMusic') == "true"
        self.enableimportsongrating = settings('enableImportSongRating') == "true"
        self.enableexportsongrating = settings('enableExportSongRating') == "true"
        self.enableupdatesongrating = settings('enableUpdateSongRating') == "true"
        self.userid = window('emby_currUser')
        self.server = window('emby_server%s' % self.userid)

        Items.__init__(self)

    def _get_func(self, item_type, action):

        if item_type == "MusicAlbum":
            actions = {
                'added': self.add_albums,
                'update': self.add_updateAlbum,
                'userdata': self.updateUserdata,
                'remove': self.remove
            }
        elif item_type in ("MusicArtist", "AlbumArtist"):
            actions = {
                'added': self.add_artists,
                'update': self.add_updateArtist,
                'remove': self.remove
            }
        elif item_type == "Audio":
            actions = {
                'added': self.add_songs,
                'update': self.add_updateSong,
                'userdata': self.updateUserdata,
                '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 artists, albums, songs
        views = self.emby_db.getView_byType('music')

        for view in views:
            # Process artists
            self.compare_artists(view)
            # Process albums
            self.compare_albums()
            # Process songs
            self.compare_songs()

        return True

    def compare_artists(self, view):

        all_embyartistsIds = set()
        update_list = list()

        if self.pdialog:
            self.pdialog.update(heading=lang(29999), message="%s Artists..." % lang(33031))

        artists = dict(self.emby_db.get_checksum('MusicArtist'))
        album_artists = dict(self.emby_db.get_checksum('AlbumArtist'))
        emby_artists = self.emby.getArtists(view['id'], dialog=self.pdialog)

        for item in emby_artists['Items']:

            if self.should_stop():
                    return False

            item_id = item['Id']
            API = api.API(item)

            all_embyartistsIds.add(item_id)
            if item_id in artists:
                if artists[item_id] != API.get_checksum():
                    # Only update if artist is not in Kodi or checksum is different
                    update_list.append(item_id)
            elif album_artists.get(item_id) != API.get_checksum():
                # Only update if artist is not in Kodi or checksum is different
                update_list.append(item_id)

            #compare_to.pop(item_id, None)

        log.info("Update for Artist: %s", update_list)

        emby_items = self.emby.getFullItems(update_list)
        total = len(update_list)

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

        # Process additions and updates
        if emby_items:
            self.process_all("MusicArtist", "update", emby_items, total)
        # Process removals
        for artist in artists:
            if artist not in all_embyartistsIds and artists[artist] is not None:
                self.remove(artist)

    def compare_albums(self):

        if self.pdialog:
            self.pdialog.update(heading=lang(29999), message="%s Albums..." % lang(33031))

        albums = dict(self.emby_db.get_checksum('MusicAlbum'))
        emby_albums = self.emby.getAlbums(basic=True, dialog=self.pdialog)

        return self.compare("MusicAlbum", emby_albums['Items'], albums)

    def compare_songs(self):

        if self.pdialog:
            self.pdialog.update(heading=lang(29999), message="%s Songs..." % lang(33031))

        songs = dict(self.emby_db.get_checksum('Audio'))
        emby_songs = self.emby.getSongs(basic=True, dialog=self.pdialog)

        return self.compare("Audio", emby_songs['Items'], songs)

    def add_artists(self, items, total=None):

        for item in self.added(items, total):
            if self.add_updateArtist(item):
                # Add albums
                all_albums = self.emby.getAlbumsbyArtist(item['Id'])
                self.add_albums(all_albums['Items'])

    def add_albums(self, items, total=None):

        update = True if not self.total else False

        for item in self.added(items, total, update):
            self.title = "%s - %s" % (item.get('AlbumArtist', "unknown"), self.title)

            if self.add_updateAlbum(item):
                # Add songs
                all_songs = self.emby.getSongsbyAlbum(item['Id'])
                self.add_songs(all_songs['Items'])

    def add_songs(self, items, total=None):

        update = True if not self.total else False

        for item in self.added(items, total, update):
            self.title = "%s - %s" % (item.get('AlbumArtist', "unknown"), self.title)

            if self.add_updateSong(item):
                self.content_pop(self.title)

    @catch_except()
    def add_updateArtist(self, item, artisttype="MusicArtist"):
        # Process a single artist
        kodicursor = self.kodicursor
        emby_db = self.emby_db
        artwork = self.artwork
        API = api.API(item)

        update_item = True
        itemid = item['Id']
        emby_dbitem = emby_db.getItem_byId(itemid)
        try:
            artistid = emby_dbitem[0]
        except TypeError:
            update_item = False
            log.debug("artistid: %s not found", itemid)
        else:
            pass

        ##### The artist details #####
        lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        dateadded = API.get_date_created()
        checksum = API.get_checksum()

        name = item['Name']
        musicBrainzId = API.get_provider('MusicBrainzArtist')
        genres = " / ".join(item.get('Genres'))
        bio = API.get_overview()

        # Associate artwork
        artworks = artwork.get_all_artwork(item, parent_info=True)
        thumb = artworks['Primary']
        backdrops = artworks['Backdrop'] # List

        if thumb:
            thumb = "<thumb>%s</thumb>" % thumb
        if backdrops:
            fanart = "<fanart>%s</fanart>" % backdrops[0]
        else:
            fanart = ""


        ##### UPDATE THE ARTIST #####
        if update_item:
            log.info("UPDATE artist itemid: %s - Name: %s", itemid, name)
            # Update the checksum in emby table
            emby_db.updateReference(itemid, checksum)

        ##### OR ADD THE ARTIST #####
        else:
            log.info("ADD artist itemid: %s - Name: %s", itemid, name)
            # safety checks: It looks like Emby supports the same artist multiple times.
            # Kodi doesn't allow that. In case that happens we just merge the artist entries.
            artistid = self.kodi_db.get_artist(name, musicBrainzId)
            # Create the reference in emby table
            emby_db.addReference(itemid, artistid, artisttype, "artist", checksum=checksum)

        # Process the artist
        if self.kodi_version > 15:
            self.kodi_db.update_artist_16(genres, bio, thumb, fanart, lastScraped, artistid)
        else:
            self.kodi_db.update_artist(genres, bio, thumb, fanart, lastScraped, dateadded, artistid)

        # Update artwork
        artwork.add_artwork(artworks, artistid, "artist", kodicursor)

        return True

    @catch_except()
    def add_updateAlbum(self, item):
        # Process a single artist
        emby = self.emby
        kodicursor = self.kodicursor
        emby_db = self.emby_db
        artwork = self.artwork
        API = api.API(item)

        update_item = True
        itemid = item['Id']
        emby_dbitem = emby_db.getItem_byId(itemid)
        try:
            albumid = emby_dbitem[0]
        except TypeError:
            update_item = False
            log.debug("albumid: %s not found", itemid)

        ##### The album details #####
        lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        dateadded = API.get_date_created()
        userdata = API.get_userdata()
        checksum = API.get_checksum()

        name = item['Name']
        musicBrainzId = API.get_provider('MusicBrainzAlbum')
        year = item.get('ProductionYear')
        genres = item.get('Genres')
        genre = " / ".join(genres)
        bio = API.get_overview()
        rating = 0
        artists = item['AlbumArtists']
        artistname = []
        for artist in artists:
            artistname.append(artist['Name'])
        artistname = " / ".join(artistname)

        # Associate artwork
        artworks = artwork.get_all_artwork(item, parent_info=True)
        thumb = artworks['Primary']
        if thumb:
            thumb = "<thumb>%s</thumb>" % thumb

        ##### UPDATE THE ALBUM #####
        if update_item:
            log.info("UPDATE album itemid: %s - Name: %s", itemid, name)
            # Update the checksum in emby table
            emby_db.updateReference(itemid, checksum)

        ##### OR ADD THE ALBUM #####
        else:
            log.info("ADD album itemid: %s - Name: %s", itemid, name)
            # safety checks: It looks like Emby supports the same artist multiple times.
            # Kodi doesn't allow that. In case that happens we just merge the artist entries.
            albumid = self.kodi_db.get_album(name, musicBrainzId)
            # Create the reference in emby table
            emby_db.addReference(itemid, albumid, "MusicAlbum", "album", checksum=checksum)

        # Process the album info
        if self.kodi_version == 17:
            # Kodi Krypton
            self.kodi_db.update_album_17(artistname, year, genre, bio, thumb, rating, lastScraped,
                                         "album", albumid)
        elif self.kodi_version == 16:
            # Kodi Jarvis
            self.kodi_db.update_album(artistname, year, genre, bio, thumb, rating, lastScraped,
                                      "album", albumid)
        elif self.kodi_version == 15:
            # Kodi Isengard
            self.kodi_db.update_album_15(artistname, year, genre, bio, thumb, rating, lastScraped,
                                         dateadded, "album", albumid)
        else:
            # TODO: Remove Helix code when Krypton is RC
            self.kodi_db.update_album_14(artistname, year, genre, bio, thumb, rating, lastScraped,
                                         dateadded, albumid)

        # Assign main artists to album
        for artist in item['AlbumArtists']:
            artistname = artist['Name']
            artistId = artist['Id']
            emby_dbartist = emby_db.getItem_byId(artistId)
            try:
                artistid = emby_dbartist[0]
            except TypeError:
                # Artist does not exist in emby database, create the reference
                artist = emby.getItem(artistId)
                self.add_updateArtist(artist, artisttype="AlbumArtist")
                emby_dbartist = emby_db.getItem_byId(artistId)
                artistid = emby_dbartist[0]
            else:
                # Best take this name over anything else.
                self.kodi_db.update_artist_name(artistid, artistname)

            # Add artist to album
            self.kodi_db.link_artist(artistid, albumid, artistname)
            # Update emby reference with parentid
            emby_db.updateParentId(artistId, albumid)

        for artist in item['ArtistItems']:
            artistId = artist['Id']
            emby_dbartist = emby_db.getItem_byId(artistId)
            try:
                artistid = emby_dbartist[0]
            except TypeError:
                pass
            else:
                # Update discography
                self.kodi_db.add_discography(artistid, name, year)

        # Add genres
        self.kodi_db.add_genres(albumid, genres, "album")
        # Update artwork
        artwork.add_artwork(artworks, albumid, "album", kodicursor)

        return True

    @catch_except()
    def add_updateSong(self, item):
        # Process single song
        kodicursor = self.kodicursor
        emby = self.emby
        emby_db = self.emby_db
        artwork = self.artwork
        API = api.API(item)

        update_item = True
        itemid = item['Id']
        emby_dbitem = emby_db.getItem_byId(itemid)
        try:
            songid = emby_dbitem[0]
            pathid = emby_dbitem[2]
            albumid = emby_dbitem[3]
        except TypeError:
            update_item = False
            log.debug("songid: %s not found", itemid)
            songid = self.kodi_db.create_entry_song()

        ##### The song details #####
        checksum = API.get_checksum()
        dateadded = API.get_date_created()
        userdata = API.get_userdata()
        playcount = userdata['PlayCount']
        dateplayed = userdata['LastPlayedDate']

        # item details
        title = item['Name']
        musicBrainzId = API.get_provider('MusicBrainzTrackId')
        genres = item.get('Genres')
        genre = " / ".join(genres)
        artists = " / ".join(item['Artists'])
        tracknumber = item.get('IndexNumber', 0)
        disc = item.get('ParentIndexNumber', 1)
        if disc == 1:
            track = tracknumber
        else:
            track = disc*2**16 + tracknumber
        year = item.get('ProductionYear')
        duration = API.get_runtime()
        rating = 0

        #if enabled, try to get the rating from file and/or emby
        if not self.directstream:
            rating, comment, hasEmbeddedCover = musicutils.getAdditionalSongTags(itemid, rating, API, kodicursor, emby_db, self.enableimportsongrating, self.enableexportsongrating, self.enableupdatesongrating)
        else:
            hasEmbeddedCover = False
            comment = API.get_overview()


        ##### GET THE FILE AND PATH #####
        if self.directstream:
            path = "%s/emby/Audio/%s/" % (self.server, itemid)
            extensions = ['mp3', 'aac', 'ogg', 'oga', 'webma', 'wma', 'flac']

            if 'Container' in item and item['Container'].lower() in extensions:
                filename = "stream.%s?static=true" % item['Container']
            else:
                filename = "stream.mp3?static=true"
        else:
            playurl = API.get_file_path()

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

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

            path = playurl.replace(filename, "")
            window('emby_pathverified', value="true")

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

            # Update path
            self.kodi_db.update_path(pathid, path)

            # Update the song entry
            self.kodi_db.update_song(albumid, artists, genre, title, track, duration, year,
                                     filename, playcount, dateplayed, rating, comment, songid)

            # Update the checksum in emby table
            emby_db.updateReference(itemid, checksum)

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

            # Add path
            pathid = self.kodi_db.add_path(path)

            try:
                # Get the album
                emby_dbalbum = emby_db.getItem_byId(item['AlbumId'])
                albumid = emby_dbalbum[0]
            except KeyError:
                # Verify if there's an album associated.
                album_name = item.get('Album')
                if album_name:
                    log.info("Creating virtual music album for song: %s", itemid)
                    albumid = self.kodi_db.get_album(album_name, API.get_provider('MusicBrainzAlbum'))
                    emby_db.addReference("%salbum%s" % (itemid, albumid), albumid, "MusicAlbum_", "album")
                else:
                    # No album Id associated to the song.
                    log.error("Song itemid: %s has no albumId associated", itemid)
                    return False

            except TypeError:
                # No album found. Let's create it
                log.info("Album database entry missing.")
                emby_albumId = item['AlbumId']
                album = emby.getItem(emby_albumId)
                self.add_updateAlbum(album)
                emby_dbalbum = emby_db.getItem_byId(emby_albumId)
                try:
                    albumid = emby_dbalbum[0]
                    log.info("Found albumid: %s", albumid)
                except TypeError:
                    # No album found, create a single's album
                    log.info("Failed to add album. Creating singles.")
                    albumid = self.kodi_db.create_entry_album()
                    if self.kodi_version == 16:
                        self.kodi_db.add_single(albumid, genre, year, "single")

                    elif self.kodi_version == 15:
                        self.kodi_db.add_single_15(albumid, genre, year, dateadded, "single")

                    else:
                        # TODO: Remove Helix code when Krypton is RC
                        self.kodi_db.add_single_14(albumid, genre, year, dateadded)

            # Create the song entry
            self.kodi_db.add_song(songid, albumid, pathid, artists, genre, title, track, duration,
                                  year, filename, musicBrainzId, playcount, dateplayed, rating)

            # Create the reference in emby table
            emby_db.addReference(itemid, songid, "Audio", "song", pathid=pathid, parentid=albumid,
                                 checksum=checksum)

        # Link song to album
        self.kodi_db.link_song_album(songid, albumid, track, title, duration)
        # Create default role
        if self.kodi_version > 16:
            self.kodi_db.add_role()

        # Link song to artists
        for index, artist in enumerate(item['ArtistItems']):

            artist_name = artist['Name']
            artist_eid = artist['Id']
            artist_edb = emby_db.getItem_byId(artist_eid)
            try:
                artistid = artist_edb[0]
            except TypeError:
                # Artist is missing from emby database, add it.
                artist_full = emby.getItem(artist_eid)
                self.add_updateArtist(artist_full)
                artist_edb = emby_db.getItem_byId(artist_eid)
                artistid = artist_edb[0] if artist_edb else None
            except Exception:
                artistid = None

            if artistid:
                # Link song to artist
                self.kodi_db.link_song_artist(artistid, songid, index, artist_name)

        # Verify if album artist exists
        album_artists = []
        for artist in item['AlbumArtists']:

            artist_name = artist['Name']
            album_artists.append(artist_name)
            artist_eid = artist['Id']
            artist_edb = emby_db.getItem_byId(artist_eid)
            try:
                artistid = artist_edb[0]
            except TypeError:
                # Artist is missing from emby database, add it.
                artist_full = emby.getItem(artist_eid)
                self.add_updateArtist(artist_full)
                artist_edb = emby_db.getItem_byId(artist_eid)
                artistid = artist_edb[0]
            finally:
                # Link artist to album
                self.kodi_db.link_artist(artistid, albumid, artist_name)
                # Update discography
                if item.get('Album'):
                    self.kodi_db.add_discography(artistid, item['Album'], 0)

        # Artist names
        album_artists = " / ".join(album_artists)
        self.kodi_db.get_album_artist(albumid, album_artists)

        # Add genres
        self.kodi_db.add_genres(songid, genres, "song")

        # Update artwork
        allart = artwork.get_all_artwork(item, parent_info=True)
        if hasEmbeddedCover:
            allart["Primary"] = "image://music@" + artwork.single_urlencode(playurl)
        artwork.add_artwork(allart, songid, "song", kodicursor)

        if item.get('AlbumId') is None:
            # Update album artwork
            artwork.add_artwork(allart, albumid, "album", kodicursor)

        return True

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

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

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

        if mediatype == "song":

            #should we ignore this item ?
            #happens when userdata updated by ratings method
            if window("ignore-update-%s" %itemid):
                window("ignore-update-%s" %itemid,clear=True)
                return

            # Process playstates
            playcount = userdata['PlayCount']
            dateplayed = userdata['LastPlayedDate']

            #process item ratings
            rating, comment, hasEmbeddedCover = musicutils.getAdditionalSongTags(itemid, rating, API, kodicursor, emby_db, self.enableimportsongrating, self.enableexportsongrating, self.enableupdatesongrating)
            self.kodi_db.rate_song(playcount, dateplayed, rating, kodiid)

        emby_db.updateReference(itemid, checksum)

    def remove(self, itemid):
        # Remove kodiid, fileid, pathid, emby reference
        emby_db = self.emby_db

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

        ##### PROCESS ITEM #####

        # Remove the emby reference
        emby_db.removeItem(itemid)


        ##### IF SONG #####

        if mediatype == "song":
            # Delete song
            self.removeSong(kodiid)
            # This should only address single song scenario, where server doesn't actually
            # create an album for the song.
            emby_db.removeWildItem(itemid)

            for item in emby_db.getItem_byWildId(itemid):

                item_kid = item[0]
                item_mediatype = item[1]

                if item_mediatype == "album":
                    childs = emby_db.getItem_byParentId(item_kid, "song")
                    if not childs:
                        # Delete album
                        self.removeAlbum(item_kid)

        ##### IF ALBUM #####

        elif mediatype == "album":
            # Delete songs, album
            album_songs = emby_db.getItem_byParentId(kodiid, "song")
            for song in album_songs:
                self.removeSong(song[1])
            else:
                # Remove emby songs
                emby_db.removeItems_byParentId(kodiid, "song")

            # Remove the album
            self.removeAlbum(kodiid)

        ##### IF ARTIST #####

        elif mediatype == "artist":
            # Delete songs, album, artist
            albums = emby_db.getItem_byParentId(kodiid, "album")
            for album in albums:
                albumid = album[1]
                album_songs = emby_db.getItem_byParentId(albumid, "song")
                for song in album_songs:
                    self.removeSong(song[1])
                else:
                    # Remove emby song
                    emby_db.removeItems_byParentId(albumid, "song")
                    # Remove emby artist
                    emby_db.removeItems_byParentId(albumid, "artist")
                    # Remove kodi album
                    self.removeAlbum(albumid)
            else:
                # Remove emby albums
                emby_db.removeItems_byParentId(kodiid, "album")

            # Remove artist
            self.removeArtist(kodiid)

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

    def removeSong(self, kodi_id):

        self.artwork.delete_artwork(kodi_id, "song", self.kodicursor)
        self.kodi_db.remove_song(kodi_id)

    def removeAlbum(self, kodi_id):

        self.artwork.delete_artwork(kodi_id, "album", self.kodicursor)
        self.kodi_db.remove_album(kodi_id)

    def removeArtist(self, kodi_id):

        self.artwork.delete_artwork(kodi_id, "artist", self.kodicursor)
        self.kodi_db.remove_artist(kodi_id)