# Copyright (c) 2002, 2003 by Intevation GmbH
# Authors:
# Jonathan Coles <jonathan@intevation.de>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with Thuban for details.

"""
Test the Classification class
"""

__version__ = "$Revision: 2688 $"
# $Source$
# $Id: test_classification.py 2688 2006-06-30 12:27:20Z frank $

import unittest

import support
support.initthuban()

import copy

from Thuban.Model.color import Color, Transparent, Black
from Thuban.Model.classification import \
    Classification, ClassGroup, \
    ClassGroupDefault, ClassGroupSingleton, ClassGroupRange,\
    ClassGroupPattern, ClassGroupProperties
from Thuban.Model.messages import CLASS_CHANGED

from Thuban.Model.range import Range



# A few colors for use by the test cases
red = Color(1, 0, 0)
green = Color(0, 1, 0)
blue = Color(0, 0, 1)


class TestClassGroupProperties(unittest.TestCase):

    def test(self):
        """Test ClassGroupProperties"""

        props = ClassGroupProperties()
        self.assertEqual(props.GetLineColor(), Black)
        self.assertEqual(props.GetLineWidth(), 1)
        self.assertEqual(props.GetFill(), Transparent)

        props.SetLineColor(red)
        self.assertEqual(props.GetLineColor(), red)

        props.SetLineColor(blue)
        self.assertEqual(props.GetLineColor(), blue)

        props.SetLineWidth(10)
        self.assertEqual(props.GetLineWidth(), 10)

        self.assertRaises(ValueError, props.SetLineWidth, -10)
        self.assertEqual(props.GetLineWidth(), 10)

        newProps1 = ClassGroupProperties()
        newProps2 = ClassGroupProperties()
        self.assertNotEqual(newProps1, props)
        self.assertEqual(newProps1, newProps2)


class TestClassGroup(unittest.TestCase):

    def test(self):
        """Test ClassGroup"""

        # test constructor with no label
        group = ClassGroup()
        self.assertEqual(group.GetLabel(), "")

        # test constructor with label
        group = ClassGroup("hallo")
        self.assertEqual(group.GetLabel(), "hallo")

        # test SetLabel()/GetLabel()
        group = ClassGroup("welt")
        group.SetLabel("hallo")
        self.assertEqual(group.GetLabel(), "hallo")

        group.SetLabel("")
        self.assertEqual(group.GetLabel(), "")

        # test Matches
        # Matches() is a virtual function...can't test it here
        #
        #self.assertEqual(group.Matches(None), False)
        #self.assertEqual(group.Matches(1), False)
        #self.assertEqual(group.Matches("hallo"), False)
        #self.assertEqual(group.Matches([]), False)

        # test GetProperties...also a virtual function
        #self.assertEqual(group.GetProperties(), None)


class TestClassGroupDefault(unittest.TestCase):

    def test(self):
        """Test ClassGroupDefault"""

        defProps = ClassGroupProperties()

        newProps = ClassGroupProperties()
        newProps.SetLineColor(Color(.25, .5, .75))
        newProps.SetLineWidth(5)
        newProps.SetFill(Color(.12, .24, .36))

        # test constructor

        group = ClassGroupDefault(newProps)
        self.assertEqual(group.GetProperties(), newProps)

        group = ClassGroupDefault(newProps, "hallo")
        self.assertEqual(group.GetProperties(), newProps)
        self.assertEqual(group.GetLabel(), "hallo")

        # test empty constructor
        group = ClassGroupDefault()
        props = group.GetProperties()

        self.assertEqual(group.GetLabel(), "")
        self.assertEqual(defProps, props)

        # test Matches()
        self.assertEqual(group.Matches(None), True)
        self.assertEqual(group.Matches(1), True)
        self.assertEqual(group.Matches("hallo"), True)
        self.assertEqual(group.Matches([]), True)

        # test SetProperties()/GetProperties()
        group.SetProperties(newProps)
        self.assertEqual(group.GetProperties(), newProps)

        # test copy
        groupCopy = copy.copy(group)
        self.assertEqual(group, groupCopy)


class TestClassGroupRange(unittest.TestCase):

    def test(self):
        """Test ClassGroupRange"""

        defProps = ClassGroupProperties()
        newProps = ClassGroupProperties()
        newProps.SetLineColor(Color(.25, .5, .75))
        newProps.SetLineWidth(5)
        newProps.SetFill(Color(.12, .24, .36))

        # test empty constructor
        group = ClassGroupRange()

        self.assertEqual(group.GetMin(), 0)
        self.assertEqual(group.GetMax(), 1)
        self.assertEqual(group.GetProperties(), defProps)
        self.assertEqual(group.GetLabel(), "")

        # test SetMax()
        self.assertRaises(ValueError, group.SetMax, 0)
        self.assertRaises(ValueError, group.SetMax, -1)
        self.assertEquals(group.GetMax(), 1)
        group.SetMax(2)
        self.assertEquals(group.GetMax(), 2)

        # test SetMin()
        self.assertRaises(ValueError, group.SetMin, 2)
        self.assertRaises(ValueError, group.SetMin, 3)
        self.assertEquals(group.GetMin(), 0)
        group.SetMin(-5)
        self.assertEquals(group.GetMin(), -5)

        # test SetProperties()/GetProperties()
        group.SetProperties(newProps)
        self.assertEqual(group.GetProperties(), newProps)

        # test SetRange()
        self.assertRaises(ValueError, group.SetRange, (1, 0))
        group.SetRange(Range("]-oo;6]"))
        self.assertEqual(group.GetRange(), "]-oo;6]")
        group.SetRange((-5, 5))
        self.assertEqual(group.GetRange(), "[-5;5[")

        # test Matches()
        self.assertEqual(group.Matches(-6), False)
        self.assertEqual(group.Matches(-5), True)
        self.assertEqual(group.Matches(0), True)
        self.assertEqual(group.Matches(4), True)
        self.assertEqual(group.Matches(5), False)

        # test copy
        groupCopy = copy.copy(group)
        self.assertEqual(group, groupCopy)


class TestClassGroupSingleton(unittest.TestCase):

    def test(self):
        """Test ClassGroupSingleton"""

        defProps = ClassGroupProperties()
        newProps = ClassGroupProperties()
        newProps.SetLineColor(Color(.25, .5, .75))
        newProps.SetLineWidth(5)
        newProps.SetFill(Color(.12, .24, .36))

        # test empty constructor
        group = ClassGroupSingleton()

        self.assertEqual(group.GetValue(), 0)
        self.assertEqual(group.GetProperties(), defProps)
        self.assertEqual(group.GetLabel(), "")

        # test SetProperties()/GetProperties()
        group.SetProperties(newProps)
        self.assertEqual(group.GetProperties(), newProps)

        # test SetValue()
        group.SetValue(5)
        self.assertEqual(group.GetValue(), 5)

        # test Matches()
        self.assertEqual(group.Matches(0), False)
        self.assertEqual(group.Matches(5), True)

        group.SetValue("5")
        self.assertNotEqual(group.GetValue(), 5)

        # test Matches()
        self.assertEqual(group.Matches(5), False)
        self.assertEqual(group.Matches("5"), True)

        group.SetValue("hallo")
        self.assertEqual(group.GetValue(), "hallo")

        # test Matches()
        self.assertEqual(group.Matches("HALLO"), False)
        self.assertEqual(group.Matches("hallo"), True)

        # test copy
        groupCopy = copy.copy(group)
        self.assertEqual(group, groupCopy)


class TestClassGroupPattern(unittest.TestCase):

    def test(self):
        """Test ClassGroupPattern"""

        defProps = ClassGroupProperties()
        newProps = ClassGroupProperties()
        newProps.SetLineColor(Color(.25, .5, .75))
        newProps.SetLineWidth(5)
        newProps.SetFill(Color(.12, .24, .36))

        # test empty constructor
        group = ClassGroupPattern()

        self.assertEqual(group.GetPattern(), "")
        self.assertEqual(group.GetProperties(), defProps)
        self.assertEqual(group.GetLabel(), "")

        # test SetProperties()/GetProperties()
        group.SetProperties(newProps)
        self.assertEqual(group.GetProperties(), newProps)

        # test SetPattern()
        group.SetPattern("A")
        self.assertEqual(group.GetPattern(), "A")

        # test Matches()
        self.assertEqual(group.Matches("CBA"), False)
        self.assertEqual(group.Matches("ABC"), True)

        group.SetPattern("a")
        self.assertNotEqual(group.GetPattern(), "A")

        # test Matches()
        self.assertEqual(group.Matches("Abc"), False)
        self.assertEqual(group.Matches("aBC"), True)

        group.SetPattern("hallo")
        self.assertEqual(group.GetPattern(), "hallo")

        # test Matches()
        self.assertEqual(group.Matches("HALLO"), False)
        self.assertEqual(group.Matches("hallo"), True)

        # test copy
        groupCopy = copy.copy(group)
        self.assertEqual(group, groupCopy)


class TestClassification(unittest.TestCase, support.SubscriberMixin):

    """Test cases for Classification"""

    def setUp(self):
        self.clazz = Classification()
        self.clazz.Subscribe(CLASS_CHANGED, self.subscribe_with_params,
                             CLASS_CHANGED)
        self.clear_messages()

    def tearDown(self):
        self.clear_messages()
        self.clazz.Destroy()
        del self.clazz

    def test_defaults(self):
        """Test Classification default settings"""
        self.assertEqual(self.clazz.FindGroup(-1),
                         self.clazz.GetDefaultGroup())
        self.assertEqual(self.clazz.GetDefaultLineColor(), Black)
        self.assertEqual(self.clazz.GetDefaultFill(), Transparent)
        self.assertEqual(self.clazz.GetDefaultLineWidth(), 1)

        # The default group is not counted, hence 0 groups
        self.assertEqual(self.clazz.GetNumGroups(), 0)

        # No messages should have been sent so far
        self.check_messages([])

    def test_set_default_properties(self):
        """Test Classification.SetDefaultLineColor and SetDefaultFill"""
        # No messages so far
        self.check_messages([])

        # Change the default line color
        self.clazz.SetDefaultLineColor(red)
        self.assertEqual(self.clazz.GetDefaultLineColor(), red)
        self.assertEqual(self.clazz.GetDefaultFill(), Transparent)
        self.assertEqual(self.clazz.GetDefaultLineWidth(), 1)

        self.check_messages([(CLASS_CHANGED,)])
        self.clear_messages()

        self.clazz.SetDefaultFill(green)
        self.assertEqual(self.clazz.GetDefaultFill(), green)
        self.assertEqual(self.clazz.GetDefaultLineColor(), red)
        self.assertEqual(self.clazz.GetDefaultLineWidth(), 1)
        self.check_messages([(CLASS_CHANGED,)])

        self.check_messages([(CLASS_CHANGED,)])
        self.clear_messages()

        self.clazz.SetDefaultLineWidth(10)
        self.assertEqual(self.clazz.GetDefaultFill(), green)
        self.assertEqual(self.clazz.GetDefaultLineColor(), red)
        self.assertEqual(self.clazz.GetDefaultLineWidth(), 10)
        self.check_messages([(CLASS_CHANGED,)])

    def test_set_default_group(self):
        """Test Classification.SetDefaultGroup()"""
        prop = ClassGroupProperties()
        prop.SetLineColor(blue)
        prop.SetLineWidth(5)
        prop.SetFill(red)

        self.clazz.SetDefaultGroup(ClassGroupDefault(prop))
        self.assertEqual(self.clazz.GetDefaultFill(), red)
        self.assertEqual(self.clazz.GetDefaultLineColor(), blue)
        self.assertEqual(self.clazz.GetDefaultLineWidth(), 5)
        self.check_messages([(CLASS_CHANGED,)])

    def test_add_singleton(self):
        """Test Classification.AppendGroup(ClassGroupSingleton())"""
        self.assertEquals(self.clazz.FindGroup(5),
                          self.clazz.GetDefaultGroup())

        s = ClassGroupSingleton(5)
        self.clazz.AppendGroup(s)
        self.check_messages([(CLASS_CHANGED,)])
        self.assertEquals(self.clazz.FindGroup(5), s)
        self.assertEquals(self.clazz.FindGroup(0),
                          self.clazz.GetDefaultGroup())

    def test_add_range(self):
        """Test Classification.AppendGroup(ClassGroupRange())"""
        self.assertEquals(self.clazz.FindGroup(0),
                          self.clazz.GetDefaultGroup())

        r = ClassGroupRange((-10, 10))
        self.clazz.AppendGroup(r)
        self.check_messages([(CLASS_CHANGED,)])
        self.assertEquals(self.clazz.FindGroup(-11),
                          self.clazz.GetDefaultGroup())
        self.assertEquals(self.clazz.FindGroup(-10), r)
        self.assertEquals(self.clazz.FindGroup(9), r)
        self.assertEquals(self.clazz.FindGroup(5), r)
        self.assertEquals(self.clazz.FindGroup(10),
                          self.clazz.GetDefaultGroup())

    def test_add_pattern(self):
        """Test Classification.AppendGroup(ClassGroupPattern())"""
        self.assertEquals(self.clazz.FindGroup(5),
                          self.clazz.GetDefaultGroup())

        s = ClassGroupPattern("A")
        self.clazz.AppendGroup(s)
        self.check_messages([(CLASS_CHANGED,)])
        self.assertEquals(self.clazz.FindGroup("A"), s)
        self.assertEquals(self.clazz.FindGroup("B"),
                          self.clazz.GetDefaultGroup())

    def test_multiple_groups_numerical(self):
        """Test numerical Classification with multiple groups"""
        # two singletons matching 1 to test whether they're tested in
        # the right order. Use a non default fill on the second to make
        # it compare unequal to the first.
        s1 = ClassGroupSingleton(1)
        s1a = ClassGroupSingleton(1)
        s1a.GetProperties().SetFill(blue)
        # Sanity check: are they considered different?
        self.assertNotEqual(s1, s1a)

        s2 = ClassGroupSingleton(2)
        r = ClassGroupRange((-10, 10))

        self.clazz.AppendGroup(s1)
        self.clazz.AppendGroup(s2)
        self.clazz.AppendGroup(s1a)
        self.clazz.AppendGroup(r)
        self.check_messages([(CLASS_CHANGED,), (CLASS_CHANGED,),
                             (CLASS_CHANGED,), (CLASS_CHANGED,)])

        self.assertEquals(self.clazz.FindGroup(-11),
                          self.clazz.GetDefaultGroup())
        self.assertEquals(self.clazz.FindGroup(-10), r)
        self.assertEquals(self.clazz.FindGroup(1), s1)
        self.assertEquals(self.clazz.FindGroup(2), s2)
        self.assertEquals(self.clazz.FindGroup(3), r)
        self.assertEquals(self.clazz.FindGroup(9), r)
        self.assertEquals(self.clazz.FindGroup(10),
                          self.clazz.GetDefaultGroup())

    def test_multiple_groups_textual(self):
        """Test textual Classification with multiple groups"""
        # A singleton and a pattern matching 'A' to test whether 
        # they're tested in the right order. Use a non default fill 
        # on the pattern to make it compare unequal to the first.
        s = ClassGroupSingleton("A")
        p = ClassGroupPattern("A")
        p.GetProperties().SetFill(blue)
        # Sanity check: are they considered different?
        self.assertNotEqual(s, p)

        self.clazz.AppendGroup(s)
        self.clazz.AppendGroup(p)
        self.check_messages([(CLASS_CHANGED,), (CLASS_CHANGED,)])

        self.assertEquals(self.clazz.FindGroup("bca"),
                          self.clazz.GetDefaultGroup())
        self.assertEquals(self.clazz.FindGroup("A"), s)
        self.assertEquals(self.clazz.FindGroup("Abc"), p)
        self.assertEquals(self.clazz.FindGroup("abc"),
                          self.clazz.GetDefaultGroup())

    def test_insert_group(self):
        """Test Classification.InsertGroup()"""
        s1 = ClassGroupSingleton(1)
        s2 = ClassGroupSingleton(2)
        r = ClassGroupRange((0, 10))

        self.clazz.AppendGroup(s1)
        self.clazz.AppendGroup(r)
        self.assertEquals(self.clazz.FindGroup(2), r)
        self.clear_messages()

        self.clazz.InsertGroup(1, s2)
        self.assertEquals(self.clazz.FindGroup(2), s2)
        self.check_messages([(CLASS_CHANGED,)])

    def test_remove_group(self):
        """Test Classification.RemoveGroup()"""
        s1 = ClassGroupSingleton(1)
        s2 = ClassGroupSingleton(2)
        r = ClassGroupRange((0, 10))

        self.clazz.AppendGroup(s1)
        self.clazz.AppendGroup(s2)
        self.clazz.AppendGroup(r)
        self.assertEquals(self.clazz.FindGroup(2), s2)
        self.clear_messages()

        self.clazz.RemoveGroup(1)
        self.assertEquals(self.clazz.FindGroup(2), r)
        self.check_messages([(CLASS_CHANGED,)])

    def test_replace_group(self):
        """Test Classification.ReplaceGroup()"""
        s1 = ClassGroupSingleton(1)
        s2 = ClassGroupSingleton(2)
        r = ClassGroupRange((0, 10))

        self.clazz.AppendGroup(s2)
        self.clazz.AppendGroup(r)
        self.assertEquals(self.clazz.FindGroup(2), s2)
        self.assertEquals(self.clazz.FindGroup(1), r)
        self.clear_messages()

        self.clazz.ReplaceGroup(0, s1)
        self.assertEquals(self.clazz.FindGroup(2), r)
        self.assertEquals(self.clazz.FindGroup(1), s1)
        self.check_messages([(CLASS_CHANGED,)])

    def test_deepcopy_numerical(self):
        """Test deepcopy(numerical Classification())"""
        self.clazz.AppendGroup(ClassGroupSingleton(5))
        self.clazz.AppendGroup(ClassGroupRange((-10, 10)))

        clazz = copy.deepcopy(self.clazz)

        self.assertEquals(clazz.GetNumGroups(), self.clazz.GetNumGroups())

        for i in range(clazz.GetNumGroups()):
            self.assertEquals(clazz.GetGroup(i), self.clazz.GetGroup(i))

    def test_deepcopy_textual(self):
        """Test deepcopy(textual Classification())"""
        self.clazz.AppendGroup(ClassGroupSingleton("A"))
        self.clazz.AppendGroup(ClassGroupPattern("B"))

        clazz = copy.deepcopy(self.clazz)

        self.assertEquals(clazz.GetNumGroups(), self.clazz.GetNumGroups())

        for i in range(clazz.GetNumGroups()):
            self.assertEquals(clazz.GetGroup(i), self.clazz.GetGroup(i))


    def test_iterator(self):
        """Test Classification iteration"""
        groups = [ClassGroupSingleton(5), ClassGroupSingleton(5),
                  ClassGroupRange((-3, 3)), ClassGroupSingleton(-5),
                  ClassGroupDefault()]

        for g in groups:
            self.clazz.AppendGroup(g)

        def convert(group):
            if isinstance(group, ClassGroupDefault):   return 0
            if isinstance(group, ClassGroupSingleton): return 1
            if isinstance(group, ClassGroupRange):     return 2

        list = []
        for g in self.clazz:
            list.append(convert(g))

        self.assertEquals(list, [0, 1, 1, 2, 1, 0])


if __name__ == "__main__":
    support.run_tests()
