from xml.etree.ElementTree import tostring, fromstring

import pymasep as pm
from pymasep.common.exception import CreationException
from pymasep.common.initializer import InitializerObjectState
from pymasep.common.template import Template


def test_init(mock_environment):
    expected_name = 'state'
    object_state = pm.common.ObjectState(environment=mock_environment, name='false_name',
                                         template=mock_environment.game.templates['EmptyObjectStateTemplate'])
    assert isinstance(object_state, pm.common.BaseObject)
    assert isinstance(object_state, pm.common.ObjectState)
    assert object_state.environment == mock_environment
    assert object_state.name == expected_name
    assert len(object_state.characteristics) == 0


def test_add_characteristic_charac_added(mock_environment):
    object_state = pm.common.ObjectState(environment=mock_environment, name='state',
                                         template=mock_environment.game.templates['EmptyObjectStateTemplate'])
    charac = pm.common.Characteristic(environment=mock_environment, name='Charac1',
                                      template=mock_environment.game.templates['CharacStr'])
    charac.value = 'test'
    object_state.add_characteristic(charac)
    assert len(object_state.characteristics) == 1
    assert object_state.characteristics['Charac1'] == charac


def test_to_xml_one_characteristic_transformed(mock_environment):
    expected_xml_str = '<ObjectState name="state" state="run">' \
                       '<Characteristic name="Step0" state="run"><int>10</int></Characteristic>' \
                       '</ObjectState>'
    objstate = pm.common.ObjectState(environment=mock_environment, name='state',
                                     template=mock_environment.game.templates['EmptyObjectStateTemplate'])
    charac = pm.common.Characteristic(environment=mock_environment, name='Step0',
                                      template=mock_environment.game.templates['CharacInt'])
    charac.value = 10
    objstate.add_characteristic(charac)

    xml_str_result = tostring(objstate.to_xml(), encoding='unicode')

    assert xml_str_result == expected_xml_str


def test_to_xml_two_characteristics_transformed(mock_environment):
    expected_xml_str = '<ObjectState name="state" state="run">' \
                       '<Characteristic name="Step0" state="run"><int>10</int></Characteristic>' \
                       '<Characteristic name="Charac1" state="run"><str>test</str></Characteristic>' \
                       '</ObjectState>'
    objstate = pm.common.ObjectState(environment=mock_environment, name='state',
                                     template=mock_environment.game.templates['EmptyObjectStateTemplate'])
    charac = pm.common.Characteristic(environment=mock_environment, name='Step0',
                                      template=mock_environment.game.templates['CharacInt'])
    charac.value = 10
    objstate.add_characteristic(charac)
    charac = pm.common.Characteristic(environment=mock_environment, name='Charac1',
                                      template=mock_environment.game.templates['CharacStr'])
    charac.value = 'test'
    objstate.add_characteristic(charac)

    xml_str_result = tostring(objstate.to_xml(), encoding='unicode')

    assert xml_str_result == expected_xml_str


def test_to_xml_two_characteristics_init(mock_environment):
    expected_xml_str = '<ObjectState name="state" state="init">' \
                       '<Characteristic name="Step0" state="init" initializer="initializer_interface_int">' \
                       '<int />' \
                       '</Characteristic>' \
                       '<Characteristic name="Charac1" state="run"><str>test</str></Characteristic>' \
                       '</ObjectState>'
    objstate = pm.common.ObjectState(environment=mock_environment, name='state',
                                     template=mock_environment.game.templates['EmptyObjectStateTemplate'])
    charac = pm.common.Characteristic(environment=mock_environment, name='Step0',
                                      template=mock_environment.game.templates['CharacInt'])
    charac.initializer = mock_environment.game.initializers['initializer_interface_int']
    charac.state = 'init'
    objstate.add_characteristic(charac)
    charac = pm.common.Characteristic(environment=mock_environment, name='Charac1',
                                      template=mock_environment.game.templates['CharacStr'])
    charac.value = 'test'
    objstate.add_characteristic(charac)

    xml_str_result = tostring(objstate.to_xml(), encoding='unicode')

    assert xml_str_result == expected_xml_str


def test_from_xml_no_template_should_init(mock_environment):
    objectstate_xml_str = '<ObjectState name="state">' \
                          '<Characteristic name="Step0"><int>0</int></Characteristic>' \
                          '<Characteristic name="Charac1"><str>test</str></Characteristic>' \
                          '</ObjectState>'
    xml_node = fromstring(objectstate_xml_str)

    object_state = mock_environment.create_base_object(name='', xml_node=xml_node)
    object_state.from_xml(mock_environment, xml_node)

    assert isinstance(object_state, pm.common.ObjectState)
    assert object_state.name == 'state'
    assert object_state.nature is None
    assert object_state.parent is None
    assert object_state.template is None
    assert object_state.initializer is None
    assert object_state.state == 'run'
    assert len(object_state.characteristics) == 2
    # Step0 and Charact initialized with values in xml
    assert object_state.characteristics['Step0'].value == 0
    assert object_state.characteristics['Charac1'].value == 'test'


def test_from_xml_template_no_subelement(mock_environment):
    objectstate_xml_str = '<ObjectState name="state" template="DefaultObjectStateTemplate" />'
    xml_node = fromstring(objectstate_xml_str)

    object_state = mock_environment.create_base_object(name='', xml_node=xml_node)
    try:
        object_state.from_xml(mock_environment, xml_node)
    except CreationException as e:
        assert str(e) == 'SubElement must be present if no initializer not present'
    else:
        assert False


def test_from_xml_template_initializer_charac_should_init(mock_environment):
    objectstate_xml_str = '<ObjectState name="state" template="DefaultObjectStateTemplate" ' \
                          'initializer="ObjStIntInitializer">' \
                          '<Characteristic name="CharacStr"><str>test</str></Characteristic>' \
                          '</ObjectState>'
    initializer_obj_state_c_int = InitializerObjectState(mock_environment.game, {'name': 'ObjStIntInitializer',
                                                                                 'subclass_initializer': {
                                                                                     'CharacInt': 'init_fixed_int'}})
    mock_environment.game.add_initializer(initializer_obj_state_c_int)
    xml_node = fromstring(objectstate_xml_str)

    object_state = mock_environment.create_base_object(name='', xml_node=xml_node)
    object_state.from_xml(mock_environment, xml_node)

    assert isinstance(object_state, pm.common.ObjectState)
    assert object_state.name == 'state'
    assert object_state.state == 'run'
    assert object_state.parent is None
    assert len(object_state.characteristics) == 2
    assert object_state.characteristics['CharacInt'].value == 1
    assert object_state.characteristics['CharacStr'].value == 'test'


def test_from_xml_initializer_fixed_value(mock_environment):
    objectstate_xml_str = '<ObjectState name="state" ' \
                          'template="DefaultObjectStateTemplate" ' \
                          'initializer="ObjStInitializer" />'
    xml_node = fromstring(objectstate_xml_str)

    object_state = mock_environment.create_base_object(name='', xml_node=xml_node)
    object_state.from_xml(mock_environment, xml_node)

    assert len(object_state.characteristics) == 2
    assert object_state.name == 'state'
    assert object_state.characteristics['CharacStr'].value == 'OneValue'


def test_from_xml_template_initializer_charac_not_complete_creation(mock_environment):
    objectstate_xml_str = '<ObjectState name="state" template="ThreeObjectStateTemplate" ' \
                          'initializer="ObjStIntInitializer">' \
                          '<Characteristic name="CharacStr"><str>test</str></Characteristic>' \
                          '</ObjectState>'
    objectstate_tmpl = Template(mock_environment.game, {"name": "ThreeObjectStateTemplate",
                                                        "created_class": "ObjectState",
                                                        "subclass_templates": ["CharacInt", "CharacStr",
                                                                               "CharacTuple"]})
    mock_environment.game.add_template(objectstate_tmpl)
    initializer_obj_state_c_int = InitializerObjectState(mock_environment.game, {'name': 'ObjStIntInitializer',
                                                                                 'subclass_initializer': {
                                                                                     'CharacInt': 'init_fixed_int'}})
    mock_environment.game.add_initializer(initializer_obj_state_c_int)
    xml_node = fromstring(objectstate_xml_str)

    object_state = mock_environment.create_base_object(name='', xml_node=xml_node)
    try:
        object_state.from_xml(mock_environment, xml_node)
    except CreationException as e:
        assert str(e) == 'Missing initializer for CharacTuple characteristic'
    else:
        assert False


def test_from_xml_template_initializer_charac_complete(mock_environment):
    objectstate_xml_str = '<ObjectState name="state" template="ThreeObjectStateTemplate" ' \
                          'initializer="ObjStIntInitializer">' \
                          '<Characteristic name="CharacTuple">' \
                          '<tuple><item><int>10</int></item><item><int>11</int></item></tuple>' \
                          '</Characteristic>' \
                          '</ObjectState>'
    objectstate_tmpl = Template(mock_environment.game, {"name": "ThreeObjectStateTemplate",
                                                        "created_class": "ObjectState",
                                                        "subclass_templates": ["CharacInt", "CharacStr",
                                                                               "CharacTuple"]})
    mock_environment.game.add_template(objectstate_tmpl)
    initializer_obj_state_c_int = InitializerObjectState(mock_environment.game, {'name': 'ObjStIntInitializer',
                                                                                 'subclass_initializer': {
                                                                                     'CharacInt': 'initializer_interface_int',
                                                                                     'CharacStr': 'init_fixed_str'}})
    mock_environment.game.add_initializer(initializer_obj_state_c_int)
    xml_node = fromstring(objectstate_xml_str)

    object_state = mock_environment.create_base_object(name='', xml_node=xml_node)
    object_state.from_xml(mock_environment, xml_node)

    assert object_state.state == 'init'
    assert object_state.initializer == mock_environment.game.initializers['ObjStIntInitializer']
    assert len(object_state.characteristics) == 3
    assert object_state.characteristics['CharacInt'].value is None
    assert object_state.characteristics['CharacStr'].value == 'OneValue'
    assert object_state.characteristics['CharacTuple'].value == (10, 11)
    assert object_state.characteristics['CharacInt'].state == 'init'
    assert object_state.characteristics['CharacStr'].state == 'run'
    assert object_state.characteristics['CharacTuple'].state == 'run'


def test_from_xml_state_all_charac_init(mock_environment):
    objectstate_xml_str = '<ObjectState name="state" state="init" initializer="ObjStInitializerInterface">' \
                          '<Characteristic name="CharacStr" state="init"><str /></Characteristic>' \
                          '<Characteristic name="CharacInt" state="run"><int>10</int></Characteristic>' \
                          '</ObjectState>'
    xml_node = fromstring(objectstate_xml_str)

    object_state = mock_environment.create_base_object(name='', xml_node=xml_node)
    object_state.from_xml(mock_environment, xml_node)

    assert object_state.state == 'init'
    assert object_state.initializer == mock_environment.game.initializers['ObjStInitializerInterface']
    assert len(object_state.characteristics) == 2
    assert object_state.characteristics['CharacStr'].value is None
    assert object_state.characteristics['CharacStr'].value_type == str
    assert object_state.characteristics['CharacInt'].value == 1
    assert object_state.characteristics['CharacInt'].value_type == int
    assert object_state.characteristics['CharacInt'].state == 'run'
    assert object_state.characteristics['CharacStr'].state == 'init'


def test_from_xml_state_all_charac_run_should_run(mock_environment):
    objectstate_xml_str = '<ObjectState name="state" state="run">' \
                          '<Characteristic name="Step0" state="run"><int>0</int></Characteristic>' \
                          '<Characteristic name="Charac1" state="run"><str>test</str></Characteristic>' \
                          '</ObjectState>'
    xml_node = fromstring(objectstate_xml_str)

    object_state = mock_environment.create_base_object(name='', xml_node=xml_node)
    object_state.from_xml(mock_environment, xml_node)

    assert object_state.state == 'run'


def test_from_xml_state_one_charac_init_should_init(mock_environment):
    objectstate_xml_str = '<ObjectState name="state">' \
                          '<Characteristic name="Step0" state="init" initializer="initializer_interface_int">' \
                          '<int />' \
                          '</Characteristic>' \
                          '<Characteristic name="Charac1" state="run"><str>test</str></Characteristic>' \
                          '</ObjectState>'
    xml_node = fromstring(objectstate_xml_str)

    object_state = mock_environment.create_base_object(name='', xml_node=xml_node)
    object_state.from_xml(mock_environment, xml_node)

    assert object_state.state == 'init'


def test_id_str(mock_environment):
    objectstate_xml_str = '<ObjectState name="state">' \
                          '<Characteristic name="Step0"><int>0</int></Characteristic>' \
                          '<Characteristic name="Charac1"><str>test</str></Characteristic>' \
                          '</ObjectState>'
    xml_node = fromstring(objectstate_xml_str)

    object_state = mock_environment.create_base_object(name='', xml_node=xml_node)
    object_state.from_xml(mock_environment, xml_node)

    id_str = object_state.id_str()

    assert id_str == 'stateCharac1testStep00'
