# -*- 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.


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

from elisa.base_components.media_provider import MediaProvider
from elisa.core.media_uri import MediaUri
from elisa.core import common, component
import os
from twisted.internet import defer, threads

from elisa.extern.flickrest import Flickr, FlickrError
from twill import commands as twill


from elisa.extern.translation import gettexter, N_
T_ = gettexter('elisa-flickr')

plugin_registry = common.application.plugin_registry
InternetLocationMessage = plugin_registry.get_component_class('base:internet_location_message')

class FlickrMedia(MediaProvider):
    """
    DOCME
    """
    secret = '14693e87a1349c20'
    key = '4bb9b930c2f01fab6b3fd20e02d165fb'

    config_doc = {'per_page' :  'show so many entries should be' \
                                'shown per page?',
                  'login': 'Your Flickr username (optional)',
                  'password': 'Your Flickr password (optional)',
                  'locations': 'A list of tags or flickr:// URIs'
                  }


    default_config = {'per_page': '30',
                      'login': '',
                      'password': '',
                      'locations': []
                      }


    def initialize(self):
        MediaProvider.initialize(self)
        self._cache = {}
        self._login = self.config.get('login','')
        self._password = self.config.get('password','')
        self._per_page = int(self.config.get('per_page', '30'))
        self._locations = self.config.get('locations', [])
        
        self._show_pages = False # Hardcoded, but I'll be a config-option
                                 # when it is usable
        self._connected = False
        self._perm = "read"

        if self._login and self._password:
            self._perm = "write"

        try:
            self._flickr = Flickr(self.key, self.secret, self._perm)
            if self._perm == "write":
                threads.deferToThread(self._connect_flickr)
            else:
                self._connected_cb(None)
        except FlickrError, e:
            raise component.InitializeFailure(self.name, e.get('msg'))


    def _connected_cb(self, auth):
        self.debug("Connected, %r", auth)
        self._connected = True

        uri = "flickr:///"
        action_type = InternetLocationMessage.ActionType.LOCATION_ADDED
        msg = InternetLocationMessage(action_type, "flickr", 'flickr', uri,
                                           media_types=['image',],
                                           theme_icon='flickr')
        common.application.bus.send_message(msg)

    def _error_cb(self, failure):
        self.warning('Failed to connect to flickr. Maybe the API-Key or the'
                     ' Secret is wrong. Error message :%s',
                     failure.getErrorMessage())

    def _connect_flickr(self):

        def auth_open_url(state):
            if state is None:
                self._connected_cb(state)
            else:
                twill.go(state['url'])
                twill.formvalue(1, 'login', self._login)
                twill.formvalue(1, 'passwd', self._password)
                twill.submit()
                twill.config('readonly_controls_writeable', 'True')
                try:
                    twill.formvalue(3, 'done_auth', '1')
                    twill.submit()
                except:
                    # already granted
                    pass
                dfr = self._flickr.authenticate_2(state)
                dfr.addCallbacks(self._connected_cb, self._error_cb)
                return dfr

        self.debug("Authenticating")
        self._flickr.authenticate_1().addCallbacks(auth_open_url,
                                                   self._error_cb)

    def scannable_uri_schemes__get(self):
        return []

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

    def get_real_uri(self, uri):
        if self._blocking_is_directory(uri):
            return uri

        server = uri.get_param('server')
        secret = uri.get_param('secret')
        # remove the starting '/'
        id = uri.path[1:]
        # build the real URL for the Image
        return MediaUri(self._build_uri(id, server, secret, 'medium'))

    def _build_uri(self, id, server, secret, size):
        size_suffixes = {'square': '_s',
                         'thumbnail': '_t',
                         'small': '_m',
                         'medium': '',
                         'large': '_b',
                         'original': '_o',
                        }
        uri = "http://static.flickr.com/%s/%s_%s%s.jpg" % (server, id, secret,
                                                           size_suffixes[size])
        return uri

    def get_media_type(self, uri):
        return defer.succeed(self._blocking_get_media_type(uri))

    def _blocking_get_media_type(self, uri):
        if self._blocking_is_directory(uri):
            return {'file_type' : 'directory', 'mime_type' : ''}
        else:
            return {'file_type' : 'image', 'mime_type' : 'image/jpeg'}


    def is_directory(self, uri):
        return defer.succeed(self._blocking_is_directory(uri))

    def _blocking_is_directory(self, uri):
        return uri.host != "photo"

    def has_children_with_types(self, uri, media_types):
        df = threads.deferToThread(self._blocking_has_children_with_types, uri,
                                   media_types)
        return df

    def _blocking_has_children_with_types(self, uri, media_types):
        has_children = False
        media_type_ok = len(set(['image',
                                 'directory']).intersection(media_types)) > 0
        if uri.host != 'photo' and media_type_ok:
            has_children = True
        return has_children


    def get_direct_children(self, uri, children_with_info):
        if not self._connected:
            dfr = defer.Deferred()
            dfr.callback(children_with_info)

        elif uri.host == '':
            return self._build_main_menu(children_with_info)
        else:
            dfr = self._dispatch(uri, children_with_info)

        return dfr


    def next_location(self, from_uri, root=None):
        next_uri = None
        
        def got_children(children):
            self._cache[from_uri] = [ str(i[0]) for i in children ]
            return MediaUri(self._cache[from_uri][0])
        
        if self._blocking_is_directory(from_uri):
            dfr = self.get_direct_children(from_uri,[])
            dfr.addCallback(got_children)
        else:
            for dir_uri, items in self._cache.iteritems():
                try:
                    index = items.index(str(from_uri))
                except:
                    pass
                else:
                    try:
                        next_uri = MediaUri(items[index+1])
                    except:
                        pass
                    else:
                        break
            if next_uri:
                if self._blocking_is_directory(next_uri):
                    dfr = self.next_location(next_uri, root)
                else:
                    dfr = defer.succeed(next_uri)
            else:
                dfr = defer.succeed(None)
        return dfr
    

    def _dispatch(self, uri, children_with_info):
        page = int(uri.get_param('page', '0'))
        
        if uri.host == 'mine':
            if uri.path[1:] == 'latest':
                dfr = self._flickr.photos_search(user_id='me', page=page,
                                                 per_page=self._per_page)
                dfr.addCallback(self._got_images, children_with_info, str(uri))

            elif uri.path[1:] == 'photosets':
                dfr = self._flickr.photosets_getList(per_page=self._per_page)
                dfr.addCallback(self._got_photosets, children_with_info)

            elif uri.path[1:] == "tags":
                dfr = self._flickr.tags_getListUser(per_page=self._per_page)
                dfr.addCallback(self._got_whotags, children_with_info)

            elif uri.path[1:] == 'favorites':
                dfr = self._flickr.favorites_getList(page=page)
                dfr.addCallback(self._got_images, children_with_info,str(uri))

            else:
                dfr = threads.deferToThread(self._build_mine_menu,children_with_info)

        elif uri.host == 'photo':
            dfr = defer.Deferred()
            dfr.callback(children_with_info)

        elif uri.host == 'photoset':
            dfr = self._flickr.photosets_getPhotos(photoset_id=uri.path[1:],
                                                   page=page,
                                                   per_page=self._per_page)
            dfr.addCallback(self._got_images_in_photoset, children_with_info,
                            str(uri))

        elif uri.host == 'interesting':
            dfr = self._flickr.interestingness_getList(page=page,
                                                       per_page=self._per_page)
            dfr.addCallback(self._got_images, children_with_info, str(uri))

        elif uri.host == "hottags":
            dfr = self._flickr.tags_getHotList()
            dfr.addCallback(self._got_hottags, children_with_info)

        elif uri.host == "tag":
            who = uri.get_param('who')
            dfr = self._flickr.photos_search(tags=uri.path[1:],
                                             user_id=who,
                                             page=page,
                                             per_page=self._per_page)
            dfr.addCallback(self._got_images, children_with_info, str(uri))
            
        elif uri.host == "custom":
            
            for location in self._locations:
                children_with_info.append((MediaUri(location), {}))
            dfr = defer.succeed(children_with_info)
        else:
            dfr = defer.succeed(children_with_info)
        return dfr

    def _build_mine_menu(self, list_of_children):

        mine_menu  = {"flickr://mine/favorites" : T_(N_("my Favorites")),
                      "flickr://mine/latest" : T_(N_("latest")),
                      "flickr://mine/photosets" : T_(N_("in photosets")),
                      "flickr://mine/tags" : T_(N_("by tag"))}

        return self._from_list(list_of_children, mine_menu)


    def _build_main_menu(self, list_of_children):

        # the interesting uri
        interesting = MediaUri("flickr://interesting/")

        # create the main-menu
        main_menu = {"flickr://hottags/": T_(N_("Hot Tags")),}
        if self._perm == 'write':
            main_menu["flickr://mine/latest"] = T_(N_("My Photos"))
        if self._locations:
            main_menu["flickr://custom/"] = T_(N_("Custom locations"))
            
        self._from_list(list_of_children, main_menu)

        # fetch interesting images for the main-menu, too
        return self._dispatch(interesting, list_of_children)

    def _from_list(self, list_of_children, menu):

        for entry, label in menu.items():
            list_of_children.append((self._make_uri(entry, label), {}))


        return list_of_children

    def _make_uri(self, uri, label=None):
        m = MediaUri(uri)
        if label != None:
            m.label = label
        return m

    def _got_images(self, ret, list_of_children, url=""):
        photos = ret.find('photos')
        pages = int(photos.get('pages'))
        page = int(photos.get('page'))

        for nu in ret.findall("photos/photo"):
            self._add_photo(nu, list_of_children)

        # We don't support more pages yet
        if (pages > 1 and page == 1 and self._show_pages):
            i = 1
            while (i < pages):
                i = i + 1
                m = MediaUri(url)
                m.set_param('page', i)
                m.label = T_(N_('page %s'), i)
                list_of_children.append((m,{}))
        return list_of_children

    def _got_images_in_photoset(self, ret, list_of_children, url=''):
        photos = ret.find('photoset')
        pages = int(photos.get('pages'))
        page = int(photos.get('page'))

        for nu in ret.findall("photoset/photo"):
            self._add_photo(nu, list_of_children)

        #  We don't support more pages before the GUADEC 07
        if (pages > 1 and page == 1 and self._show_pages):
            i = 1
            while (i < pages):
                i = i + 1
                m = MediaUri(url)
                m.set_param('page', i)
                m.label = T_(N_('page %s'), i)
                list_of_children.append((m,{}))
        return list_of_children

    def _add_photo(self, nu, list_of_children):
        id = nu.get('id')
        title = nu.get('title')
        server = nu.get('server')
        secret = nu.get('secret')

        uri = self._make_uri("flickr://photo/%s?server=%s&secret=%s" %
                                    (id, server, secret), title)
        list_of_children.append((uri, {
                'title' : title,
                'server' : server,
                'secret' : secret,
                'owner' : nu.get('owner'),
                'ispublic' : nu.get('ispublic'),
                'isfriend' : nu.get('isfriend'),
                'isfamily' : nu.get('isfamily'),
                'default_image': MediaUri(self._build_uri(id, server, secret,
                                                          'small')
                                         )
                                        }
                                    )
                                )
        return list_of_children

    def _got_hottags(self, ret, list_of_children):
        for nu in ret.findall("hottags/tag"):
            name = nu.text
            score = nu.get('score')
            uri = MediaUri("flickr://tag/%s" % name)
            list_of_children.append(( uri, {"name" : name, score: "score"}))
        return list_of_children

    def _got_whotags(self, ret, list_of_children):
        who = ret.find("who").get('id')
        for nu in ret.findall("who/tags/tag"):
            name = nu.text
            score = nu.get('score')
            uri = MediaUri("flickr://tag/%s?who=%s" % (name, who))
            list_of_children.append(( uri, {"name" : name, score: "score"}))
        return list_of_children

    def _got_photosets(self, ret, list_of_children):
        for nu in ret.findall("photosets/photoset"):
            id = nu.get('id')
            name = nu.find('title').text
            server = nu.get('server')
            photos = nu.get('photos')
            secret = nu.get('secret')
            primary = nu.get('primary')
            uri = self._make_uri("flickr://photoset/%s" % id, name)
            list_of_children.append((uri, { 'name' : name,
                                            'id' : id,
                                            'server' : server,
                                            'photos' : photos,
                                            'secret' : secret,
                                            'primary' : primary}
                                            )
                                    )
        return list_of_children

    
