from typing import Optional
from xml.etree.ElementTree import ElementTree, Element

from pymasep.common.object import Object
from pymasep.utils import *


class Agent(Object):
    """
    Agent that act inside the environment

    :param environment: an Environment instance
    :param name: name of the Agent.
    :param xml_node: xml used to create the agent.
    :param template: template of the agent. 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: str = None,
                 template=None,
                 xml_node: Optional[ElementTree] = None,
                 parent=None) -> None:
        super().__init__(environment=environment, name=name, template=template, xml_node=xml_node, parent=parent)
        self.action = None
        self.observation = None
        self.reward = None
        self.controller = None
        self.control = None
        self.intention = None

    def choose_action(self) -> None:
        """
        choose the next action of the agent using its controller
        """
        self.action = self.controller.action_choice(observation=self.observation,
                                                    agent=self)

    def set_observation_reward(self, observation, reward: Optional[Any] = None) -> None:
        self.observation = observation
        self.reward = reward
        self.controller.on_observe(observation, reward)

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

        :return: the string unique ID
        """
        id_intention = ''
        if self.intention:
            id_intention = self.intention.id_str()
        return super().id_str() + id_intention

    def to_xml(self) -> Element:
        """
        Transform the Agent to xml. See :ref:`serialization` for more information.
        :return:  xml version of the Agent
        """
        result = super().to_xml()
        result.set('controller', classname(self.controller))
        if self.control is not None:
            result.set('control', self.control)
        if self.intention is not None:
            result.append(self.intention.to_xml())
        return result

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

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

        :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
        """
        super().from_xml(environment, xml_node)
        if 'control' in xml_node.attrib:
            self.control = xml_node.attrib['control']
        for object_state_xml in xml_node:
            # Agent handle only Intention
            if object_state_xml.tag == 'Intention':
                if not self.intention:
                    self.intention = self.environment.create_object(name='', xml_node=object_state_xml, parent=self)
