# coding: utf-8
"frozen and hidden values"
from .autopath import do_autopath
do_autopath()

from py.test import raises

from tiramisu.setting import owners, groups
from tiramisu import ChoiceOption, BoolOption, IntOption, FloatOption, \
    StrOption, OptionDescription, SymLinkOption, MasterSlaves, Config, \
    Params, ParamContext, ParamOption, ParamValue
from tiramisu.error import PropertiesOptionError, ConfigError


def compare(calculated, expected):
    def convert_list(val):
        if isinstance(val, list):
            val = tuple(val)
        return val
    # convert to tuple
    for idx in range(len(calculated[0])):
        right_idx = expected[0].index(calculated[0][idx])
        for typ in range(4):
            assert convert_list(calculated[typ][idx]) == expected[typ][right_idx]


#____________________________________________________________
#freeze
def make_description_freeze():
    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
    gcdummy = BoolOption('dummy', 'dummy', default=False)
    objspaceoption = ChoiceOption('objspace', 'Object space',
                                  ('std', 'thunk'), 'std')
    booloption = BoolOption('bool', 'Test boolean option', default=True)
    intoption = IntOption('int', 'Test int option', default=0)
    floatoption = FloatOption('float', 'Test float option', default=2.3)
    stroption = StrOption('str', 'Test string option', default="abc")
    boolop = BoolOption('boolop', 'Test boolean option op', default=[True], multi=True)
    wantref_option = BoolOption('wantref', 'Test requires', default=False, properties=('force_store_value',),
                                requires=({'option': booloption, 'expected': True, 'action': 'hidden'},))
    wantref2_option = BoolOption('wantref2', 'Test requires', default=False, properties=('force_store_value', 'hidden'))
    wantref3_option = BoolOption('wantref3', 'Test requires', default=[False], multi=True, properties=('force_store_value',))
    st2 = SymLinkOption('st2', wantref3_option)
    wantframework_option = BoolOption('wantframework', 'Test requires',
                                      default=False,
                                      requires=({'option': booloption, 'expected': True, 'action': 'hidden'},))

    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
    descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption,
                              wantref_option, wantref2_option, wantref3_option, st2, stroption,
                              wantframework_option,
                              intoption, boolop])
    return descr


def return_val():
    return 1


def return_val2(value):
    return value


def return_val3(context, value):
    return value


def test_freeze_whole_config():
    descr = make_description_freeze()
    api = Config(descr)
    api.property.read_write()
    api.property.add('everything_frozen')
    assert api.option('gc.dummy').value.get() is False
    prop = []
    try:
        api.option('gc.dummy').value.set(True)
    except PropertiesOptionError as err:
        prop = err.proptype
    assert 'frozen' in prop
    assert api.option('gc.dummy').value.get() is False
    #
    api.property.pop('everything_frozen')
    api.option('gc.dummy').value.set(True)
    assert api.option('gc.dummy').value.get() is True
    #
    api.property.add('everything_frozen')
    owners.addowner("everythingfrozen2")
    prop = []
    try:
        api.option('gc.dummy').owner.set('everythingfrozen2')
    except PropertiesOptionError as err:
        prop = err.proptype
    assert 'frozen' in prop


def test_freeze_one_option():
    "freeze an option "
    descr = make_description_freeze()
    api = Config(descr)
    api.property.read_write()
    #freeze only one option
    api.option('gc.dummy').property.add('frozen')
    assert api.option('gc.dummy').value.get() is False
    prop = []
    try:
        api.option('gc.dummy').value.set(True)
    except PropertiesOptionError as err:
        prop = err.proptype
    assert 'frozen' in prop


def test_frozen_value():
    "setattr a frozen value at the config level"
    s = StrOption("string", "", default="string")
    descr = OptionDescription("options", "", [s])
    api = Config(descr)
    api.property.read_write()
    api.property.add('frozen')
    api.option('string').property.add('frozen')
    prop = []
    try:
        api.option('string').value.set('egg')
    except PropertiesOptionError as err:
        prop = err.proptype
    assert 'frozen' in prop


def test_freeze():
    "freeze a whole configuration object"
    descr = make_description_freeze()
    api = Config(descr)
    api.property.read_write()
    api.property.add('frozen')
    api.option('gc.name').property.add('frozen')
    prop = []
    try:
        api.option('gc.name').value.set('framework')
    except PropertiesOptionError as err:
        prop = err.proptype
    assert 'frozen' in prop


def test_freeze_multi():
    descr = make_description_freeze()
    api = Config(descr)
    api.property.read_write()
    api.property.add('frozen')
    api.option('boolop').property.add('frozen')
    prop = []
    try:
        api.option('boolop').value.set([True])
    except PropertiesOptionError as err:
        prop = err.proptype
    assert 'frozen' in prop


def test_force_store_value():
    descr = make_description_freeze()
    conf = Config(descr)
    compare(conf.value.exportation(), (('wantref', 'wantref2', 'wantref3'), (None, None, None), (False, False, (False,)), ('forced', 'forced', 'forced')))
    conf.option('wantref').value.set(True)
    compare(conf.value.exportation(), (('wantref', 'wantref2', 'wantref3'), (None, None, None), (True, False, (False,)), ('user', 'forced', 'forced')))
    conf.option('wantref').value.reset()
    compare(conf.value.exportation(), (('wantref', 'wantref2', 'wantref3'), (None, None, None), (False, False, (False,)), ('forced', 'forced', 'forced')))


def test_force_store_value_no_requirement():
    booloption = BoolOption('bool', 'Test boolean option', default=True)
    try:
        BoolOption('wantref', 'Test requires', default=False,
                   requires=({'option': booloption, 'expected': True, 'action': 'force_store_value'},))
    except ValueError:
        pass


def test_force_store_value_masterslaves_slave():
    b = IntOption('int', 'Test int option', multi=True)
    c = StrOption('str', 'Test string option', multi=True, properties=('force_store_value',))
    descr = MasterSlaves("int", "", [b, c])
    raises(ConfigError, "conf = Config(descr)")


#def test_force_store_value_masterslaves():
#    b = IntOption('int', 'Test int option', multi=True, properties=('force_store_value',))
#    c = StrOption('str', 'Test string option', multi=True)
#    descr = MasterSlaves("int", "", [b, c])
#    api = Config(descr)
#    assert api.value.get() == {'int': ('forced', ())}


def test_force_store_value_masterslaves_sub():
    b = IntOption('int', 'Test int option', multi=True, properties=('force_store_value',))
    c = StrOption('str', 'Test string option', multi=True)
    descr = MasterSlaves("int", "", [b, c])
    odr = OptionDescription('odr', '', [descr])
    api = Config(odr)
    compare(api.value.exportation(), (('int.int',), (None,), (tuple(),), ('forced',)))


def test_force_store_value_callback():
    b = IntOption('int', 'Test int option', properties=('force_store_value',), callback=return_val)
    descr = OptionDescription("int", "", [b])
    api = Config(descr)
    compare(api.value.exportation(), (('int',), (None,), (1,), ('forced',)))


def test_force_store_value_callback_params():
    b = IntOption('int', 'Test int option', properties=('force_store_value',), callback=return_val2, callback_params=Params(kwargs={'value': ParamValue(2)}))
    descr = OptionDescription("int", "", [b])
    api = Config(descr)
    compare(api.value.exportation(), (('int',), (None,), (2,), ('forced',)))


def test_force_store_value_callback_params_2():
    b = IntOption('int', 'Test int option', properties=('force_store_value',), callback=return_val3, callback_params=Params(ParamContext(), {'value': ParamValue(2)}))
    descr = OptionDescription("int", "", [b])
    api = Config(descr)
    compare(api.value.exportation(), (('int',), (None,), (2,), ('forced',)))


def test_force_store_value_callback_params_with_opt():
    a = IntOption('val1', "", 2)
    b = IntOption('int', 'Test int option', properties=('force_store_value',), callback=return_val2, callback_params=Params(kwargs={'value': ParamOption(a)}))
    descr = OptionDescription("int", "", [a, b])
    api = Config(descr)
    compare(api.value.exportation(), (('int',), (None,), (2,), ('forced',)))