from typing import Optional

from xml.etree.ElementTree import Element

from pymasep.common.base_object import BaseObject
from pymasep.common.exception import CreationException


class Object(BaseObject):
    """
    Object inside the environment

    :param environment: an Environment instance
    :param name: name of the Object.
    :param xml_node: xml used to create the object.
    :param template: template of the object. Needed if no xml_node.
    :param parent: parent object (see objects hierarchy)

    :raise pymasep.common.exception.CreationException: if no xml or no template is present
    """

    def __init__(self, environment,
                 name: Optional[str] = None,
                 xml_node: Optional[Element] = None,
                 template=None,
                 parent: Optional[BaseObject] = None):
        super().__init__(environment=environment, name=name, template=template, xml_node=xml_node, parent=parent)
        self.object_state = None
        if xml_node is None:
            self.object_state = self.environment.create_object(name='state',
                                                               template=template.subclass_templates[0],
                                                               parent=self)

    @BaseObject.state.getter
    def state(self) -> str:
        result = self.object_state.state
        return result

    def id_str(self) -> str:
        """
        Create an str unique ID of the Object.
        Depends on the content of the base object and the id_str of all characteristics in object_state

        :return: the string unique ID
        """
        return super().id_str() + self.object_state.id_str()

    def to_xml(self) -> Element:
        """
        Transform the Object to xml with ObjectState content
        See :ref:`serialization` for more information.

        :return xml version of the ObjectState
        """
        result = super().to_xml()
        obj_state = self.object_state.to_xml()
        result.append(obj_state)
        return result

    def from_xml(self, environment, xml_node: Element) -> None:
        """
        Transform a xml to an Object. The instance must be created before (with __init__(), passing the xml_node).
        See :ref:`serialization` for more information.

        :param environment: the environment where the Object is created.
        :param xml_node: the xml node containing .

        :raise  pymasep.common.exception.CreationException: SubElement must be present if no initializer not present.
                                  At creation, Initializer must be present for all characteristics
                                  if initializer is not present for ObjectState
        """

        def create_object_state_from_template(template_name):
            object_tmpl = self.environment.game.templates[template_name]
            for object_state_tmpl in object_tmpl.subclass_templates:
                self.object_state = self.environment.create_object(name='state',
                                                                   template=object_state_tmpl,
                                                                   parent=self)

        def apply_initializer(initializer_name):
            initializer = self.environment.game.initializers[initializer_name]
            initializer.apply(self)

        super().from_xml(environment=environment, xml_node=xml_node)
        if 'state' not in xml_node.attrib:
            if len(xml_node) != 0:
                if 'template' in xml_node.attrib:
                    create_object_state_from_template(xml_node.attrib['template'])
                for object_state_xml in xml_node:
                    # Object handle only ObjectState
                    if object_state_xml.tag == 'ObjectState':
                        object_state = self.environment.create_object(name='',
                                                                      xml_node=object_state_xml,
                                                                      parent=self)
                        if not self.object_state:
                            self.object_state = object_state
                        else:
                            # merge all characteristics.
                            for c in object_state.characteristics.values():
                                self.object_state.add_characteristic(c)
                if 'initializer' in xml_node.attrib:
                    apply_initializer(xml_node.attrib['initializer'])
            else:
                if 'initializer' not in xml_node.attrib:
                    raise CreationException('SubElement must be present if no initializer not present')
                else:
                    if 'template' in xml_node.attrib:
                        create_object_state_from_template(xml_node.attrib['template'])
                    apply_initializer(xml_node.attrib['initializer'])
            for charac in [c for c in self.object_state.characteristics.values() if c.state is None]:
                raise CreationException('Missing initializer for ' + charac.name + ' characteristic')
        else:
            for object_state_xml in xml_node:
                # Object handle only ObjectState
                if object_state_xml.tag == 'ObjectState':
                    object_state = self.environment.create_object(name='',
                                                                  xml_node=object_state_xml,
                                                                  parent=self)
                    self.object_state = object_state
                # if 'initializer' in xml_node.attrib:
                #     apply_initializer(xml_node.attrib['initializer'])
