# -*- coding: utf-8 -*-
# Moovida - Home multimedia server
# Copyright (C) 2006-2009 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 Moovida with Fluendo's plugins.
#
# The GPL part of Moovida is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Moovida" in the root directory of this distribution package
# for details on that license.
#
# Authors:
#   Benjamin Kampmann <benjamin@fluendo.com>


from elisa.plugins.daap.daap_resource_provider import DaapResourceProvider
from elisa.plugins.daap.models import DaapDatabaseListModel,\
        DaapServerInfoModel, DaapSongListModel, DaapPlaylistListModel, \
        DaapPlaylistModel, DaapSongModel, DaapArtistListModel, DaapArtistModel

from elisa.plugins.base.tests.resource_providers import CommonResourceTester

from twisted.trial.unittest import TestCase
from elisa.core.media_uri import MediaUri
from twisted.internet import defer

import re

class Dummy(object):
    pass

class DummyConnection(object):

    revision_id = None
    login_data = None
    session_id = '12'

    def __init__(self, *args):
        self.init_args = args
        self.login_called = 0
        ## self.revision_id = 5
        ## self.login_data = ""

    def login(self, *args):
        # we can always login :D
        self.login_data = args
        self.login_called += 1
        return defer.succeed(True)

    def request(self, *args):
        return defer.succeed(args[1])


class TestDaapResourceProvider(CommonResourceTester, TestCase):

    resource_class = DaapResourceProvider

    working_uris = ['daap://localhost:3689/database', 'daap://server.com/server-info',
                    'daap://otherplace.local/update?session-id',
                    'daap://localhost/', 'daap://localhost/fooo']

    playable_uris = []

    def setUpClass(self):

        # now fake the connection class, it wants to use

        from elisa.plugins.daap import daap_resource_provider
        daap_resource_provider.DaapConnection = DummyConnection

    def test_playable_uris(self):
        """
        do some setup and run the playable uris test from the common class
        """

        self.resource._connections =  {'localhost:1234': DummyConnection(),
                                       'tester:12': DummyConnection() }

        uris = ['daap://localhost:1234/databases/1/items/test.mp3',
                'daap://localhost:1234/databases/12/items/1289.mp3',
                'daap://tester:12/databases/123/items/198.mp3']
                
        for uri in uris:
            model = DaapSongModel()
            model.playable_uri = MediaUri(uri)
            result_uri = MediaUri('%s?session-id=12' % uri)
            result_uri.scheme = 'http'
            self.playable_uris.append( (model, result_uri))
        
        return super(TestDaapResourceProvider, self).test_playable_uris()

    def test_request_creates_connection(self):
        """
        test whether doing a request for a new server really creates a new
        connection every time
        """
        self.assertEquals(self.resource._connections, {})

        model = Dummy()

        uris_to_test = ['daap://localhost/databases', 'daap://server.com/server-info',
                        'daap://other.place.somewhere.strange.org:90',
                        'daap://server.com:3688/login']
        
        for id in xrange(len(uris_to_test)):
            uri = MediaUri(uris_to_test[id])
            model, dfr = self.resource.get(uri, model)
            self.assertEquals(len(self.resource._connections.keys()), id+1)

        # test that it is not recreating localhost because of the missing port
        nums = len(self.resource._connections.keys())
        uri = MediaUri('daap://localhost:3689')
        self.resource.get(uri, model)
        self.assertEquals(len(self.resource._connections.keys()), nums)

    def test_login(self):
        """
        test whether doing a request with and without password works proper
        """
        
        self.assertEquals(self.resource._connections, {})

        uri = MediaUri('daap://user:some_pw@localhost')

        model = Dummy()

        def check_and_second_request(result):
            connection = self.resource._connections.values()[0]
            self.assertEquals(connection.login_data, (u'some_pw',))

            # make sure that it is not creating another one because it does not give
            # the pw
            uri = MediaUri('daap://localhost/databases')
            model, dfr = self.resource.get(uri, Dummy())
            self.assertEquals(len(self.resource._connections), 1)
            return dfr

        model, dfr = self.resource.get(uri, model)
        return dfr.addCallback(check_and_second_request)
       

    def test_no_login_data(self):
        """
        test whether no password is passed through, if not specified
        """
        self.assertEquals(self.resource._connections, {})

        uri = MediaUri('daap://localhost/databases')

        model = Dummy()
        model, dfr = self.resource.get(uri, model) 

        def check(result):
            connection = self.resource._connections.values()[0]
            self.assertEquals(connection.login_data, (None,))

        return dfr.addCallback(check)

    def test_relogin_login_failed(self):
        """
        test that it tries to login again when the login failed
        """
        self.assertEquals(self.resource._connections, {})

        uri = MediaUri('daap://localhost/databases')

        model = Dummy()
        model, dfr = self.resource.get(uri, model) 

        def end_check(result):
            connection = self.resource._connections.values()[0]
            self.assertEquals(connection.login_called, 2)

        def check_and_rerequest(result, model):
            connection = self.resource._connections.values()[0]
            self.assertEquals(connection.login_data, (None,))
            self.assertEquals(connection.login_called, 1)

            connection.revision_id = None

            # now we get another login dfr
            model, dfr = self.resource.get(uri, model)
            return dfr.addCallback(end_check)

        return dfr.addCallback(check_and_rerequest, model)

    def test_no_relogin_login_worked(self):
        """
        test that it tries to login again when the login worked
        """
        self.assertEquals(self.resource._connections, {})

        uri = MediaUri('daap://localhost/databases')

        model = Dummy()
        model, dfr = self.resource.get(uri, model) 

        def got_result(model, previous_model):
            self.failUnless(isinstance(model, DaapDatabaseListModel))

        def check_and_rerequest(result, model):
            connection = self.resource._connections.values()[0]
            self.assertEquals(connection.login_data, (None,))
            self.assertEquals(connection.login_called, 1)

            connection.revision_id = '1234'

            # don't get a login, but the tuple of a request
            new_model, dfr = self.resource.get(uri, model)
            dfr.addCallback(got_result, model)
            return dfr

        return dfr.addCallback(check_and_rerequest, model)


    def test_models(self):
        """
        test that we get the right model for the right uri
        """

        uri_to_model =  {
            'daap://localhost/databases/1/containers/3/items?type=music' :
            DaapPlaylistModel, 'daap://localhost/server-info': DaapServerInfoModel,
            'daap://localhost/databases': DaapDatabaseListModel,
            'daap://localhost/databases/12348/items' : DaapSongListModel,
            'daap://localhost/databases/1/containers': DaapPlaylistListModel,
            'daap://localhost/artists': DaapArtistListModel,
            'daap://localhost/artist/123': DaapArtistModel}

        for uri, klass in uri_to_model.iteritems():
            m_uri = MediaUri(uri)
            model, dfr = self.resource.get(m_uri, None)
            self.assertTrue(isinstance(model, klass))
