#!/usr/bin/env python

#############################################################################
##
# This file is part of Taurus
##
# http://taurus-scada.org
##
# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
##
# Taurus is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
##
# Taurus is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
##
# You should have received a copy of the GNU Lesser General Public License
# along with Taurus.  If not, see <http://www.gnu.org/licenses/>.
##
#############################################################################

"""Test for taurus.core.tango.test.test_tangovalidator..."""

# __all__ = []

import numpy
from taurus.external import unittest
from taurus.external.pint import Quantity
import taurus
from taurus.test import insertTest
from taurus.core.taurusbasetypes import DataType
from taurus.core.evaluation.evalattribute import EvaluationAttrValue


@insertTest(helper_name='read_attr',
            attr_fullname='eval:1',
            expected=dict(rvalue=Quantity(1, 'dimensionless'),
                          type=DataType.Integer,
                          label='1',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue=Quantity([], 'dimensionless'),
                                wvalue=None
                                ),
            expectedshape=None
            )
@insertTest(helper_name='read_attr',
            attr_fullname='eval:Quantity("1m")+Quantity("2m")',
            expected=dict(rvalue=Quantity(3, 'm'),
                          type=DataType.Integer,
                          label='Quantity("1m")+Quantity("2m")',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue=Quantity(3, 'm'),
                                wvalue=None
                                ),
            expectedshape=None
            )
@insertTest(helper_name='read_attr',
            attr_fullname='eval:foo=Quantity("1m");bar=Quantity("2m");foo+bar',
            expected=dict(rvalue=Quantity(3, 'm'),
                          type=DataType.Integer,
                          label='foo+bar',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue=Quantity(3, 'm'),
                                wvalue=None
                                ),
            expectedshape=None
            )
@insertTest(helper_name='read_attr',
            attr_fullname='eval:foo=Q("1m");foo-Q("2m")',
            expected=dict(rvalue=Quantity(-1, 'm'),
                          type=DataType.Integer,
                          label='foo-Q("2m")',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue=Quantity(-1, 'm'),
                                wvalue=None
                                ),
            expectedshape=None
            )
@insertTest(helper_name='read_attr',
            attr_fullname='eval:[123, 123, 123]',
            expected=dict(rvalue=Quantity([123, 123, 123], 'dimensionless'),
                          type=DataType.Integer,
                          label='[123, 123, 123]',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue=Quantity([123, 123, 123],
                                                'dimensionless'),
                                wvalue=None
                                ),
            expectedshape=(3, )
            )
@insertTest(helper_name='read_attr',
            attr_fullname='eval:Quantity([1.23, 1.23, 1.23], "m")',
            expected=dict(rvalue=Quantity([1.23, 1.23, 1.23], 'm'),
                          type=DataType.Float,
                          label='Quantity([1.23, 1.23, 1.23], "m")',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue=Quantity([1.23, 1.23, 1.23], 'm'),
                                wvalue=None
                                ),
            expectedshape=(3, )
            )
@insertTest(helper_name='read_attr',
            attr_fullname='eval:foo=Quantity([1.23, 1.23, 1.23], "m");foo*10',
            expected=dict(rvalue=Quantity([12.3, 12.3, 12.3], 'm'),
                          type=DataType.Float,
                          label='foo*10',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue=Quantity([12.3, 12.3, 12.3],
                                                'm'),
                                wvalue=None
                                ),
            expectedshape=(3, )
            )
@insertTest(helper_name='read_attr',
            attr_fullname='eval:[[1,2,3]]*3',
            expected=dict(rvalue=Quantity([[1, 2, 3]] * 3, 'dimensionless'),
                          type=DataType.Integer,
                          label='[[1,2,3]]*3',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue=Quantity([[1, 2, 3]] * 3,
                                                'dimensionless'),
                                wvalue=None
                                ),
            expectedshape=(3, 3)
            )
@insertTest(helper_name='read_attr',
            attr_fullname='eval:foo=1;foo+{eval:foo=2;foo}+{eval:foo=3;foo}',
            expected=dict(rvalue=Quantity(6, 'dimensionless'),
                          type=DataType.Integer,
                          label='foo+foo+foo',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue=Quantity(6, 'dimensionless'),
                                wvalue=None
                                ),
            expectedshape=None
            )
@insertTest(helper_name='read_attr',
            attr_fullname='eval:True',
            expected=dict(rvalue=True,
                          type=DataType.Boolean,
                          label='True',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue=True,
                                wvalue=None
                                ),
            expectedshape=None
            )
@insertTest(helper_name='read_attr',
            attr_fullname='eval:foo=1;bar=2;foo==bar',
            expected=dict(rvalue=False,
                          type=DataType.Boolean,
                          label='foo==bar',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue=False,
                                wvalue=None
                                ),
            expectedshape=None
            )
@insertTest(helper_name='read_attr',
            attr_fullname='eval:[[True, False, True]]*3',
            expected=dict(rvalue=[[True, False, True]] * 3,
                          type=DataType.Boolean,
                          label='[[True, False, True]]*3',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue=[[True, False, True]] * 3,
                                wvalue=None
                                ),
            expectedshape=(3, 3)
            )
@insertTest(helper_name='read_attr',
            attr_fullname='eval:"1m"',
            expected=dict(rvalue="1m",
                          type=DataType.String,
                          label='"1m"',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue="1m",
                                wvalue=None
                                ),
            expectedshape=None
            )
@insertTest(helper_name='read_attr',
            attr_fullname='eval:"m"*3',
            expected=dict(rvalue="mmm",
                          type=DataType.String,
                          label='"m"*3',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue="mmm",
                                wvalue=None
                                ),
            expectedshape=None
            )
@insertTest(helper_name='read_attr',
            attr_fullname='eval:"2+5-10"',
            expected=dict(rvalue="2+5-10",
                          type=DataType.String,
                          label='"2+5-10"',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue="2+5-10",
                                wvalue=None
                                ),
            expectedshape=None
            )
@insertTest(helper_name='read_attr',
            attr_fullname='eval:[["HelloWorld", "HelloWorld", "HelloWorld"]]*3',
            expected=dict(rvalue=[["HelloWorld", "HelloWorld", "HelloWorld"]] * 3,
                          type=DataType.String,
                          label='[["HelloWorld", "HelloWorld",' +
                                ' "HelloWorld"]]*3',
                          writable=False,
                          ),
            expected_attrv=dict(rvalue=[["HelloWorld", "HelloWorld",
                                         "HelloWorld"]] * 3,
                                wvalue=None
                                ),
            expectedshape=(3, 3)
            )
class EvalAttributeTestCase(unittest.TestCase):

    def read_attr(self, attr_fullname, expected={}, expected_attrv={},
                  expectedshape=None):
        """check creation and correct read of an evaluationAttribute"""

        a = taurus.Attribute(attr_fullname)
        read_value = a.read()

        msg = ('read() for "{0}" did not return an EvaluationAttrValue ' +
               '(got a {1})').format(attr_fullname,
                                     read_value.__class__.__name__)
        self.assertTrue(isinstance(read_value, EvaluationAttrValue), msg)

        # Test attribute
        for k, exp in expected.iteritems():
            try:
                got = getattr(a, k)
            except AttributeError:
                msg = ('The attribute, "%s" does not provide info on %s' %
                       (attr_fullname, k))
                self.fail(msg)
            msg = ('%s for "%s" should be %r (got %r)' %
                   (attr_fullname, k,  exp, got))
            self.__assertValidValue(exp, got, msg)

        # Test attribute value
        for k, exp in expected_attrv.iteritems():
            try:
                got = getattr(read_value, k)
            except AttributeError:
                msg = ('The read value for "%s" does not provide info on %s' %
                       (attr_fullname, k))
                self.fail(msg)
            msg = ('%s for "%s" should be %r (got %r)' %
                   (attr_fullname, k,  exp, got))
            self.__assertValidValue(exp, got, msg)

        if expectedshape is not None:
            shape = numpy.shape(read_value.rvalue)
            msg = ('rvalue.shape for %s should be %r (got %r)' %
                   (attr_fullname, expectedshape, shape))
            self.assertEqual(shape, expectedshape, msg)

    def __assertValidValue(self, exp, got, msg):
        # if we are dealing with quantities, use the magnitude for comparing
        if isinstance(got, Quantity):
            got = got.to(Quantity(exp).units).magnitude
        if isinstance(exp, Quantity):
            exp = exp.magnitude
        try:
            # for those values that can be handled by numpy.allclose()
            chk = numpy.allclose(got, exp)
        except:
            # for the rest
            if isinstance(got, numpy.ndarray):
                got = got.tolist()
            chk = bool(got == exp)

        self.assertTrue(chk, msg)
