from string import lowercase

from twisted.python.util import sibpath
from twisted.trial import unittest

from epsilon import extime
from epsilon.extime import Time

from axiom import store
from axiom.store import Store
from axiom.dependency import installOn

from xmantissa.people import (Organizer, Person, RealName, EmailAddress,
                              AddPersonFragment, Mugshot)
from xmantissa.webapp import PrivateApplication

class PeopleModelTestCase(unittest.TestCase):
    """
    Tests for the model parts of the person organizer code.
    """
    def setUp(self):
        """
        Create a bunch of people with names beginning with various letters.
        """
        self.store = Store()
        self.organizer = Organizer(store=self.store)

        letters = lowercase.decode('ascii')
        for firstPrefix, lastPrefix in zip(letters, reversed(letters)):
            first = firstPrefix + u'Alice'
            last = lastPrefix + u'Jones'
            person = Person(
                store=self.store,
                organizer=self.organizer,
                created=Time(),
                name=first + u' ' + last)
            RealName(
                store=self.store,
                person=person,
                first=first,
                last=last)

    def test_nameRestriction(self):
        """
        Test the query which loads Person items with last names in a particular
        alphabetic range.
        """
        for case in (unicode.upper, unicode.lower):
            people = list(self.store.query(Person, self.organizer.lastNamesBetweenComparison(case(u'a'), case(u'b'))))
            self.assertEqual(len(people), 1)
            self.assertEqual(people[0].name, u'zAlice aJones')


    def test_nameSorting(self):
        """
        Test the query which loads Person items orders them alphabetically by
        name.
        """
        people = list(self.store.query(
            Person,
            self.organizer.lastNamesBetweenComparison(u'm', u'p'),
            sort=self.organizer.lastNameOrder().ascending))
        self.assertEqual(len(people), 3)
        self.assertEqual(people[0].name, u'nAlice mJones')
        self.assertEqual(people[1].name, u'mAlice nJones')
        self.assertEqual(people[2].name, u'lAlice oJones')



class PeopleTests(unittest.TestCase):
    def testPersonCreation(self):
        s = store.Store()
        o = Organizer(store=s)

        beforeCreation = extime.Time()
        p = o.personByName(u'testuser')
        afterCreation = extime.Time()

        self.assertEquals(p.name, u'testuser')
        self.failUnless(
            beforeCreation <= p.created <= afterCreation,
            "not (%r <= %r <= %r)" % (beforeCreation, p.created, afterCreation))

        # Make sure people from that organizer don't collide with
        # people from a different organizer
        another = Organizer(store=s)
        q = another.personByName(u'testuser')
        self.failIfIdentical(p, q)
        self.assertEquals(q.name, u'testuser')

        # And make sure people within a single Organizer don't trample
        # on each other.
        notQ = another.personByName(u'nottestuser')
        self.failIfIdentical(q, notQ)
        self.assertEquals(q.name, u'testuser')
        self.assertEquals(notQ.name, u'nottestuser')


    def test_getEmailAddresses(self):
        """
        Verify that getEmailAddresses yields the associated email address
        strings for a person.
        """
        s = store.Store()
        p = Person(store=s)
        EmailAddress(store=s, person=p, address=u'a@b.c')
        EmailAddress(store=s, person=p, address=u'c@d.e')
        # Ordering is undefined, so let's use a set.
        self.assertEquals(set(p.getEmailAddresses()),
                          set([u'a@b.c', u'c@d.e']))


    def test_getEmailAddress(self):
        """
        Verify that getEmailAddress yields the only associated email address
        for a person if it is the only one.
        """
        s = store.Store()
        p = Person(store=s)
        EmailAddress(store=s, person=p, address=u'a@b.c')
        self.assertEquals(p.getEmailAddress(), u'a@b.c')

    def testPersonRetrieval(self):
        s = store.Store()
        o = Organizer(store=s)

        name = u'testuser'
        firstPerson = o.personByName(name)
        self.assertIdentical(firstPerson, o.personByName(name))

    def testPersonCreation2(self):
        s = store.Store()
        o = Organizer(store=s)

        class original:
            store = s

        addPersonFrag = AddPersonFragment(original)
        addPersonFrag.addPerson(u'Captain P.', u'Jean-Luc', u'Picard', u'jlp@starship.enterprise')

        person = s.findUnique(Person)
        self.assertEquals(person.name, 'Captain P.')

        email = s.findUnique(EmailAddress, EmailAddress.person == person)

        self.assertEquals(email.address, 'jlp@starship.enterprise')

        rn = s.findUnique(RealName, RealName.person == person)

        self.assertEquals(rn.first + ' ' + rn.last, 'Jean-Luc Picard')


    def test_doublePersonCreation(self):
        """
        Test that L{AddPersonFragment.addPerson} raises a ValueError when it is
        called twice with the same email address. This ensures that
        L{Organizer.personByEmailAddress} can always return a unique person.
        """
        # make ourselves an AddPersonFragment
        s = store.Store()
        o = Organizer(store=s)
        class original(object):
            store = s
        fragment = AddPersonFragment(original)

        address = u'test@example.com'
        fragment.addPerson(u'flast', u'First', u'Last', address)
        self.assertRaises(ValueError, fragment.addPerson, u'foobar', u'Foo',
                          u'Bar', address)


    def testMugshot(self):
        """
        Create a Mugshot item, check that it thumbnails it's image correctly
        """

        try:
            from PIL import Image
        except ImportError:
            raise unittest.SkipTest('PIL is not available')

        s = store.Store(self.mktemp())

        p = Person(store=s, name=u'Bob')

        imgpath = sibpath(__file__, 'resources/square.png')
        imgfile = file(imgpath)

        m = Mugshot.fromFile(p, imgfile, u'png')

        self.assertEqual(m.type, 'image/png')
        self.assertIdentical(m.person,  p)

        self.failUnless(m.body)
        self.failUnless(m.smallerBody)

        img = Image.open(m.body.open())
        self.assertEqual(img.size, (m.size, m.size))

        smallerimg = Image.open(m.smallerBody.open())
        self.assertEqual(smallerimg.size, (m.smallerSize, m.smallerSize))

    def testLinkToPerson(self):
        s = store.Store()

        privapp = PrivateApplication(store=s)
        installOn(privapp, s)

        o = Organizer(store=s)
        installOn(o, s)

        p = Person(store=s)

        self.assertEqual(o.linkToPerson(p),
                         (privapp.linkTo(o.storeID)
                             + '/'
                             + privapp.toWebID(p)))


syntax highlighted by Code2HTML, v. 0.9.1