# *****************************************************************************
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program 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 General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Module authors:
#   Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
#
# *****************************************************************************
"""test data types."""


# no fixtures needed
import pytest

from frappy.datatypes import BoolType, FloatRange, IntRange, StructOf
from frappy.errors import ProgrammingError
from frappy.modulebase import HasAccessibles
from frappy.params import Command, Parameter


def test_Command():
    class Mod(HasAccessibles):
        @Command()
        def cmd(self):
            """do something"""
        @Command(IntRange(-9,9), result=IntRange(-1,1), description='do some other thing')
        def cmd2(self):
            pass

    assert Mod.cmd.description == 'do something'
    assert Mod.cmd.argument is None
    assert Mod.cmd.result is None
    assert Mod.cmd.for_export() == {'datainfo': {'type': 'command'},
                                'description': 'do something'}

    assert Mod.cmd2.description == 'do some other thing'
    assert isinstance(Mod.cmd2.argument, IntRange)
    assert isinstance(Mod.cmd2.result, IntRange)
    assert Mod.cmd2.for_export() == {'datainfo': {'type': 'command', 'argument': {'type': 'int', 'min': -9, 'max': 9},
                                                  'result': {'type': 'int', 'min': -1, 'max': 1}},
                                     'description': 'do some other thing'}
    assert Mod.cmd2.exportProperties() == {'datainfo': {'type': 'command', 'argument': {'type': 'int', 'max': 9, 'min': -9},
                                                        'result': {'type': 'int', 'max': 1, 'min': -1}},
                                           'description': 'do some other thing'}


def test_cmd_struct_opt():
    with pytest.raises(ProgrammingError):
        class WrongName(HasAccessibles):  # pylint: disable=unused-variable
            @Command(StructOf(a=IntRange(), b=IntRange()))
            def cmd(self, a, c):
                pass
    class Mod(HasAccessibles):
        @Command(StructOf(a=IntRange(), b=IntRange()))
        def cmd(self, a=5, b=5):
            pass
    assert Mod.cmd.datatype.argument.optional == ['a', 'b']
    class Mod2(HasAccessibles):
        @Command(StructOf(a=IntRange(), b=IntRange()))
        def cmd(self, a, b=5):
            pass
    assert Mod2.cmd.datatype.argument.optional == ['b']
    class Mod3(HasAccessibles):
        @Command(StructOf(a=IntRange(), b=IntRange()))
        def cmd(self, a, b):
            pass
    assert Mod3.cmd.datatype.argument.optional == []


def test_Parameter():
    class Mod(HasAccessibles):
        p1 = Parameter('desc1', datatype=FloatRange(), default=0)
        p2 = Parameter('desc2', datatype=FloatRange(), default=0, readonly=True)
        p3 = Parameter('desc3', datatype=FloatRange(), default=0, readonly=False)
        p4 = Parameter('desc4', datatype=FloatRange(), constant=1)
    assert repr(Mod.p1) != repr(Mod.p3)
    assert id(Mod.p1.datatype) != id(Mod.p2.datatype)
    assert Mod.p1.exportProperties() == {'datainfo': {'type': 'double'}, 'description': 'desc1', 'readonly': True}
    assert Mod.p2.exportProperties() == {'datainfo': {'type': 'double'}, 'description': 'desc2', 'readonly': True}
    assert Mod.p3.exportProperties() == {'datainfo': {'type': 'double'}, 'description': 'desc3', 'readonly': False}
    assert Mod.p4.exportProperties() == {'datainfo': {'type': 'double'}, 'description': 'desc4', 'readonly': True,
                                         'constant': 1.0}
    p3 = Mod.p1.copy()
    assert id(p3) != id(Mod.p1)
    assert repr(Mod.p1) == repr(p3)

    with pytest.raises(ProgrammingError):
        Parameter(None, datatype=float, inherit=False)


def test_Override():
    class Base(HasAccessibles):
        p1 = Parameter('description1', datatype=BoolType, default=False)
        p2 = Parameter('description1', datatype=BoolType, default=False)
        p3 = Parameter('description1', datatype=BoolType, default=False)

    class Mod(Base):
        p1 = Parameter(default=True)
        p2 = Parameter()  # override without change

    assert id(Mod.p1) != id(Base.p1)
    assert id(Mod.p2) != id(Base.p2)
    assert id(Mod.p3) == id(Base.p3)
    assert repr(Mod.p2) == repr(Base.p2)  # must be a clone
    assert repr(Mod.p3) == repr(Base.p3)  # must be a clone
    assert Mod.p1.default is True
    # manipulating default makes Base.p1 and Mod.p1 match
    Mod.p1.default = False
    assert repr(Mod.p1) == repr(Base.p1)

    for cls in locals().values():
        if hasattr(cls, 'accessibles'):
            for p in cls.accessibles.values():
                assert isinstance(p.ownProperties, dict)
                assert p.copy().ownProperties == {}


def test_Export():
    class Mod(HasAccessibles):
        param = Parameter('description1', datatype=BoolType, default=False)
    assert Mod.param.export == '_param'


@pytest.mark.parametrize('arg, value', [
    ('always', 0),
    (0, 0),
    ('never', 999999999),
    (999999999, 999999999),
    (1, 1),
])
def test_update_unchanged_ok(arg, value):
    par = Parameter('', datatype=FloatRange(), default=0, update_unchanged=arg)
    assert par.update_unchanged == value


@pytest.mark.parametrize('arg', ['alws', '', -2, -0.1, None])
def test_update_unchanged_fail(arg):
    with pytest.raises(ProgrammingError):
        Parameter('', datatype=FloatRange(), default=0, update_unchanged=arg)