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

"""
Test the Menu
"""

__version__ = "$Revision: 1.10 $"

import unittest

import support
support.initthuban()

from Thuban.Model.classgen import \
    generate_singletons, \
    generate_uniform_distribution, \
    generate_quantiles, \
    calculate_quantiles, \
    grey_ramp, CustomRamp, FixedRamp
from Thuban.Model.range import Range
from Thuban.Model.color import Color

from Thuban.Model.classification import ClassGroupRange, ClassGroupProperties

class ClassGenTest(unittest.TestCase):

    def doClassRangeTest(self, clazz, ranges):
        self.assertEquals(clazz.GetNumGroups(), len(ranges))
        for i in range(clazz.GetNumGroups()):
            group = clazz.GetGroup(i)
            r1 = str(Range(ranges[i]))
            r2 = group.GetRange()
            self.assertEquals(r1, r2)

        self.doBoundsTest(clazz)

    def doClassSingleTest(self, clazz, _list):
        self.assertEquals(clazz.GetNumGroups(), len(_list))
        for i in range(clazz.GetNumGroups()):
            group = clazz.GetGroup(i)
            self.assertEquals(group.GetValue(), _list[i])

        self.doBoundsTest(clazz)

    def doBoundsTest(self, clazz, ramp = grey_ramp):

        #
        # check that the properties are right (i.e. the first group
        # is all white, the last is all black). This assumes that
        # the grey_ramp, unless another is provided.
        #
        if clazz.GetNumGroups() >= 1:
            groupF = clazz.GetGroup(0)
            first = ramp.GetProperties(0)
            self.assertEquals(groupF.GetProperties(), first)

        if clazz.GetNumGroups() >= 2:
            groupL = clazz.GetGroup(clazz.GetNumGroups() - 1)
            last = ramp.GetProperties(1)
            self.assertEquals(groupL.GetProperties(), last)
            
    def test_generate_singletons(self):
        """Test generate_singletons"""

        eq = self.assertEquals
        gs = generate_singletons
        ramp = grey_ramp

        _list = [1, 2, 3, 4]
        cl = gs(_list, ramp)
        self.doClassSingleTest(cl, _list)

        _list = range(0, 100)
        cl = gs(_list, ramp)
        self.doClassSingleTest(cl, _list)

        _list = range(-100, 100)
        cl = gs(_list, ramp)
        self.doClassSingleTest(cl, _list)

        _list = ['a', 'b', 'c', 'd']
        cl = gs(_list, ramp)
        self.doClassSingleTest(cl, _list)

        _list = []
        cl = gs(_list, ramp)
        self.doClassSingleTest(cl, _list)

        _list = [1]
        cl = gs(_list, ramp)
        self.doClassSingleTest(cl, _list)
        

    def test_generate_uniform_distribution(self):
        """Test generate_uniform_distribution"""

        eq = self.assertEquals
        gud = generate_uniform_distribution
        ramp = grey_ramp

        cl = gud(0, 99, 10, ramp, True)
        self.doClassRangeTest(cl, ("[0;10[", "[10;20[", "[20;30[", "[30;40[",
                              "[40;50[", "[50;60[", "[60;70[", "[70;80[",
                              "[80;90[", "[90;99]"))

        cl = gud(0, 99, 10, ramp, False)
        self.doClassRangeTest(cl, ("[0;9.9[", "[9.9;19.8[", "[19.8;29.7[", 
                              "[29.7;39.6[", "[39.6;49.5[", "[49.5;59.4[", 
                              "[59.4;69.3[", "[69.3;79.2[", "[79.2;89.1[", 
                              "[89.1;99.0]"))

        cl = gud(1, 2, 2, ramp, False)
        self.doClassRangeTest(cl, ("[1;1.5[", "[1.5;2]"))

        cl = gud(1, 2, 2, ramp, True)
        self.doClassRangeTest(cl, ("[1;2[", "[2;2]"))


    def test_generate_quantiles(self):
        """Test generate_quantiles"""

        eq = self.assertEquals

        #
        # Test calculate_quantiles
        #

        gq = generate_quantiles

        ramp = grey_ramp

        adj, cl = gq([1, 2, 3, 4], [.25, .5, .75, 1.0], ramp, Range("[1;4]"))
        self.failIf(adj)
        self.doClassRangeTest(cl, ("[1;1]", "]1;2]", "]2;3]", "]3;4]"))

        adj, cl = gq(range(0, 100), [.25, .5, .75, 1.0], ramp, Range("[0;100["))
        self.failIf(adj)
        self.doClassRangeTest(cl, ("[0;24]", "]24;49]", "]49;74]", "]74;100["))

        adj, cl = gq(range(0, 100), [.33, .66, 1.0], ramp, Range("[0;100]"))
        self.failIf(adj)
        self.doClassRangeTest(cl, ("[0;32]", "]32;65]", "]65;100]"))

        # negative input
        adj,cl = gq(range(-100,100), [.33, .66, 1.0], ramp, Range("[-100;100]"))
        self.failIf(adj)
        self.doClassRangeTest(cl, ("[-100;-35]", "]-35;31]", "]31;100]"))

        # unequal percentiles
        adj,cl = gq(range(0, 100), [.25, .66, .8, 1.0], ramp, Range("[0;100]"))
        self.failIf(adj)
        self.doClassRangeTest(cl, ("[0;24]", "]24;65]", "]65;79]", "]79;100]"))

        # input all the same
        adj,cl = gq([1, 1, 1, 1], [.25, .5, .75, 1.0], ramp, Range("[1;4]"))
        self.failUnless(adj)
        self.doClassRangeTest(cl, ("[1;4]",))

        # empty input
        adj,cl = gq([], [.25, .5, .75, 1.0], ramp, Range("[1;4]"))
        self.failUnless(adj)
        self.doClassRangeTest(cl, ())

        # empty range
        adj,cl = gq([1, 2, 3, 4], [.25, .5, .75, 1.0], ramp, Range("]0;1["))
        self.failUnless(adj)
        self.doClassRangeTest(cl, ())

        # empty percentiles
        self.assertRaises(ValueError, gq,
                          [1, 2, 3, 4], [], ramp, Range("]0;1["))

        # single percentile
        self.assertRaises(ValueError, gq,
                          [1, 2, 3, 4], [.5], ramp, Range("[0;4]"))

        # more percentiles than input
        adj,cl = gq([1], [.5, 1.0], ramp, Range("[0;4]"))
        self.failUnless(adj)
        self.doClassRangeTest(cl, ("[0;4]",))

        adj,cl = gq([1], [.1, .2, .3, .4, .5, .6, .7, .8, .9, 1.0], ramp, Range("[0;4]"))
        self.failUnless(adj)
        self.doClassRangeTest(cl, ("[0;4]",))

        # range smaller than the input
        adj,cl = gq([1, 2, 3, 4], [.5, 1.0], ramp, Range("[2;3]"))
        self.failIf(adj)
        self.doClassRangeTest(cl, ("[2;2]","]2;3]"))

        # range outside the input
        adj,cl = gq([5, 6, 7, 8], [.5, 1.0], ramp, Range("[2;3]"))
        self.failUnless(adj)
        self.doClassRangeTest(cl, ())

        adj,cl = gq([1, 1, 1, 1, 1, 1], [.25, .5, .75, 1.0], ramp, Range("[1;4]"))
        self.failUnless(adj)
        self.doClassRangeTest(cl, ("[1;4]",))

        adj,cl = gq([1, 1, 1, 1, 1, 2, 3], [.25, .5, .75, 1.0], ramp, Range("[1;4]"))
        self.failUnless(adj)
        self.doClassRangeTest(cl, ("[1;1]", "]1;2]", "]2;4]"))

        # adjusted quantiles
        adj,cl = gq([1, 1, 1, 1, 1, 
                     2, 2, 2, 2, 2, 
                     3, 3, 3, 3, 3, 
                     4, 4, 4, 4, 4,
                     5, 5, 5, 5, 5], 
                    [.12, .24, .36, .50, .62, .76, .88, 1.0], ramp, Range("[1;5]"))
        
        self.failUnless(adj)
        self.doClassRangeTest(cl, ("[1;1]", "]1;2]", "]2;3]", "]3;4]", "]4;5]"))
        
    def test_calculate_quantiles(self):
        """Test calculate_quantiles"""

        eq = self.assertEquals

        #
        # Test calculate_quantiles
        #

        cq = calculate_quantiles

        result = cq([1, 2, 3, 4], [.25, .5, .75, 1.0], Range("[1;4]"))
        eq(result, (0, 0, 3, [(0, .25), (1, .5), (2, .75), (3, 1.0)]))

        result = cq(range(0, 100), [.25, .5, .75, 1.0], Range("[0;100]"))
        eq(result, (0, 0, 99, [(24, .25), (49, .5), (74, .75), (99, 1.0)]))

        result = cq(range(0, 100), [.33, .66, 1.0], Range("[0;100]"))
        eq(result, (0, 0, 99, [(32, .33), (65, .66), (99, 1.0)]))

        # negative input
        result = cq(range(-100, 100), [.33, .66, 1.0], Range("[-100;100]"))
        eq(result, (0, 0, 199, [(65, .33), (131, .66), (199, 1.0)]))

        # unequal percentiles
        result = cq(range(0, 100), [.25, .66, .8, 1.0], Range("[0;100]"))
        eq(result, (0, 0, 99, [(24, .25), (65, .66), (79, .8), (99, 1.0)]))

        # input all the same
        result = cq([1, 1, 1, 1], [.25, .5, .75, 1.0], Range("[1;4]"))
        eq(result, (1, 0, 3, [(3, 1.0)]))

        # empty input
        result = cq([], [.25, .5, .75, 1.0], Range("[1;4]"))
        eq(result, None)

        # empty range
        result = cq([1, 2, 3, 4], [.25, .5, .75, 1.0], Range("]0;1["))
        eq(result, None)

        # empty percentiles
        self.assertRaises(ValueError, cq, [1, 2, 3, 4], [], Range("]0;1["))

        # single percentile
        self.assertRaises(ValueError, cq, [1, 2, 3, 4], [.5], Range("[0;4]"))

        # percentile doesn't cover range
        self.assertRaises(ValueError, cq, [1, 2, 3, 4], [.5,.8], Range("[0;4]"))

        # more percentiles than input
        result = cq([1], [.5, 1.0], Range("[0;4]"))
        eq(result, (1, 0, 0, [(0, 1.0)]))

        result = cq([1], [.1, .2, .3, .4, .5, .6, .7, .8, .9, 1.0], Range("[0;4]"))
        eq(result, (1, 0, 0, [(0, 1.0)]))

        # range smaller than the input
        result = cq([1, 2, 3, 4], [.5, 1.0], Range("[2;3]"))
        eq(result, (0, 1, 2, [(1, .5), (2, 1.0)]))

        # range outside the input
        result = cq([5, 6, 7, 8], [.5, 1.0], Range("[2;3]"))
        eq(result, None)

        result = cq([1, 1, 1, 1, 1, 1], [.25, .5, .75, 1.0], Range("[1;4]"))
        eq(result, (1, 0, 5, [(5, 1.0)]))

        result = cq([1, 1, 1, 1, 1, 2, 3], [.25, .5, .75, 1.0], Range("[1;4]"))
        eq(result, (1, 0, 6, 
               [(4, 0.7142857142857143), # the algorithm generated
                (5, 0.8571428571428571), # these values, but they are
                (6, 1.0)]))              # right.

        # adjusted quantiles
        result = cq([1, 1, 1, 1, 1, 
                     2, 2, 2, 2, 2, 
                     3, 3, 3, 3, 3, 
                     4, 4, 4, 4, 4,
                     5, 5, 5, 5, 5], 
                    [.12, .24, .36, .50, .62, .76, .88, 1.0], Range("[1;5]"))
        eq(result, (1, 0, 24, [(4, .2), (9, .4), (14, .6), (19, .8), (24, 1.0)]))


class TestCustomRamp(unittest.TestCase):

    def test_color_interpolation(self):
        """Test CustomRamp color interpolation"""
        start = ClassGroupProperties()
        start.SetFill(Color(1, 1, 1))
        start.SetLineColor(Color(0, 0, 0))

        end = ClassGroupProperties()
        end.SetFill(Color(1, 0, 0))
        end.SetLineColor(Color(0, 1, 0))

        ramp = CustomRamp(start, end)
        half = ramp.GetProperties(0.5)
        self.assertEquals(half.GetFill(), Color(1, 0.5, 0.5))
        self.assertEquals(half.GetLineColor(), Color(0, 0.5, 0))


class TestFixedRamp(unittest.TestCase):

    def test(self):
        """Test FixedRamp"""
        eq = self.assertEquals

        for lineColor, lineWidth, fillColor in \
            [(None, None, None), (Color(1, 1, 1), None, None),
             (None, 4, None), (None, None, Color(0, 1, 0)),
             (Color(1, 1, 1), 4, None), (Color(1, 1, 1), None, Color(0, 1, 0)),
             (None, 4, Color(0, 1, 0)), (Color(1, 1, 1), 4, Color(0, 1, 0))]:

            framp = FixedRamp(grey_ramp, (lineColor, lineWidth, fillColor))

            for i in [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]:
                props = framp.GetProperties(i)
                grey = Color(1 - i, 1 - i, 1 - i)
                if lineColor is not None:
                    eq(props.GetLineColor(), lineColor)
                else:
                    eq(props.GetLineColor(), grey)
                if lineWidth is not None:
                    eq(props.GetLineWidth(), lineWidth)
                if fillColor is not None:
                    eq(props.GetFill(), fillColor)
                else:
                    eq(props.GetFill(), grey)

if __name__ == "__main__":
    unittest.main()

