from symbols.pddl.operator import TypedOperator
from symbols.pddl.predicate import Predicate

from symbols.transfer.kb import KnowledgeBase


class TypedPDDL:
    def __init__(self, task_id, domain_name, kb: KnowledgeBase):
        self.name = domain_name
        self.types = list()
        self.vocabulary = [Predicate('notfailed')]
        self._operators = list()
        self._strips = True
        self._kb = kb
        self._task_id = task_id
        self.grounded_objects = dict()
        self.parent_types = dict()
        self.probabilistic = False

    def get_parent_type(self, type):
        if type in self.parent_types:
            return self.parent_types[type]
        return None

    def is_ambiguous(self, object):
        # check if the object is one of many. Either it is not its own class, or something else has the same class!
        type = self._kb.get_type(self._task_id, object)
        if type != object:
            return True
        n_objects = self._kb.n_objects(self._task_id)
        for i in range(n_objects):
            if i == object:
                continue
            if self._kb.get_type(self._task_id, i) == type:
                return True  # there's another object that has the same class as this one!
        return False

    def object_type(self, object):
        return self._kb.get_type(self._task_id, object)

    def type_name(self, object):

        if object in self.grounded_objects:
            return self.grounded_objects[object]
        type = self.object_type(object)
        return 'type{}'.format(type)

    def set_types(self, types):
        self.types = types

    def add_predicate(self, predicate: Predicate):
        self.vocabulary.append(predicate)

    def add_operator(self, operator: TypedOperator):
        self._operators.append(operator)

    def operators(self):
        return self._operators

    def add_grounded_type(self, object, old_type, new_type):
        self.grounded_objects[object] = new_type
        self.parent_types[new_type] = old_type

    def __str__(self):

        description = "; Automatically generated " + self.name + " domain PPDDL file.\n"
        description += "(define (domain " + self.name + ")\n"
        description += "\t(:requirements{}{} :fluents :typing)\n".format(' :strips' if self._strips else '',
                                                                ' :probabilistic-effects' if self.probabilistic else '')

        types = '\n\t\t\t'.join(['{} - object'.format(type) for type in self.types])
        for child, parent in self.parent_types.items():
            types += '\n\t\t\t{} - {}'.format(child, parent)

        description += "\t(:types {}\n\t)\n".format(types)
        description += "\t(:functions (id ?x))\n" # for object identifiers!

        description += "\t(:predicates\n"
        for predicate in self.vocabulary:
            description += ("\t\t(" + str(predicate) + ")\n")
        description += '\t)\n'
        for i, operator in enumerate(self._operators):

            operator.probabilistic = self.probabilistic

            operator.id = i
            description += str(operator) + "\n\n"
        return description + ')'
