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

import os

from twisted.trial.unittest import TestCase, SkipTest
from twisted.internet import defer, reactor
from twisted.internet.protocol import Protocol

from twisted.web2.http import Response, StatusResponse
from twisted.web2.channel.http import HTTPFactory, HTTPChannel
from twisted.web2 import iweb, server
from twisted.web2 import responsecode
from twisted.web2.resource import Resource, LeafResource

from zope.interface import implements

from elisa.core.media_uri import MediaUri
from elisa.core.utils.misc import run_functional_tests_check
from elisa.plugins.shoutcast.shoutcast_resource import ShoutcastResource
from elisa.plugins.base.models.media import PlayableModel

TESTS_DIR = os.path.dirname(__file__)


class PlaylistResource(object):
    implements(iweb.IResource)

    def locateChild(self, request, segments):
        return (self, server.StopTraversal)

    def renderHTTP(self, request):
        station_id = request.args['id'][0]
        filename = 'playlist_%s.pls' % station_id
        fname = os.path.join(TESTS_DIR, 'data', filename)
        newxml_phtml = open(fname).read()
        response = Response(responsecode.OK, stream=newxml_phtml)
        response.headers.setHeader('Connection', ['close'])
        return response

class GenresResource(object):
    implements(iweb.IResource)

    def locateChild(self, request, segments):
        return (self, server.StopTraversal)

    def renderHTTP(self, request):
        genre = request.args.get('genre', [])
        if genre:
            filename = "%s_genre.xml" % genre[0]
        else:
            filename = 'all_genres.xml'
            if 'search' in request.args:
                filename = 'search_%s.xml' % request.args['search'][0].lower()

        fname = os.path.join(TESTS_DIR, 'data', filename)
        newxml_phtml = open(fname).read()
        response = Response(responsecode.OK, stream=newxml_phtml)
        response.headers.setHeader('Connection', ['close'])
        return response

class SbinResource(object):
    implements(iweb.IResource)

    def __init__(self):
        self.my_children = {'': self,
                            'newxml.phtml': GenresResource(),
                            'tunein-station.pls': PlaylistResource()
                            }

    def locateChild(self, request, segments):
        res = self.my_children.get(segments[0])
        if res == self:
            return (self, server.StopTraversal)
        elif res == None:
            return (None, segments)
        else:
            return (res, segments[1:])


class TestShoutcastServerRootResource(object):
    implements(iweb.IResource)

    def __init__(self):
        self.my_children = {'': self,
                            'sbin': SbinResource(),
                            }

    def locateChild(self, request, segments):
        res = self.my_children.get(segments[0])
        if res == self:
            return (self, server.StopTraversal)
        elif res == None:
            return (None, segments)
        else:
            return (res, segments[1:])


    def renderHTTP(self, request):
        response = StatusResponse(responsecode.OK,
                                  'Server\'s root resource: /',
                                  'Elisa test HTTP Server BETA')
        return response


class TestShoutcastServerRequest(server.Request):
    site = server.Site(TestShoutcastServerRootResource())

class ShoutcastTestMixin(object):
    def setUp(self):
        def set_provider(provider):
            self.provider = provider
            return provider

        config = {}
        dfr = ShoutcastResource.create(config)
        dfr.addCallback(set_provider)
        return dfr

    def tearDown(self):
        return self.provider.clean()

    def test_unsupported_uri(self):
        model, dfr = self.provider.get(MediaUri('shoutcast://idontexist'))
        self.failUnlessFailure(dfr, NotImplementedError)
        return dfr

    def test_get_genres(self):

        def got_genres(genres_model):
            self.failUnless(genres_model.genres)
            names = [g.name for g in genres_model.genres]
            expected_names = self.provider.config['genres']
            self.assertEquals(len(names), len(expected_names))
            for name in names:
                self.failIf(name not in expected_names)

        genres_uri = MediaUri('shoutcast://genres')
        model, dfr = self.provider.get(genres_uri)
        dfr.addCallback(got_genres)
        return dfr

    def test_get_all_genres(self):

        def got_genres(genres_model):
            self.failUnless(genres_model.genres)
            return genres_model

        genres_uri = MediaUri('shoutcast://all_genres')
        model, dfr = self.provider.get(genres_uri)
        dfr.addCallback(got_genres)
        return dfr

    def test_get_all_then_one(self):

        def got_first_genre(model):
            self.failUnless(len(model.stations))

        def got_genres(genres_model):
            model, dfr = self.provider.get(genres_model.genres[0].uri)
            dfr.addCallback(got_first_genre)
            return dfr

        dfr = self.test_get_all_genres()
        dfr.addCallback(got_genres)
        return dfr

    def test_search(self):

        def got_search_results(results):
            self.failUnless(results.stations)

        model, dfr = self.provider.get(MediaUri('shoutcast://search/prince'))
        dfr.addCallback(got_search_results)
        return dfr

    def test_get_decades(self):

        def got_decades(model):
            names = [g.name for g in model.genres]
            expected_names = ['50s', '60s', '70s', '80s', '90s']
            self.assertEquals(len(names), len(expected_names))
            for name in names:
                self.failIf(name not in expected_names)

        model, dfr = self.provider.get(MediaUri('shoutcast://decades'))
        dfr.addCallback(got_decades)
        return dfr

    def test_get_one_genre(self):

        def got_stations(model):
            self.failUnless(len(model.stations) != 0)
            first = model.stations[0]
            self.failUnless(first.name)
            self.failUnless(first.genre)
            self.failUnless(first.bitrate)
            self.failUnless(hasattr(first, 'listeners_count'))
            self.failUnless(first.currently_playing)
            self.failUnless(first.get_playable)

        model, dfr = self.provider.get(MediaUri('shoutcast://genre/Alternative'))
        dfr.addCallback(got_stations)
        return dfr

    def test_get_station(self):
        def got_station(model):
            self.failUnless(isinstance(model, PlayableModel))
            self.failUnless(model.uri)
            uri = MediaUri(model.uri)
            self.failIf(uri.scheme == "shoutcast")
            self.failUnless(model.title)
            
        uri = MediaUri('shoutcast://station/sbin/tunein-station.pls?station_id=1025')
        model, dfr = self.provider.get(uri)
        dfr.addCallback(got_station)
        return dfr

    def test_popular_stations(self):
        def got_stations(model):
            self.failUnless(len(model.stations) != 0)

        model, dfr = self.provider.get(MediaUri('shoutcast://popular_stations'))
        dfr.addCallback(got_stations)
        return dfr


class TestShoutcastResourceProviderFunctional(ShoutcastTestMixin, TestCase):

    def setUp(self):
        run_functional_tests_check()
        return ShoutcastTestMixin.setUp(self)


class TestFakeShoutcastResourceProvider(ShoutcastTestMixin, TestCase):
    
    def setUpClass(self):
        ShoutcastResource.base_url = 'localhost:1234'
        self.server_factory = HTTPFactory(TestShoutcastServerRequest)
        self.port = reactor.listenTCP(1234, self.server_factory)

    def test_get_all_then_one(self):
        raise SkipTest("Not supported by our test fake shoutcast server")

    def tearDownClass(self):
        ShoutcastResource.base_url = 'www.shoutcast.com'
        return defer.maybeDeferred(self.port.stopListening)

    def test_get_countries(self):

        def got_countries(model):
            names = [g.name for g in model.genres]
            expected_names = ['Albanian', 'Armenian', 'Brazilian', 'Croatian',
                              'Greece', 'Haitian', 'Indian', 'Indonesia',
                              'Japanese', 'Malaysia', 'Nigerian', 'Romania',
                              'Romanian', 'Spain']
            self.assertEquals(len(names), len(expected_names))
            for name in names:
                self.failIf(name not in expected_names)

        model, dfr = self.provider.get(MediaUri('shoutcast://stations_by_country'))
        dfr.addCallback(got_countries)
        return dfr


    def test_search(self):

        def got_search_results(results):
            self.failUnless(results.stations)
        
        model, dfr = self.provider.get(MediaUri('shoutcast://search/prince'))
        dfr.addCallback(got_search_results)
        return dfr
