import os
import sys
import tempfile
import shutil
import unittest
from StringIO import StringIO
from hurry.file.interfaces import IFileRetrieval
from zope.component.hooks import setSite
from zope.interface.verify import verifyClass, verifyObject
from waeup.sirp.app import University
from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
from waeup.sirp.image import createWAeUPImageFile
from waeup.sirp.imagestorage import (
    md5digest, Basket, ImageStorage, ImageStorageFileRetrieval,)

class HelperFuncsTests(unittest.TestCase):

    def setUp(self):
        self.workdir = tempfile.mkdtemp()

    def tearDown(self):
        shutil.rmtree(self.workdir)

    def test_md5digest(self):
        samplefile = os.path.join(self.workdir, 'sample')
        open(samplefile, 'wb').write('blah')
        fp = open(samplefile, 'r')
        digest = md5digest(fp)
        self.assertEqual(digest, '6f1ed002ab5595859014ebf0951522d9')

class BasketTests(FunctionalTestCase):

    layer = FunctionalLayer

    def setUp(self):
        super(BasketTests, self).setUp()
        self.workdir = tempfile.mkdtemp()
        self.samplefile = os.path.join(self.workdir, 'sample')
        self.otherfile = os.path.join(self.workdir, 'other')
        open(self.samplefile, 'wb').write('Hi there!')
        open(self.otherfile, 'wb').write('Hi from other!')
        self.basket = Basket()
        self.fd = open(self.samplefile, 'r')
        self.fd2 = open(self.otherfile, 'r')
        self.stderr = StringIO()
        self.old_stderr = sys.stderr

    def tearDown(self):
        sys.stderr = self.old_stderr
        super(BasketTests, self).tearDown()
        self.fd.close()
        shutil.rmtree(self.workdir)
        self.basket._del() # Remove subojects explicitly
        del self.basket
        return

    def test_ifaces(self):
        pass

    def test_curr_id_empty(self):
        curr_id = self.basket.curr_id
        self.assertEqual(curr_id, '1')

    def test_getInternalId_empty(self):
        basket_id = self.basket.getInternalId(self.fd)
        self.assertTrue(basket_id is None)

    def test_storeFile_single(self):
        basket_id = self.basket.storeFile(self.fd, 'sample')
        self.assertEqual(basket_id, '1')
        contents = self.basket['1'].open('r').read()
        self.assertEqual(contents, 'Hi there!')

    def test_storeFile_double(self):
        basket_id1 = self.basket.storeFile(self.fd, 'sample')
        basket_id2 = self.basket.storeFile(self.fd, 'sample')
        self.assertTrue(basket_id1 == basket_id2 == '1')
        contents = self.basket['1'].open('r').read()
        self.assertEqual(contents, 'Hi there!')

    def test_storeFile_multiple(self):
        basket_id1 = self.basket.storeFile(self.fd, 'sample')
        basket_id2 = self.basket.storeFile(self.fd2, 'sample')
        self.assertEqual(basket_id1, '1')
        self.assertEqual(basket_id2, '2')
        contents1 = self.basket['1'].open('r').read()
        contents2 = self.basket['2'].open('r').read()
        self.assertEqual(contents1, 'Hi there!')
        self.assertEqual(contents2, 'Hi from other!')

    def test_retrieveFile(self):
        basket_id = self.basket.storeFile(self.fd, 'sample')
        fd = self.basket.retrieveFile(basket_id)
        result = fd.read()
        self.assertEqual(result, 'Hi there!')

    def test_retrieveFile_not_existent(self):
        result = self.basket.retrieveFile('not-a-valid-basket-id')
        self.assertTrue(result is None)

    def test_detect_zero_length_blobs(self):
        # Ensure we get a warning when an empty Blob is found
        self.basket.storeFile(self.fd, 'sample')
        self.basket['1'].open('w').write('')
        self.fd.seek(0)
        sys.stderr = self.stderr         # Redirect stderr
        self.basket.storeFile(self.fd, 'sample')
        sys.stderr = self.old_stderr     # Restore stderr
        self.stderr.seek(0)
        self.assertTrue(
            "EMPTY BLOB DETECTED" in self.stderr.read())

    def test_refill_zero_length_blobs(self):
        # When we detect an empty Blob, it will be reused
        self.basket.storeFile(self.fd, 'sample')
        self.basket['1'].open('w').write('')
        self.fd.seek(0)
        sys.stderr = self.stderr         # Redirect stderr
        self.basket.storeFile(self.fd, 'sample')
        sys.stderr = self.old_stderr     # Restore stderr
        contents = self.basket['1'].open('r').read()
        self.assertEqual(contents, 'Hi there!')


class ImageStorageTests(FunctionalTestCase):

    layer = FunctionalLayer

    def setUp(self):
        super(ImageStorageTests, self).setUp()
        self.workdir = tempfile.mkdtemp()
        self.samplefile = os.path.join(self.workdir, 'sample')
        self.otherfile = os.path.join(self.workdir, 'other')
        open(self.samplefile, 'wb').write('Hi there!')
        open(self.otherfile, 'wb').write('Hi from other!')
        self.storage = ImageStorage()
        self.fd = open(self.samplefile, 'r')
        self.fd2 = open(self.otherfile, 'r')

    def tearDown(self):
        super(ImageStorageTests, self).tearDown()
        self.fd.close()
        self.fd2.close()
        shutil.rmtree(self.workdir)
        self.storage._del() # Remove subojects explicitly
        del self.storage
        return

    def test_ifaces(self):
        pass

    def test_storeFile(self):
        full_id = self.storage.storeFile(self.fd, 'sample.txt')
        self.assertEqual(full_id, '396199333edbf40ad43e62a1c1397793-1')

    def test_storeFile_duplicate(self):
        full_id1 = self.storage.storeFile(self.fd, 'sample1.txt')
        full_id2 = self.storage.storeFile(self.fd, 'sample2.txt')
        full_id3 = self.storage.storeFile(self.fd, 'sample1.txt')
        self.assertEqual(full_id1, '396199333edbf40ad43e62a1c1397793-1')
        self.assertEqual(full_id2, '396199333edbf40ad43e62a1c1397793-1')
        self.assertEqual(full_id3, '396199333edbf40ad43e62a1c1397793-1')
        contents = self.storage.retrieveFile(
            '396199333edbf40ad43e62a1c1397793-1').read()
        self.assertEqual(contents, 'Hi there!')

    def test_storeFile_multiple(self):
        full_id1 = self.storage.storeFile(self.fd, 'sample1.txt')
        full_id2 = self.storage.storeFile(self.fd2, 'sample1.txt')
        self.assertEqual(full_id1, '396199333edbf40ad43e62a1c1397793-1')
        self.assertEqual(full_id2, '6936fcf8d564f1c5be5a017e650c5e8f-1')

    def test_retrieveFile_not_existent(self):
        result = self.storage.retrieveFile('not-existent')
        self.assertTrue(result is None)

    def test_retrieveFile_illegal_marker(self):
        result1 = self.storage.retrieveFile('really-not-existent')
        result2 = self.storage.retrieveFile('notexistent')
        self.assertTrue(result1 is result2 is None)

    def test_retrieveFile(self):
        full_id = self.storage.storeFile(self.fd, 'sample.txt')
        result = self.storage.retrieveFile(full_id)
        content = result.read()
        self.assertEqual(content, 'Hi there!')

    def test_retrieveFile_multiple(self):
        full_id1 = self.storage.storeFile(self.fd, 'sample.txt')
        full_id2 = self.storage.storeFile(self.fd2, 'other.txt')
        result1 = self.storage.retrieveFile(full_id1)
        result2 = self.storage.retrieveFile(full_id2)
        content1 = result1.read()
        content2 = result2.read()
        self.assertEqual(content1, 'Hi there!')
        self.assertEqual(content2, 'Hi from other!')

class ImageStorageFileRetrievalTests(FunctionalTestCase):

    layer = FunctionalLayer

    def setUp(self):
        super(ImageStorageFileRetrievalTests, self).setUp()
        self.workdir = tempfile.mkdtemp()
        self.samplefile = os.path.join(self.workdir, 'sample')
        self.otherfile = os.path.join(self.workdir, 'other')
        open(self.samplefile, 'wb').write('Hi there!')
        open(self.otherfile, 'wb').write('Hi from other!')
        self.storage = ImageStorage()
        self.fd = open(self.samplefile, 'r')
        self.fd2 = open(self.otherfile, 'r')
        # Set up a single image storage in a site
        self.getRootFolder()['app'] = University()
        self.app = self.getRootFolder()['app']
        if not 'images' in self.app.keys():
            self.app['images'] = ImageStorage()
        self.storage = self.app['images']
        return


    def tearDown(self):
        super(ImageStorageFileRetrievalTests, self).tearDown()
        self.fd.close()
        self.fd2.close()
        shutil.rmtree(self.workdir)
        self.storage._del() # Remove subojects explicitly
        return

    def test_ifaces(self):
        retrieval = ImageStorageFileRetrieval()
        assert verifyClass(IFileRetrieval, ImageStorageFileRetrieval)
        assert verifyObject(IFileRetrieval, retrieval)
        return

    def test_getImageStorage_nosite(self):
        retrieval = ImageStorageFileRetrieval()
        storage = retrieval.getImageStorage()
        self.assertTrue(storage is None)
        return

    def test_getImageStorage(self):
        setSite(self.app)
        retrieval = ImageStorageFileRetrieval()
        storage = retrieval.getImageStorage()
        self.assertTrue(storage is self.storage)
        return

    def test_isImageStorageEnabled_nosite(self):
        retrieval = ImageStorageFileRetrieval()
        self.assertTrue(retrieval.isImageStorageEnabled() is False)
        return

    def test_isImageStorageEnabled(self):
        setSite(self.app)
        retrieval = ImageStorageFileRetrieval()
        self.assertTrue(retrieval.isImageStorageEnabled() is True)
        return

    def test_getFile_nosite(self):
        retrieval = ImageStorageFileRetrieval()
        f = retrieval.getFile('Hi there!')
        self.assertEqual(f.read(), 'Hi there!')
        return

    def test_getFile(self):
        setSite(self.app)
        retrieval = ImageStorageFileRetrieval()
        waeup_image = retrieval.createFile('sample.txt', self.fd)
        full_id = waeup_image.data
        result = retrieval.getFile(full_id)
        self.assertEqual(result.read(), 'Hi there!')
        return

    def test_createFile_nosite(self):
        retrieval = ImageStorageFileRetrieval()
        waeup_image = retrieval.createFile('sample.txt', self.fd)
        self.assertEqual(waeup_image.data, 'Hi there!')
        return

    def test_createFile(self):
        # Ensure we can create WAeUPImageFiles when in site
        setSite(self.app)
        retrieval = ImageStorageFileRetrieval()
        waeup_image = retrieval.createFile('sample.txt', self.fd)
        full_id = waeup_image.data
        self.assertEqual(full_id, '396199333edbf40ad43e62a1c1397793-1')
        return

    def test_waeupimagefile(self):
        # Make sure WAeUPImageFile can use our file retrieval
        setSite(self.app)
        myfile = createWAeUPImageFile('sample.jpg', self.fd)
        contents = myfile.file.read()
        self.assertEqual(contents, 'Hi there!')

    def test_waeupimagefile_raw(self):
        # Make sure we can retrieve a file also if it was initialized
        # with no image storage available
        myfile = createWAeUPImageFile('sample.jpg', self.fd)
        setSite(self.app)
        contents = myfile.file.read()
        self.assertEqual(contents, 'Hi there!')
