# -*- coding: utf-8 -*-
# Moovida - Home multimedia server
# Copyright (C) 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.
#
# Author: Olivier Tilloy <olivier@fluendo.com>


from elisa.core import default_config
from elisa.core.utils import caching, defer
from elisa.core.utils.locale_helper import filesystem_encoding
from elisa.core.media_uri import MediaUri

from twisted.trial.unittest import TestCase

import os.path, shutil


class ObjectToSerialize(object):
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        return self.value == other.value


class TestCaching(TestCase):

    _distant_data = 'some data to cache'

    def _get(self, uri):
        # Mock to replace a call to the real page getter
        return defer.succeed(self._distant_data)

    def setUpClass(self):
        self.cache_dir = self.mktemp()
        os.makedirs(self.cache_dir)
        # Monkey patch caching._get
        self._caching_get = caching._get
        caching._get = self._get

    def tearDownClass(self):
        shutil.rmtree(self.cache_dir)
        # Unpatch
        caching._get = self._caching_get

    def setUp(self):
        self._config_dir_orig = default_config.CONFIG_DIR

    def tearDown(self):
        default_config.CONFIG_DIR = self._config_dir_orig

    def test_get_cache_path_exists(self):
        default_config.CONFIG_DIR = os.path.abspath(self.mktemp())
        os.mkdir(default_config.CONFIG_DIR)
        cache_dir = u'test_cache_dir'
        cache_path = os.path.join(default_config.CONFIG_DIR, cache_dir)
        os.mkdir(cache_path)
        result = caching.get_cache_path(cache_dir)
        self.failUnlessEqual(result, cache_path)

    def test_get_cache_path_exists_not(self):
        default_config.CONFIG_DIR = os.path.abspath(self.mktemp())
        cache_dir = u'test_cache_dir'
        cache_path = os.path.join(default_config.CONFIG_DIR, cache_dir)
        self.failIf(os.path.exists(cache_path))
        result = caching.get_cache_path(cache_dir)
        self.failUnlessEqual(result, cache_path)
        self.failUnless(os.path.exists(cache_path))

    def test_get_pictures_cache_path(self):
        default_config.CONFIG_DIR = os.path.abspath(self.mktemp())
        os.mkdir(default_config.CONFIG_DIR)
        cache_path = os.path.join(default_config.CONFIG_DIR, u'pictures_cache')
        os.mkdir(cache_path)
        result = caching.get_pictures_cache_path()
        self.failUnlessEqual(result, cache_path)

    def test_get_cached_file_path(self):
        # Test caching local files
        filepath = __file__.decode(filesystem_encoding())
        uri = MediaUri({'scheme': 'file', 'path': filepath})
        cached_file_path = caching.get_cached_file_path(uri, self.cache_dir)
        self.failUnlessEqual(cached_file_path, filepath)

        # Test caching a resource without extension
        uri = MediaUri('http://example.com/test')
        hashed = '44739ab8292f3d29beb6975ac3207e46'
        cached_file_path = caching.get_cached_file_path(uri, self.cache_dir)
        self.failUnlessEqual(cached_file_path,
                             os.path.join(self.cache_dir, hashed))

        # Test caching a resource without extension, forcing the extension
        uri = MediaUri('http://example.com/test')
        hashed = '44739ab8292f3d29beb6975ac3207e46.png'
        cached_file_path = caching.get_cached_file_path(uri, self.cache_dir,
                                                        extension=u'png')
        self.failUnlessEqual(cached_file_path,
                             os.path.join(self.cache_dir, hashed))

        # Test caching a resource with an extension
        uri = MediaUri('http://example.com/test.html')
        hashed = '0cb489738a493cf24b03574c2f458ed6.html'
        cached_file_path = caching.get_cached_file_path(uri, self.cache_dir)
        self.failUnlessEqual(cached_file_path,
                             os.path.join(self.cache_dir, hashed))

        # Test caching a resource with an extension, forcing a different
        # extension
        uri = MediaUri('http://example.com/test.ogv')
        hashed = '5dfbad84d522d815726faaf122f7e4da.png'
        cached_file_path = caching.get_cached_file_path(uri, self.cache_dir,
                                                        extension=u'png')
        self.failUnlessEqual(cached_file_path,
                             os.path.join(self.cache_dir, hashed))

    def _check_cached_data(self, cached_file_path, expected_data):
        fd = open(cached_file_path, 'rb')
        self.failUnlessEqual(fd.read(), expected_data)
        fd.close()

    def test_cache_to_file(self):
        cached_file_path = os.path.join(self.cache_dir, 'cached')
        data = 'some data'
        result = caching.cache_to_file(data, cached_file_path)
        self.failUnlessEqual(result, cached_file_path)
        self.failUnless(os.path.exists(cached_file_path))
        self._check_cached_data(cached_file_path, data)

        # Test caching to an already existing file
        more_data = 'some more data'
        result = caching.cache_to_file(more_data, cached_file_path)
        self.failUnlessEqual(result, cached_file_path)
        self.failUnless(os.path.exists(cached_file_path))
        self._check_cached_data(cached_file_path, more_data)

    def test_get_and_cache_to_file_already_cached(self):
        # Do not cache an already cached file
        uri = MediaUri('http://example.com/test.jpg')
        cache_file = caching.get_cached_file_path(uri, self.cache_dir)
        data = 'already cached'
        fd = open(cache_file, 'wb')
        fd.write(data)
        fd.close()
        dfr = caching.get_and_cache_to_file(uri, self.cache_dir)
        dfr.addCallback(self._check_cached_data, data)

    def test_get_and_cache_to_file_new(self):
        uri = MediaUri('http://example.com/test.png')
        cache_file = caching.get_cached_file_path(uri, self.cache_dir)
        dfr = caching.get_and_cache_to_file(uri, self.cache_dir)
        dfr.addCallback(self._check_cached_data, self._distant_data)

    def test_serialize_deserialize(self):
        dump_file_path = os.path.join(self.cache_dir, 'serialized')
        obj = ObjectToSerialize('some data')
        caching.serialize(obj, dump_file_path)
        deserialized = caching.deserialize(dump_file_path)
        self.failUnlessEqual(deserialized, obj)

        # Overwrite an already serialized object
        other_obj = ObjectToSerialize('some more data')
        caching.serialize(other_obj, dump_file_path)
        deserialized = caching.deserialize(dump_file_path)
        self.failUnlessEqual(deserialized, other_obj)

        # Try to de-serialize from a inexistent file
        dump_file_path = os.path.join(self.cache_dir, 'inexistent')
        self.failUnlessRaises(IOError, caching.deserialize, dump_file_path)
