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


from twisted.trial.unittest import TestCase, SkipTest

from elisa.plugins.base.local_resource import LocalResource

from elisa.plugins.base.models.file import FileModel, DirectoryModel

from elisa.core.media_uri import MediaUri
from elisa.core.utils.defer import Deferred

import os, re, sys
import locale

class TestLocalMixin(object):

    filename = "base_local_resource"
    directories = ()
    files = ()

    def setUp(self):
        """
        set up the simple file structure
        """

        def set_resource(resource):
            self.resource = resource

        for dir in self.directories:
            real_dir = os.path.join(self.filename, *dir)
            
            if os.path.exists(real_dir):
                continue
            os.makedirs(real_dir)

        for file in self.files:

            real_file = os.path.join(self.filename, *file)

            if os.path.exists(real_file):
                continue

            r = os.open(real_file, os.O_CREAT)
            os.close(r)

        return LocalResource.create({}).addCallback(set_resource)


class TestLocalResource(TestLocalMixin, TestCase):

    directories = (('hidden',), ('test', 'folder_path'),
                    ('first_dir',), ('second_dir',),
                    ('broken_link',), )

 
    files = (('first',), ('second',), ('third',), ('fourth',),
                 ('test', 'file_wired_name'),
                 ('broken_link', 'test'),
                )

    def test_supported_uris_compile_and_work(self):
        """
        test whether the supported uris compiles and works
        """
        
        uris = ( 'file://./test/help',
                 'file:///somwhere/something')
                # ADD MORE

        compiled = re.compile(LocalResource.supported_uri)

        for uri in uris:
            self.assertTrue(compiled.match(uri) is not None)

    def test_wrong_encoding(self):

        def fake_system_encoding():
            return (None, 'cp1252')

        old_getdefaultlocale = locale.getdefaultlocale
        locale.getdefaultlocale = fake_system_encoding

        filename = 'Libert\xc3\xa9, \xc3\xa9galit\xc3\xa9, banane.mov'
        filename = filename.decode('cp1252')
        dirname = 'ibreakfilenameencodings'
        if not os.path.exists(dirname):
            os.makedirs(dirname)
        full_path = os.path.join(dirname, filename)

        open(full_path, 'w').close()

        uri = MediaUri('file://./%s/' % dirname)

        def got_dir_listing(model):
            self.assertEquals(filename, model.files[0].name)
            locale.getdefaultlocale = old_getdefaultlocale

        m, dfr = self.resource.get(uri, None)
        dfr.addCallback(got_dir_listing)
        return dfr

    def test_wrong_filename_encoding(self):
        """
        fake os.listdir results, make it return a unicode filename
        """
        filename = u'De Andr\xe9, Fabrizio'

        def fake_listdir(directory_path):
            return [filename,]

        self.resource._listdir = fake_listdir

        dirname = 'ibreakfilenameencodings'
        if not os.path.exists(dirname):
            os.makedirs(dirname)

        uri = MediaUri('file://./%s/' % dirname)

        def got_dir_listing(model):
            self.assertEquals(model.files, [])

        m, dfr = self.resource.get(uri, None)
        dfr.addCallback(got_dir_listing)
        return dfr


    def test_uri_in_model(self):
        """
        test whether the uri is in the model we get
        """
        dfrs = []

        # directory
        uri = MediaUri("file://./%s/" % self.filename)
        model, dfr = self.resource.get(uri)

        def check_in_result(result, uri):
            self.assertTrue(result.uri == uri)

        dfr.addCallback(check_in_result, uri)
        dfrs.append(dfr)

        # file
        uri = MediaUri("file://./%s/first" % self.filename)
        model, dfr = self.resource.get(uri)
        dfr.addCallback(check_in_result, uri)

        dfrs.append(dfr)

        # context model does not get reset
        context_model = FileModel()
        context_model.uri = "nope"
        model, dfr = self.resource.get(uri, context_model)

        dfr.addCallback(check_in_result, 'nope')

        dfrs.append(dfr)

        return dfrs

    def test_simple_directory_reading(self):
        """
        test whether we get the desired data in a simple reading
        """
        uri = MediaUri('file://./%s' % self.filename)
        model, dfr = self.resource.get(uri)

        def check_simple(result):
            self.assertTrue(model is result)
            self.assertTrue(isinstance(result, DirectoryModel))
            self.assertEquals(len(result.files), 0)

        dfr.addCallback(check_simple)
        return dfr

    def test_simple_directory_listing(self):
        """
        test whether reading the files works in the simple case
        """
        uri = MediaUri('file://./%s/' % self.filename)
        model, dfr = self.resource.get(uri, None)
        
        def check_result(result):
            self.assertTrue(model is result)

            self.assertTrue(isinstance(result, DirectoryModel))
            self.assertEquals(len(result.files), 9)

            self.assertFalse(result.atime == 0)
            self.assertFalse(result.mtime == 0)
            self.assertFalse(result.size == -1)

        dfr.addCallback(check_result)
        return dfr


    def test_sub_dir(self):
        """
        test whether the subdirectory gets returned as a directory model
        """
        uri = MediaUri('file://./%s/' % self.filename)

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

        def check_result(result):
            self.assertTrue(result is model)

            names = [file.name for file in result.files]

            hidden_idx = names.index('hidden')
            qmark_idx = names.index('test')

            self.assertTrue(isinstance(result.files[hidden_idx],
                    DirectoryModel))

            self.assertTrue(isinstance(result.files[qmark_idx],
                    DirectoryModel))

        dfr.addCallback(check_result)
        return dfr


    def test_false_dir(self):
        """
        test whether reading an not existing directory fails as expected
        """
        uri = MediaUri('file://./%s/false' % self.filename)
        model, dfr = self.resource.get(uri)
        self.failUnlessFailure(dfr, IOError)
        return dfr

    def test_listing_of_nonexisting_dir(self):
        """
        test whether listing of non existing directory fails as expected
        """
        context_model = DirectoryModel()
        uri = MediaUri('file://./%s/false' % self.filename)
        model, dfr = self.resource.get(uri, context_model)
        self.failUnlessFailure(dfr, IOError)
        return dfr

    def test_listing_of_file(self):
        """
        test whether listing of non file fails as expected
        """
        uri = MediaUri('file://./%s/first/' % self.filename)
        model, dfr = self.resource.get(uri, None)
        self.failUnlessFailure(dfr, IOError)
        return dfr

    def test_file_reading(self):
        """
        test whether we can read the file as expected
        """
        context_model = FileModel()

        # prepare the file
        rd = open(os.path.join(self.filename, 'first'), 'w')
        rd.write('tee')
        rd.close()


        uri = MediaUri('file://./%s/first' % self.filename)
        model, dfr = self.resource.get(uri, context_model)

        def test_result(result):
            self.assertTrue(result is model)
            self.assertTrue(result is context_model)

            self.assertEquals(result.size, 3)
            self.assertFalse(result.mtime == 0)
            self.assertFalse(result.atime == 0)

        dfr.addCallback(test_result)
        return dfr

    def test_modified_time(self):
        """
        test whether posting the modification time works
        """
        context_model = FileModel()
        uri = MediaUri('file://./%s/second' % self.filename)
        model, dfr = self.resource.get(uri, context_model)

        def check_time(result, old_time, new_time):
            self.assertFalse(result.mtime == old_time)
            self.assertEquals(result.mtime, new_time)

        def request_time(result, uri, old_time, new_time):
            model, dfr = self.resource.get(uri, FileModel())
            dfr.addCallback(check_time, old_time, new_time)
            return dfr

        def change_time(result, uri):
            old_time = result.mtime
            new_time = 123456
            dfr = self.resource.post(uri, time=new_time)
            dfr.addCallback(request_time, uri, old_time, new_time)
            return dfr
            

        dfr.addCallback(change_time, uri)
        return dfr

    def test_delete_file(self):
        """
        test whether deleting works
        """
        uri = MediaUri('file://./%s/third' % self.filename)
        dfr = self.resource.delete(uri)


        def retry_to_read(result):
            model, dfr = self.resource.get(uri)
            self.failUnlessFailure(dfr, IOError)

        dfr.addCallback(retry_to_read)
        return dfr

    def test_delete_dir(self):
        """
        test whether deleting works
        """
        uri = MediaUri('file://./%s/first_dir' % self.filename)
        dfr = self.resource.delete(uri)


        def retry_to_read(result):
            model, dfr = self.resource.get(uri)
            self.failUnlessFailure(dfr, IOError)

        dfr.addCallback(retry_to_read)
        return dfr

class TestLinuxOnly(TestLocalMixin, TestCase):

    filename = "base_local_resource_broken_links"
    directories = (('hidden',), ('qmark', 'folder?path'),
                    ('first_dir',), ('second_dir',),
                    ('broken_link',), )

 
    files = (('first',), ('second',), ('third',), ('fourth',),
                 ('qmark', 'file?wired_name'),
                 ('qmark', 'folder?path','file_with?mark_in_it'),
                 ('broken_link', 'test'),
                )

    def setUp(self):
        if sys.platform != 'linux2':
            raise SkipTest("This Test is only for Linux")
        dfr = super(TestLinuxOnly, self).setUp()
        # set up our broken link
        broken_link = os.path.join(self.filename, 'broken_link', 'broken')

        if not os.path.islink(broken_link):
            os.symlink('nope', broken_link)
        return dfr

    def test_broken_symlink_in_listing(self):
        """
        test that listing a directory with broken symlinks does work
        """

        uri = MediaUri('file://./%s/broken_link/' % self.filename)
        model, dfr = self.resource.get(uri, None)
        
        def check_result(result):
            self.assertTrue(model is result)

            self.assertTrue(isinstance(result, DirectoryModel))
            # we need to have one file. filtering out broken symbolic links
            # makes sense.
            self.assertEquals(len(result.files), 1)
            self.assertEquals(result.files[0].name, 'test')

        dfr.addCallback(check_result)
        return dfr

    def test_sub_dir(self):
        """
        test whether the subdirectory gets returned as a directory model
        """
        uri = MediaUri('file://./%s/' % self.filename)

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

        def check_result(result):
            self.assertTrue(result is model)

            names = [file.name for file in result.files]

            hidden_idx = names.index('hidden')
            qmark_idx = names.index('qmark')

            self.assertTrue(isinstance(result.files[hidden_idx],
                    DirectoryModel))

            self.assertTrue(isinstance(result.files[qmark_idx],
                    DirectoryModel))

        dfr.addCallback(check_result)
        return dfr

