# -*- coding: utf-8 -*-
# Elisa - Home multimedia server
# Copyright (C) 2006-2008 Fluendo Embedded S.L. (www.fluendo.com).
# All rights reserved.
#
# This file is available under one of two license agreements.
#
# This file is licensed under the GPL version 3.
# See "LICENSE.GPL" in the root of this distribution including a special
# exception to use Elisa with Fluendo's plugins.
#
# The GPL part of Elisa is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Elisa" in the root directory of this distribution package
# for details on that license.

"""
IpodMediaProvider component class
"""


__maintainer__ = 'Benjamin Kampmann <benjamin@fluendo.com>'

from elisa.base_components.media_provider import MediaProvider
from elisa.core.media_uri import MediaUri, quote, _unicode
from elisa.core.application import ComponentsLoadedMessage
from elisa.core.component import InitializeFailure
from elisa.core.observers.dict import DictObservable
from elisa.core.utils import sorting
from elisa.core import log

import os

from twisted.internet import defer, threads

"""
TODO:

- support artwork
- support photos (experimental in gpod)
- support videos (?)

"""

import gpod

class Artist:

    def __init__(self, name):
        self.name = name
        self.albums = {}

    def __repr__(self):
        return "<Artist name=%r %r albums>" % (self.name, len(self.albums))

    def add_album(self, album):
        if album.name:
            self.albums[album.name] = album

class Album:

    def __init__(self, name):
        self.name = name
        self.songs = {}

    def __repr__(self):
        return "<Album name=%r %r songs>" % (self.name, len(self.songs))

    def add_song(self, song):
        self.songs[song.title] = song
    
class Song:

    def __init__(self, song_id, nr, name, path, typ):
        self.id = song_id
        self.nr = nr
        self.name = name
        self.title = '%s - %s' % (nr, name)
        self.path = path
        self.filetype = typ

def load_artists(db):
    artists = {}

    for track in db:
        artist_name = _unicode(track['artist'])
        album_name = _unicode(track['album'])

        # workaround some gpod oddities with recent iPods (tested with
        # 6th gen Nano)
        if artist_name in ('', None):
            continue
        if album_name in ('', None):
            continue

        track_id = track['id']
        title = _unicode(track['title'])
        track_nb = str(track['track_nr']).rjust(2, '0')
        track_type = track['filetype']
        song_path = track['ipod_path'].replace(':','/')
        song_title = '%s - %s' % (track_nb, title)
        
        if artist_name not in artists:
            artist = Artist(artist_name)
            artists[artist_name] = artist
        else:
            artist = artists[artist_name]

        if album_name not in artist.albums:
            album = Album(album_name)
            artist.add_album(album)
        else:
            album = artist.albums[album_name]
 
        if track_id not in album.songs:
            song = Song(track_id, track_nb, title, song_path, track_type)
            album.add_song(song)

    return artists



class IpodMedia(MediaProvider):
    """
    This class implements IPod support
    """
    def __init__(self):
        MediaProvider.__init__(self)
        self.dbs = {}

    def scannable_uri_schemes__get(self):
        return []

    def supported_uri_schemes__get(self):
        return {'ipod' : 0}

    def _blocking_get_media_type(self, uri):
        return {'file_type' : 'directory', 'mime_type' : '' }

    def _blocking_is_directory(self, uri):
        if uri.get_param('id'):
            return False
        return os.path.exists("%s%s" % (uri.host,uri.path))

    def _blocking_has_children_with_types(self, uri, media_types):
        has_children = False
        if not os.path.isfile(uri.path):
            has_children = len(set(['audio',
                                    'directory']).intersection(media_types)) > 0
        return has_children
    
    def get_direct_children(self, uri, l):
        d = threads.deferToThread(self._blocking_get_direct_children, uri, l)
        return d

    def _blocking_get_direct_children(self, uri, children):
        artist = uri.get_param('artist')
        album = uri.get_param('album')

        self.debug("Retrieving children for %s, artist: %s, album: %s",
                   uri, artist, album)

        # libgpod doesn't support unicode....
        path = str(uri.path)
        if path not in self.dbs:
            # lazily fetch ipod db from dbs dict
            try:
                if os.path.isdir(path):
                    db = gpod.Database(path)
                elif os.path.isfile(path):
                    # it's a iTunes database
                    db = gpod.Database('', local=True, localdb=path)
                else:
                    return children
            except gpod.DatabaseException,e:
                self.warning("Could not read the Database: %s"  % e)
                return children
            else:
                artists = load_artists(db)
                self.dbs[path] = (db, artists)

        db, artists = self.dbs[path]

        db_artist = None
        db_album = None

        if artist:
            db_artist = artists.get(artist)

        if album and db_artist:
            db_album = db_artist.albums.get(album)

        if db_album:
            self.debug("Fetching songs for %s album", db_album.name)
            # fetch songs of given album

            # sort alpha-numerically
            song_titles = db_album.songs.keys()
            sorting.natural_sort(song_titles)
            
            for song_title in song_titles:
                song = db_album.songs[song_title]
                metadata = DictObservable()
                self.debug("Processing song: %s" % song_title)
                child_uri = MediaUri("file://%s%s" % (str(uri.path), song.path))
                # FIXME: REMOVE THIS UGLY HACK !!!
                metadata['song_artist']= artist
                metadata['song_album'] = album
                metadata['song'] = song_title
                child_uri.label = song_title
                children.append((child_uri, metadata))
        elif db_artist:
            # fetch albums list of given artist
            self.debug("Fetching albums for %r artist", db_artist.name)
            
            # sort alpha-numerically
            album_titles = db_artist.albums.keys()
            sorting.natural_sort(album_titles)

            self.debug("albums of %r: %r", db_artist.name, album_titles)
            for album_title in album_titles:
                album = db_artist.albums[album_title]
                metadata = DictObservable()
                child_uri = MediaUri("ipod://%s" % uri.path)
                metadata['artist'] = artist
                metadata['album'] = album.name
                # Don't use a DictObservable for params!
                child_uri.set_params({'artist':artist,'album':album.name})
                child_uri.label = album.name
                children.append((child_uri, metadata))
        else:
            # return artists list
            
            # sort alpha-numerically
            artist_names = artists.keys()
            sorting.natural_sort(artist_names)
            
            for artist_name in artist_names:
                artist = artists[artist_name]
                metadata = DictObservable()
                child_uri = MediaUri("ipod://%s" % uri.path)
                metadata['artist'] = artist.name
                # Don't use a DictObservable for params!
                child_uri.set_param('artist', artist.name)
                child_uri.label = artist.name
                children.append((child_uri, metadata))

        return children
