from xml.etree.ElementTree import tostring, fromstring

import pytest
import pymasep as pm
from pymasep.circular_head_list import CircularHeadList


def test_init(mock_environment):
    expected_name = 'Charac1'

    characteristic = pm.common.Characteristic(environment=mock_environment, name=expected_name,
                                              template=mock_environment.game.templates['CharacInt'])

    assert isinstance(characteristic, pm.common.BaseObject)
    assert isinstance(characteristic, pm.common.Characteristic)
    assert characteristic.environment == mock_environment
    assert characteristic.name == expected_name
    assert characteristic.value_type == int
    assert characteristic.value is None


def test_value(mock_environment):
    characteristic1 = pm.common.Characteristic(environment=mock_environment, name='Charact1',
                                               template=mock_environment.game.templates['CharacInt'])
    characteristic2 = pm.common.Characteristic(environment=mock_environment, name='Charact2',
                                               template=mock_environment.game.templates['CharacStr'])
    characteristic1.value = 1
    characteristic2.value = 'test'

    assert type(characteristic1.value) == mock_environment.game.templates['CharacInt'].value_type
    assert type(characteristic2.value) == mock_environment.game.templates['CharacStr'].value_type

    try:
        characteristic1.value = 'test2'
    except TypeError as e:
        assert str(e) == "value test2 is not set to the correct type <class 'int'>"
    else:
        assert False


@pytest.mark.parametrize('template_name, value, expected_xml_str',
                         [('CharacInt', 1,
                           '<Characteristic name="Charac1" state="run"><int>1</int></Characteristic>'),
                          ('CharacStr', "test",
                           '<Characteristic name="Charac1" state="run"><str>test</str></Characteristic>'),
                          ('CharacTuple', (10, 11),
                           '<Characteristic name="Charac1" state="run">'
                           '<tuple><item><int>10</int></item><item><int>11</int></item></tuple>'
                           '</Characteristic>')])
def test_to_xml_value_transformed(mock_environment, template_name, value, expected_xml_str):
    charac = pm.common.Characteristic(environment=mock_environment, name='Charac1',
                                      template=mock_environment.game.templates[template_name])
    charac.value = value

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

    assert xml_str_result == expected_xml_str


def test_to_xml_init(mock_environment):
    expected_xml_str = '<Characteristic name="Charac1" state="init"><int /></Characteristic>'
    charac = pm.common.Characteristic(environment=mock_environment, name='Charac1',
                                      template=mock_environment.game.templates['CharacInt'])
    charac.state = 'init'

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

    assert xml_str_result == expected_xml_str


def test_to_xml_init_with_initializer(mock_environment):
    expected_xml_str = '<Characteristic name="Charac1" state="init" initializer="initializer_interface_int">' \
                       '<int />' \
                       '</Characteristic>'
    charac = pm.common.Characteristic(environment=mock_environment, name='Charac1',
                                      template=mock_environment.game.templates['CharacInt'])
    charac.state = 'init'
    charac.initializer = mock_environment.game.initializers['initializer_interface_int']

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

    assert xml_str_result == expected_xml_str


def test_from_xml_string_value_creation(mock_environment):
    charac_xml_str = '<Characteristic name="Charac1"><str>test</str></Characteristic>'
    xml_node = fromstring(charac_xml_str)
    charac = mock_environment.create_base_object(name='', xml_node=xml_node)

    charac.from_xml(environment=mock_environment, xml_node=xml_node)

    assert charac.value == 'test'
    assert isinstance(charac, pm.common.Characteristic)
    assert charac.template is None
    assert charac.initializer is None
    assert charac.nature is None
    assert charac.state == 'run'
    assert charac.value_type == str


def test_from_xml_int_value_creation(mock_environment):
    charac_xml_str = '<Characteristic name="CharacInt"><int>10</int></Characteristic>'
    xml_node = fromstring(charac_xml_str)
    charac = mock_environment.create_base_object(name='', xml_node=xml_node)

    charac.from_xml(environment=mock_environment, xml_node=xml_node)

    assert charac.name == 'CharacInt'
    assert isinstance(charac.value, int)
    assert charac.value == 10
    assert charac.template is None
    assert charac.initializer is None
    assert charac.nature is None
    assert charac.state == 'run'
    assert charac.value_type == int


def test_from_xml_tuple_value_creation(mock_environment):
    charac_xml_str = '<Characteristic name="CharacTuple">' \
                     '<tuple><item><int>10</int></item><item><int>11</int></item></tuple>' \
                     '</Characteristic>'
    xml_node = fromstring(charac_xml_str)
    charac = mock_environment.create_base_object(name='', xml_node=xml_node)

    charac.from_xml(environment=mock_environment, xml_node=xml_node)

    assert charac.name == 'CharacTuple'
    assert isinstance(charac.value, tuple)
    assert charac.value == (10, 11)
    assert charac.template is None
    assert charac.initializer is None
    assert charac.nature is None
    assert charac.state == 'run'
    assert charac.value_type == tuple


def test_from_xml_circular_head_list_value_creation(mock_environment):
    charac_xml_str = '<Characteristic name="CharacCHL" state="run">' \
                     '<CircularHeadList>' \
                     '<data>' \
                     '<list><item><str>A</str></item><item><str>B</str></item></list>' \
                     '</data>' \
                     '<head_idx>1</head_idx>' \
                     '</CircularHeadList>' \
                     '</Characteristic>'
    xml_node = fromstring(charac_xml_str)
    charac = mock_environment.create_base_object(name='', xml_node=xml_node)

    charac.from_xml(environment=mock_environment, xml_node=xml_node)

    assert charac.name == 'CharacCHL'
    assert isinstance(charac.value, CircularHeadList)
    assert charac.value.data == ['A', 'B']
    assert charac.state == 'run'
    assert charac.value_type == CircularHeadList


def test_from_xml_initializer_fixed_value_creation(mock_environment):
    charac_xml_str = '<Characteristic name="Charac1" initializer="init_fixed_int" />'
    xml_node = fromstring(charac_xml_str)
    charac = mock_environment.create_base_object(name='', xml_node=xml_node)

    charac.from_xml(environment=mock_environment, xml_node=xml_node)

    assert charac.name == 'Charac1'
    assert charac.value == 1
    assert charac.template is None
    assert charac.initializer is None
    assert charac.state == 'run'
    assert charac.value_type == int


def test_from_xml_int_value_and_initializer_creation(mock_environment):
    charac_xml_str = '<Characteristic name="Charac1" initializer="init_fixed_int"><int>10</int></Characteristic>'
    xml_node = fromstring(charac_xml_str)
    charac = mock_environment.create_base_object(name='', xml_node=xml_node)

    charac.from_xml(environment=mock_environment, xml_node=xml_node)

    assert charac.name == 'Charac1'
    assert isinstance(charac.value, int)
    assert charac.value == 10
    assert charac.template is None
    assert charac.initializer is None
    assert charac.state == 'run'
    assert charac.value_type == int


def test_from_xml_template_no_initializer_nor_subelement_creation(mock_environment):
    base_xml_str = '<Characteristic name="Charac1" template="CharacInt" />'
    xml_node = fromstring(base_xml_str)
    base_object = mock_environment.create_base_object(name='', xml_node=xml_node)

    try:
        base_object.from_xml(environment=mock_environment, xml_node=xml_node)
    except pm.common.exception.CreationException as e:
        assert str(e) == 'Initilializer or SubElement must be present.'
    else:
        assert False


def test_from_xml_initializer_interface_creation(mock_environment):
    charac_xml_str = '<Characteristic name="Charac1" initializer="initializer_interface_int" />'
    xml_node = fromstring(charac_xml_str)
    charac = mock_environment.create_base_object(name='', xml_node=xml_node)

    charac.from_xml(environment=mock_environment, xml_node=xml_node)

    assert charac.value is None
    assert charac.state == 'init'
    assert charac.name == 'Charac1'
    assert charac.template is None
    assert charac.initializer == mock_environment.game.initializers['initializer_interface_int']
    assert charac.value_type == int


def test_from_xml_state_init_with_nothing_init(mock_environment):
    charac_xml_str = '<Characteristic name="Charac1" state="init"><int /></Characteristic>'
    xml_node = fromstring(charac_xml_str)
    charac = mock_environment.create_base_object(name='', xml_node=xml_node)

    charac.from_xml(environment=mock_environment, xml_node=xml_node)

    assert charac.state == 'init'
    assert charac.value_type == int
    assert charac.value is None
    assert charac.template is None
    assert charac.initializer is None


def test_from_xml_str_value_state_init_should_stay_init(mock_environment):
    charac_xml_str = '<Characteristic name="CharacInt" state="init"><str>test</str></Characteristic>'
    xml_node = fromstring(charac_xml_str)
    charac = mock_environment.create_base_object(name='', xml_node=xml_node)

    try:
        charac.from_xml(environment=mock_environment, xml_node=xml_node)
    except pm.common.exception.CreationException as e:
        assert str(e) == 'SubElement value must be None.'
    else:
        assert False


def test_from_xml_initializer_interface_with_state_init(mock_environment):
    charac_xml_str = '<Characteristic name="Charac1" state="init" initializer="initializer_interface_int" >' \
                     '<int />' \
                     '</Characteristic>'
    xml_node = fromstring(charac_xml_str)
    charac = mock_environment.create_base_object(name='', xml_node=xml_node)

    charac.from_xml(environment=mock_environment, xml_node=xml_node)

    assert charac.value is None
    assert charac.state == 'init'
    assert charac.name == 'Charac1'
    assert charac.template is None
    assert charac.initializer == mock_environment.game.initializers['initializer_interface_int']
    assert charac.value_type == int


def test_from_xml_initializer_not_interface_with_state_init(mock_environment):
    charac_xml_str = '<Characteristic name="CharacInt" state="init" ' \
                     'template="CharacInt" initializer="init_fixed_int" />'
    xml_node = fromstring(charac_xml_str)
    charac = mock_environment.create_base_object(name='', xml_node=xml_node)

    try:
        charac.from_xml(environment=mock_environment, xml_node=xml_node)
    except pm.common.exception.CreationException as e:
        assert str(e) == 'The initializer must be an Interface Initializer'
    else:
        assert False


def test_from_xml_initializer_with_state_run_template(mock_environment):
    charac_xml_str = '<Characteristic name="Charac1" state="run">' \
                     '<int>5</int>' \
                     '</Characteristic>'
    xml_node = fromstring(charac_xml_str)
    charac = mock_environment.create_base_object(name='', xml_node=xml_node)

    charac.from_xml(environment=mock_environment, xml_node=xml_node)

    assert charac.name == 'Charac1'
    assert charac.value == 5
    assert charac.value_type == int
    assert isinstance(charac, pm.common.Characteristic)
    assert charac.state == 'run'
    assert charac.template is None
    assert charac.initializer is None


def test_from_xml_initializer_with_state_run_template_vale(mock_environment):
    charac_xml_str = '<Characteristic name="Charac1" template="CharacInt" ' \
                     'state="run" initializer="init_fixed_int">' \
                     '<int>5</int>' \
                     '</Characteristic>'
    xml_node = fromstring(charac_xml_str)
    charac = mock_environment.create_base_object(name='', xml_node=xml_node)

    try:
        charac.from_xml(environment=mock_environment, xml_node=xml_node)
    except pm.common.exception.CreationException as e:
        assert str(e) == 'In run mode, no template or initializer allowed'
    else:
        assert False


def test_id_str(mock_environment):
    charac_xml_str = '<Characteristic name="CharacInt"><int>10</int></Characteristic>'
    xml_node = fromstring(charac_xml_str)
    charac = mock_environment.create_base_object(name='', xml_node=xml_node)

    charac.from_xml(environment=mock_environment, xml_node=xml_node)

    id_str = charac.id_str()

    assert id_str == 'CharacInt10'
