# Elisa - Home multimedia server
# Copyright (C) 2006-2008 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 Elisa with Fluendo's plugins.
#
# The GPL part of Elisa is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Elisa" in the root directory of this distribution package
# for details on that license.

import copy

from elisa.core.tests.elisa_test_case import ElisaTestCase
from elisa.core.observers.list import ListObservable, ListObserver
from elisa.core.observers.observable import Observable


class MyListObserver(ListObserver):

    def __init__(self):
        ListObserver.__init__(self)
        self.inserted_called = False
        self.removed_called = False
        self.modified_called = False
        self.dirtied_called = False
        self.element_set = None

    def inserted(self, elements, position):
        self.inserted_called = (elements, position)

    def removed(self, elements, position):
        self.removed_called = (elements, position)

    def modified(self, key, value):
        self.modified_called = (key, value)

    def dirtied(self):
        self.dirtied_called = True

    def element_attribute_set(self, position, key, old_value, new_value):
        self.element_set = (position, key, old_value, new_value)

class MyNotImplementedListObserver(ListObserver):
    pass

class TestListObservers(ElisaTestCase):

    def test_list_inserted(self):
        l = ListObservable()
        observer = MyListObserver()
        l.add_observer(observer)

        # append
        observer.inserted_called = False
        l.append("test")
        self.failUnlessEqual(observer.inserted_called, (["test"], 0))

        # extend
        observer.inserted_called = False
        l.extend(["toto"])
        self.failUnlessEqual(observer.inserted_called, (["toto"], 1))

        # insert
        observer.inserted_called = False
        l.insert(1, "titi")
        self.failUnlessEqual(observer.inserted_called, (["titi"], 1))

        # __iadd__
        observer.inserted_called = False
        l += ["tata"]
        self.failUnlessEqual(observer.inserted_called, (["tata"], 3))
       
        # __imul__
        # SKIPPED: __imul__ deactivated; requires investigation
        """
        observer.inserted_called = False
        elements = l[:]
        l *= 2
        self.failUnlessEqual(observer.inserted_called, (elements, 4))
        """

    def test_list_slicing(self):
        l = ListObservable()
        observer = MyListObserver()
        l.add_observer(observer)

        l.extend(["test1", "test2", "test3"])

        # __setslice__
        observer.inserted_called = False
        observer.removed_called = False
        removed = l[1:2]
        added = ["test4", "test5"]
        l[1:2] = added
        self.failUnlessEqual(observer.inserted_called, (added, 1))
        self.failUnlessEqual(observer.removed_called, (removed, 1))

        # __delslice__
        observer.removed_called = False
        removed = l[1:2]
        del l[1:2]
        self.failUnlessEqual(observer.removed_called, (removed, 1))

        # FIXME: make tests for extended slices [1:3:5]

    def test_list_removed(self):
        l = ListObservable()
        observer = MyListObserver()
        l.add_observer(observer)

        l.extend(["test1", "test2", "test3"])

        # pop
        observer.removed_called = False
        element = l.pop()
        self.failUnless(observer.removed_called == ([element], -1) or \
                        observer.removed_called == ([element], 2))

        observer.removed_called = False
        index = -2
        element = l.pop(index)
        self.failUnlessEqual(observer.removed_called, ([element], index))

        observer.removed_called = False
        index = 0
        element = l.pop(index)
        self.failUnlessEqual(observer.removed_called, ([element], index))


        # re-initialize the list
        l[:] = []
        l.extend(["test1", "test2", "test3"])

        # remove
        observer.removed_called = False
        l.remove("test2")
        self.failUnlessEqual(observer.removed_called, (["test2"], 1))

        observer.removed_called = False
        self.failUnlessRaises(ValueError, l.remove, "test2")
        self.failUnlessEqual(observer.removed_called, False)

        # __delitem__
        observer.removed_called = False
        del l[0]
        self.failUnlessEqual(observer.removed_called, (["test1"], 0))
        
    def test_list_modified(self):
        l = ListObservable()
        observer = MyListObserver()
        l.add_observer(observer)

        l.extend(["test1", "test2", "test3"])

        # __setitem__
        l[0] = "test4"
        self.failUnlessEqual(observer.modified_called, (0, "test4"))

    def test_list_dirtied(self):
        l = ListObservable()
        observer = MyListObserver()
        l.add_observer(observer)

        l.extend(["test1", "test2", "test3"])

        # sort
        observer.dirtied_called = False
        l.sort()
        self.failUnless(observer.dirtied_called)
        
        # reverse
        observer.dirtied_called = False
        l.reverse()
        self.failUnless(observer.dirtied_called)

    def test_multiple_observers(self):
        l = ListObservable()
        observers = []
        for i in xrange(15):
            observer = MyListObserver()
            l.add_observer(observer)
            observers.append(observer)

        # append
        for observer in observers:
            observer.inserted_called = False
        l.append("test")
        for observer in observers:
            self.failUnlessEqual(observer.inserted_called, (["test"], 0))

        l.append("test")
        for observer in observers:
            self.failUnlessEqual(observer.inserted_called, (["test"], 1))

        # remove
        observer.removed_called = False
        l.pop(0)
        for observer in observers:
            self.failUnlessEqual(observer.removed_called, (["test"], 0))

        observer.removed_called = False
        l.pop(0)
        for observer in observers:
            self.failUnlessEqual(observer.removed_called, (["test"], 0))

    def test_not_implemented_observer(self):
        l = ListObservable()
        observer = MyNotImplementedListObserver()
        l.add_observer(observer)
        l.extend(["test1", "test2", "test3"])
        # FIXME: add more cases

    def test_children_observing(self):
        l = ListObservable()
        o = MyListObserver()
        l.add_observer(o)

        child = Observable()
        l.append(child)

        child.test = "value"
        self.failUnlessEqual(o.element_set, (0, "test", None, "value"))

        child.test = "ah ah value"
        self.failUnlessEqual(o.element_set, (0, "test", "value", "ah ah value"))

        for i in xrange(10):
            child = Observable()
            l.append(child)

        child = Observable()
        l.append(child)

        child.another = "truc"
        self.failUnlessEqual(o.element_set, (11, "another", None, "truc"))

        child.another = None
        self.failUnlessEqual(o.element_set, (11, "another", "truc", None))

        del child.another
        child.another = "value"
        self.failUnlessEqual(o.element_set, (11, "another", None, "value"))
