import copy

from tala.model import move
from tala.model.entity import Entity
from tala.model import stack
from tala.model import plan_item
from tala.model import ontology
from tala.model.domain import Domain
from tala.model import condition
from tala.model.hint import Hint
from tala.model.action import Action
from tala.model.ask_feature import AskFeature
from tala.model.plan import Plan
from tala.model import sort
from tala.model.sort import Sort, CustomSort
from tala.model.predicate import Predicate
from tala.model.individual import No, Yes, Individual, NegativeIndividual
from tala.model.question import Question, WhQuestion, YesNoQuestion, ConsequentQuestion, AltQuestion, \
    KnowledgePreconditionQuestion
from tala.model.lambda_abstraction import LambdaAbstractedProposition, \
    LambdaAbstractedImplicationPropositionForConsequent, LambdaAbstractedGoalProposition, \
    LambdaAbstractedPredicateProposition
from tala.model.proposition import Proposition, GoalProposition, PropositionSet, PredicateProposition, \
    ResolvednessProposition, PrereportProposition, ImplicationProposition, UnderstandingProposition, \
    KnowledgePreconditionProposition, PreconfirmationProposition, RejectedPropositions, ServiceResultProposition, \
    ActionStatusProposition, ServiceActionStartedProposition, ServiceActionTerminatedProposition, \
    NumberOfAlternativesProposition, QuestionStatusProposition, MuteProposition, UnmuteProposition, QuitProposition
from tala.model.polarity import Polarity
from tala.model.goal import Goal, PerformGoal, ResolveGoal, HandleGoal
from tala.model.set import Set
from tala.model.openqueue import OpenQueue
from tala.model.date_time import DateTime
from tala.model.person_name import PersonName
from tala.model.service_action_outcome import SuccessfulServiceAction, FailedServiceAction
from tala.model.action_status import ActionStatus, Aborted, Done
from tala.model.ddd import DDD
from tala.ddd.services import service_interface
from tala.ddd.services.service_interface import ServiceInterface, ServiceActionInterface


class JSONParseFailure(Exception):
    pass


def preprocess_json_dict(json_dict_original):
    json_dict = copy.copy(json_dict_original)
    for float_key in ["perception_confidence", "understanding_confidence"]:
        if float_key in json_dict:
            try:
                json_dict[float_key] = float(json_dict[float_key])
            except TypeError:
                pass
    return json_dict


def create_sort(definition):
    sort_name = definition["_name"]
    if sort_name in sort.BUILTIN_SORTS:
        if sort_name == sort.DATETIME:
            return sort.DateTimeSort()
        if sort_name == sort.REAL:
            return sort.RealSort()
        if sort_name == sort.PERSON_NAME:
            return sort.PersonNameSort()
        if sort_name == sort.INTEGER:
            return sort.IntegerSort()
        if sort_name == sort.BOOLEAN:
            return sort.BooleanSort()
        if sort_name == sort.STRING:
            return sort.StringSort()
        if sort_name == sort.DOMAIN:
            return sort.DomainSort()
        if sort_name == sort.IMAGE:
            return sort.ImageSort()
        if sort_name == sort.WEBVIEW:
            return sort.WebviewSort()

    ontology_name = definition.get("_ontology_name")
    if ontology_name:
        return CustomSort(ontology_name, definition["_name"], definition["_dynamic"])
    return Sort(definition["_name"], definition["_dynamic"])


def create_predicate(name, definition):
    return Predicate(
        definition["_ontology_name"], name, create_sort(definition["sort"]), definition["feature_of_name"],
        definition["_multiple_instances"]
    )


def create_individual(individual, sort):
    def extract(string, prefix, suffix):
        if value.startswith(prefix) and value.endswith(suffix):
            return value[len(prefix):len(value) - len(suffix)]

    def extract_datetime_value():
        return extract(value, "datetime(", ")")

    def extract_person_name_value():
        return extract(value, "person_name(", ")")

    sort = create_sort(sort)
    value = individual
    if sort.is_datetime_sort():
        value = DateTime(extract_datetime_value())
    if sort.is_real_sort():
        value = float(value)
    if sort.is_person_name_sort():
        value = PersonName(extract_person_name_value())
    if sort.is_integer_sort():
        value = int(value)
    return (value, sort)


class JSONOntologyParser():
    def parse_ontology(self, ontology_as_json):
        if ontology_as_json["semantic_object_type"] == ontology.SEMANTIC_OBJECT_TYPE:
            name = ontology_as_json["_name"]
            sorts = self.parse_ontology_sorts(ontology_as_json["_sorts"])
            predicates = self.parse_ontology_predicates(ontology_as_json["_predicates"])
            individuals = self.parse_ontology_individuals(ontology_as_json["_individuals"])
            actions = self.parse_ontology_actions(ontology_as_json["_actions"], name)
            return ontology.Ontology(name, sorts, predicates, individuals, actions)
        raise JSONParseFailure(f"Failed to parse {ontology_as_json} as an ontology.")

    def parse_ontology_sorts(self, sorts_as_json):
        return set([create_sort(sort) for sort in sorts_as_json])

    def parse_ontology_predicates(self, predicates_as_json):
        return set([create_predicate(name, predicates_as_json[name]) for name in predicates_as_json])

    def parse_ontology_individuals(self, individuals_as_json):
        individuals = {}
        for individual_as_json in individuals_as_json:
            (value, sort) = create_individual(individual_as_json, individuals_as_json[individual_as_json])
            individuals[value] = sort
        return individuals

    def parse_ontology_actions(self, actions_as_json, ontology_name):
        return [action for action in actions_as_json["set"]]


class JSONDDDParser():
    def parse(self, ddd_as_json):
        name = ddd_as_json["ddd_name"]
        ontology_parser = JSONOntologyParser()
        ontology = ontology_parser.parse_ontology(ddd_as_json["ontology"])
        domain_parser = JSONDomainParser(ontology)
        domain = domain_parser.parse(ddd_as_json["domain"])
        service_interface_parser = JSONServiceInterfaceParser()
        service_interface = service_interface_parser.parse(ddd_as_json["service_interface"])
        return DDD(name, ontology, domain, service_interface)


class JSONDomainParser():
    def __init__(self, ontology=None):
        self.ontology = ontology
        self.parser = NonCheckingJSONParser()

    def parse(self, json_dict):
        ddd_name = json_dict["ddd_name"]
        domain_name = json_dict["name"]
        if self.ontology:
            self.parser = CheckingJSONParser(ddd_name, self.ontology, domain_name)
        default_questions = self.parse_default_questions(json_dict["default_questions"])
        parameters = self.parse_parameters(json_dict["parameters"])
        plans = self.parse_plans(json_dict["plans"])
        validators = self.parse_validators(json_dict["validators"])
        dependencies = self.parse_serialized_dict(json_dict.get("dependencies", {}))
        queries = self.parse_serialized_query_list(json_dict["queries"])
        return Domain(
            ddd_name, domain_name, self.ontology, plans, default_questions, parameters, validators, dependencies,
            queries
        )

    def parse_default_questions(self, default_questions):
        return [self._parse_default_question(question) for question in default_questions]

    def _parse_default_question(self, question):
        return self.parser.parse(question)

    def parse_parameters(self, parameters):
        compiled_parameters = {}
        for pair in parameters:
            key_question = self.parser.parse(pair["key"])
            values = pair["value"]
            compiled_parameters[key_question] = self._parse_parameters_for_entry(values)
        return compiled_parameters

    def _parse_parameters_for_entry(self, parameters):
        return {name: self._parse_parameter(parameters, name) for name in parameters}

    def _parse_parameter(self, parameters, name):
        def is_questions_parameter():
            return name in ["related_information"]

        def is_question_parameter():
            return name in ["service_query", "default"]

        def is_int_parameter():
            return name in ["max_spoken_alts", "max_reported_hit_count"]

        def is_boolean_parameter():
            return name in ["always_ground", "always_relevant"]

        if name == "ask_features":
            return [self.parse_ask_feature(feature) for feature in parameters[name]]
        if name == "hints":
            return [self.parse_hint(hint) for hint in parameters[name]]
        if name in ["allow_goal_accommodation", "verbalize", "incremental"]:
            return parameters[name]
        if name in ["on_zero_hits_action", "on_too_many_hits_action"]:
            return self.parser.parse_action(parameters[name])
        if is_int_parameter():
            return int(parameters[name])
        if name in ["graphical_type", "source"]:
            return parameters[name]
        if name == "alts":
            return PropositionSet([self.parser.parse(proposition) for proposition in parameters[name]["_propositions"]])
        if name == "background":
            return [self.parser.parse_predicate(predicate) for predicate in parameters[name]]
        if is_questions_parameter():
            return [self.parser.parse_question(question) for question in parameters[name]]
        if is_question_parameter():
            return self.parser.parse_question(parameters[name])
        if is_boolean_parameter():
            return bool(parameters[name])

        raise JSONParseFailure("no support for parameter", name, parameters[name], ".")
        return parameters[name]

    def parse_ask_feature(self, feature):
        return AskFeature(feature["name"], feature["kpq"])

    def parse_hint(self, hint):
        return Hint([self.parser.parse_plan_item(item) for item in hint["_plan"]])

    def parse_plans(self, plans):
        def is_builtin(goal):
            return goal["_goal_type"] == "PERFORM_GOAL" and goal["_content"]["value"] in ["top", "up"]

        return [self.parse_goal(plans[goal]) for goal in plans]

    def parse_goal(self, goal):
        result = {}
        BOOLEAN_VALUES = [
            "unrestricted_accommodation", "restart_on_completion", "reraise_on_resume", "accommodate_without_feedback"
        ]
        INTEGER_VALUES = ["max_answers"]
        STRING_VALUES = ["alternatives_predicate"]
        for entry in goal:
            if entry == "goal":
                result[entry] = self.parser.parse_goal(goal[entry])
            elif entry == "io_status":
                result[entry] = self.parse_io_status(goal[entry])
            elif entry == "plan":
                result[entry] = self.parse_plan(goal[entry])
            elif entry == "postconds":
                result[entry] = self.parse_postconds(goal[entry])
            elif entry == "postplan":
                result[entry] = self.parse_postplan(goal[entry])
            elif entry == "preferred":
                result[entry] = self.parse_preferred(goal[entry])
            elif entry == "superactions":
                result[entry] = self.parse_superactions(goal[entry])
            elif entry in BOOLEAN_VALUES:
                result[entry] = bool(goal[entry])
            elif entry in INTEGER_VALUES:
                result[entry] = int(goal[entry])
            elif entry in STRING_VALUES:
                result[entry] = str(goal[entry])
            else:
                raise JSONParseFailure("found unknown entry in goal when parsing:", entry, goal[entry])

        return result

    def parse_plan(self, plan):
        plan_items = [self.parser.parse_plan_item(item) for item in plan["stack"]]
        return Plan(reversed(plan_items))

    def parse_io_status(self, status):
        if status:
            return status
        return Domain.DEFAULT_IO_STATUS

    def parse_postconds(self, postconds):
        return [self.parse_postcond(postcond) for postcond in postconds]

    def parse_postcond(self, postcond):
        return self.parser.parse(postcond)

    def parse_postplan(self, postplan):
        try:
            return [self.parser.parse_plan_item(item) for item in postplan["stack"]]
        except TypeError:
            return [self.parser.parse_plan_item(item) for item in postplan]

    def parse_superactions(self, actions):
        return [self.parse_superaction(action) for action in actions]

    def parse_preferred(self, preference):
        try:
            return self.parser.parse_proposition(preference)
        except TypeError:
            return preference

    def parse_superaction(self, action):
        return self.parser.parse_action(action)

    def parse_validators(self, validators):
        return [self.parse_validator(validator) for validator in validators]

    def parse_validator(self, validator):
        def parse_propositions_list(predicates):
            return [self.parser.parse(predicate) for predicate in predicates]

        id = validator["validator"]
        valid_configurations = [
            parse_propositions_list(configuration) for configuration in validator["valid_configurations"]
        ]
        return {"validator": id, "valid_configurations": valid_configurations}

    def parse_serialized_dict(self, serialized_dict):
        result_dict = {}
        for pair in serialized_dict:
            key = self.parser.parse(pair["key"])
            value = pair["value"]
            result_dict[key] = self.parser.parse(value)
        return result_dict

    def parse_serialized_query_list(self, serialized_queries):
        def is_iterator(query_content):
            return "limit" in query_content

        def parse_iterator(iterator):
            result_dict = {}
            for key, value in iterator.items():
                if key in ["for_enumeration", "for_random_enumeration"]:
                    result_dict[key] = [self.parser.parse(item) for item in value]
                else:
                    result_dict[key] = value
            return result_dict

        result_list = []
        for query in serialized_queries:
            if is_iterator(query):
                query_dict = parse_iterator(query)
            else:
                query_dict = {}
                for entry, semantic_objects in query.items():
                    try:
                        query_dict[entry] = self.parser.parse(semantic_objects)
                    except AttributeError:
                        query_dict[entry] = [self.parser.parse(item) for item in semantic_objects]
            result_list.append(query_dict)
        return result_list


class JSONServiceInterfaceParser():
    def parse(self, json_dict):
        actions = self.parse_actions(json_dict["_actions"])
        queries = self.parse_queries(json_dict["_queries"])
        validators = self.parse_validators(json_dict["_validators"])
        return ServiceInterface(actions, queries, validators)

    def parse_actions(self, actions_dict):
        return [self.parse_action_interface(actions_dict[action_name]) for action_name in actions_dict]

    def parse_action_interface(self, action_dict):
        name = action_dict["_name"]
        target = self.parse_target(action_dict["_target"])
        service_parameters = self.parse_parameters(action_dict["_parameters"])
        failure_reasons = action_dict["_failure_reasons"]
        if action_dict.get("_audio_url_parameter"):
            audio_parameter = self.parse_audio_parameter(action_dict["_audio_url_parameter"])
            interface = service_interface.PlayAudioActionInterface(name, target, service_parameters, audio_parameter)
        else:
            interface = ServiceActionInterface(name, target, service_parameters, failure_reasons)
        return interface

    def parse_target(self, target_dict):
        if target_dict.get("target_type") == service_interface.DEVICE_MODULE_TARGET:
            return service_interface.DeviceModuleTarget(target_dict["_device"])
        if target_dict.get("target_type") == service_interface.FRONTEND_TARGET:
            return service_interface.FrontendTarget()
        if target_dict.get("target_type") == service_interface.HTTP_TARGET:
            endpoint = target_dict["_endpoint"]
            return service_interface.HttpTarget(endpoint)
        raise JSONParseFailure(f"Cannot parse as target: {target_dict}")

    def parse_parameters(self, parameter_dict):
        return [self.parse_parameter(parameter) for parameter in parameter_dict if parameter["_name"] != "audio_url"]

    def parse_parameter(self, parameter):
        name = parameter["_name"]
        format = parameter["_format"]
        is_optional = parameter["_is_optional"]
        return service_interface.ServiceParameter(name, format, is_optional)

    def parse_audio_parameter(self, parameter):
        return service_interface.AudioURLServiceParameter(parameter["_name"])

    def parse_queries(self, queries_dict):
        return [self.parse_query(query) for query in queries_dict.values()]

    def parse_query(self, query_dict):
        name = query_dict["_name"]
        target = self.parse_target(query_dict["_target"])
        parameters = self.parse_parameters(query_dict["_parameters"])
        return service_interface.ServiceQueryInterface(name, target, parameters)

    def parse_validators(self, validators_dict):
        return [self.parse_validator(validator) for validator in validators_dict.values()]

    def parse_validator(self, validator_dict):
        name = validator_dict["_name"]
        target = self.parse_target(validator_dict["_target"])
        parameters = self.parse_parameters(validator_dict["_parameters"])
        return service_interface.ServiceValidatorInterface(name, target, parameters)


class NonCheckingJSONParser():
    def parse(self, json_dict_unchecked):
        def is_condition():
            """ This is the template for extending the semantic object JSON
            with type information, which should be done for all semantic objects.
            """

            return json_dict.get("semantic_object_type") == condition.SEMANTIC_OBJECT_TYPE

        def is_plan_item():
            for item in plan_item.ALL_PLAN_ITEM_TYPES:
                if item in json_dict:
                    return True
            return False

        def is_individual():
            return "value" in json_dict and "sort" in json_dict

        def is_yes_no():
            return json_dict.get("semantic_object_type") == "yes/no"

        def is_move():
            return "move_type" in json_dict

        def is_action():
            return "value" in json_dict

        def is_goal():
            return "_goal_type" in json_dict

        def is_question():
            question_type = json_dict.get("_type", None)
            return question_type in Question.TYPES

        def is_predicate():
            return "name" in json_dict and "sort" in json_dict

        def is_lambda_abstracted_proposition():
            return json_dict.get("semantic_object_type") == "LambdaAbstractedProposition"

        def is_set():
            return "set" in json_dict

        def is_openqueue():
            return "openqueue" in json_dict

        def is_service_action_outcome():
            return "service_action_outcome" in json_dict

        def is_proposition():
            prop_type = json_dict.get("_type", None)
            return prop_type in Proposition.TYPES

        def is_proposition_set():
            return "_propositions" in json_dict

        def is_action_status():
            return "action_status" in json_dict

        json_dict = preprocess_json_dict(json_dict_unchecked)
        if is_condition():
            return self.parse_condition(json_dict)
        if is_plan_item():
            return self.parse_plan_item(json_dict)
        if is_individual():
            return self.parse_individual(json_dict)
        if is_set():
            return self.parse_set(json_dict["set"])
        if is_openqueue():
            return self.parse_openqueue(json_dict["openqueue"])
        if is_move():
            return self.parse_move(json_dict)
        if is_action():
            return self.parse_action(json_dict)
        if is_goal():
            return self.parse_goal(json_dict)
        if is_question():
            return self.parse_question(json_dict)
        if is_predicate():
            return self.parse_predicate(json_dict)
        if is_proposition_set():
            return self.parse_proposition_set(json_dict)
        if is_proposition():
            return self.parse_proposition(json_dict)
        if is_lambda_abstracted_proposition():
            return self.parse_lambda_abstraction(json_dict)
        if is_service_action_outcome():
            return self.parse_service_action_outcome(json_dict)
        if is_yes_no():
            if json_dict["instance"] == No.NO:
                return No()
            if json_dict["instance"] == Yes.YES:
                return Yes()
        if is_action_status():
            return self.parse_action_status(json_dict)
        raise JSONParseFailure(f"JSONParser cannot parse: {json_dict}")

    def parse_condition(self, condition_json):
        keys = ["_predicate", "_proposition"]
        for key in keys:
            if condition_json.get(key):
                argument = self.parse(condition_json[key])
                return condition.create_condition(condition_json["type"], argument)
        if condition_json.get("_query"):
            try:
                argument = self.parse(condition_json["_query"])
            except Exception:
                argument = condition_json["_query"]
            return condition.create_condition(condition_json["type"], argument)
        raise JSONParseFailure(f"Failed to parse {condition_json} as a condition.")

    def parse_plan_item(self, data):
        if plan_item.TYPE_FINDOUT in data:
            return self.parse_findout_plan_item(data)
        if plan_item.TYPE_RAISE in data:
            return self.parse_raise_plan_item(data)
        if plan_item.TYPE_RESPOND in data:
            return self.parse_respond_plan_item(data[plan_item.TYPE_RESPOND])
        if plan_item.TYPE_ASSUME in data:
            return self.parse_assume_plan_item(data[plan_item.TYPE_ASSUME])
        if plan_item.TYPE_ASSUME_ISSUE in data:
            return self.parse_assume_issue_plan_item(data)
        if plan_item.TYPE_ASSUME_SHARED in data:
            return self.parse_assume_shared_plan_item(data[plan_item.TYPE_ASSUME_SHARED])
        if plan_item.TYPE_BIND in data:
            return self.parse_bind_plan_item(data[plan_item.TYPE_BIND])
        if plan_item.TYPE_DO in data:
            return self.parse_do_plan_item(data[plan_item.TYPE_DO])
        if plan_item.TYPE_EMIT_ICM in data:
            return self.parse_emit_icm_plan_item(data[plan_item.TYPE_EMIT_ICM])
        if plan_item.TYPE_FORGET in data:
            return self.parse_forget_plan_item(data[plan_item.TYPE_FORGET])
        if plan_item.TYPE_FORGET_SHARED in data:
            return self.parse_forget_shared_plan_item(data[plan_item.TYPE_FORGET_SHARED])
        if plan_item.TYPE_FORGET_ALL in data:
            return self.parse_forget_all_plan_item(data[plan_item.TYPE_FORGET_ALL])
        if plan_item.TYPE_FORGET_ISSUE in data:
            return self.parse_forget_issue_plan_item(data[plan_item.TYPE_FORGET_ISSUE])
        if plan_item.TYPE_IF_THEN_ELSE in data:
            return self.parse_if_then_else_plan_item(data[plan_item.TYPE_IF_THEN_ELSE])
        if plan_item.TYPE_JUMPTO in data:
            return self.parse_jumpto_plan_item(data[plan_item.TYPE_JUMPTO])
        if plan_item.TYPE_INVOKE_SERVICE_QUERY in data:
            return self.parse_invoke_service_query_plan_item(data[plan_item.TYPE_INVOKE_SERVICE_QUERY])
        if plan_item.TYPE_INVOKE_DOMAIN_QUERY in data:
            return self.parse_invoke_domain_query_plan_item(data[plan_item.TYPE_INVOKE_DOMAIN_QUERY])
        if plan_item.TYPE_INVOKE_SERVICE_ACTION in data:
            return self.parse_invoke_service_action_plan_item(data[plan_item.TYPE_INVOKE_SERVICE_ACTION])
        if plan_item.TYPE_LOG in data:
            return self.parse_log_plan_item(data[plan_item.TYPE_LOG])
        if plan_item.TYPE_ACTION_PERFORMED in data:
            return self.parse_action_performed_plan_item(data[plan_item.TYPE_ACTION_PERFORMED])
        if plan_item.TYPE_ACTION_ABORTED in data:
            return self.parse_action_aborted_plan_item(data[plan_item.TYPE_ACTION_ABORTED])
        if plan_item.TYPE_CHANGE_DDD in data:
            return self.parse_change_ddd_plan_item(data[plan_item.TYPE_CHANGE_DDD])
        if plan_item.TYPE_GET_DONE in data:
            return self.parse_get_done_plan_item(data)
        if plan_item.TYPE_INVOKE_DOMAIN_QUERY in data:
            return self.parse_invoke_domain_query(data[plan_item.INVOKE_DOMAIN_QUERY])
        if plan_item.TYPE_END_TURN in data:
            return self.parse_end_turn_plan_item(data[plan_item.TYPE_END_TURN])
        if plan_item.TYPE_RESET_DOMAIN_QUERY in data:
            return self.parse_reset_domain_query(data[plan_item.TYPE_RESET_DOMAIN_QUERY])
        if plan_item.TYPE_ITERATE in data:
            return self.parse_iterate_plan_item(data[plan_item.TYPE_ITERATE])
        if plan_item.TYPE_ACTION_REPORT in data:
            return self.parse_action_report_plan_item(data[plan_item.TYPE_ACTION_REPORT])
        if plan_item.TYPE_GREET in data:
            return plan_item.Greet()
        if plan_item.TYPE_EMIT_MOVE in data:
            return self.parse_emit_move_plan_item(data)
        if plan_item.TYPE_QUESTION_REPORT in data:
            return self.parse_question_report_plan_item(data)
        if plan_item.TYPE_SERVICE_REPORT in data:
            return self.parse_service_report_plan_item(data)
        if plan_item.TYPE_RESPOND_TO_INSULT in data:
            return plan_item.RespondToInsult()
        if plan_item.TYPE_RESPOND_TO_THANK_YOU in data:
            return plan_item.RespondToThankYou()

        raise JSONParseFailure(f"PlanItem {data} not supported by json parser")

    def parse_findout_plan_item(self, data):
        allow_answer_from_pcom = data.get("allow_answer_from_pcom", False)
        domain_name = data.get("domain_name")
        question = self.parse_question(data[plan_item.TYPE_FINDOUT])
        return plan_item.Findout(domain_name, question, allow_answer_from_pcom)

    def parse_raise_plan_item(self, data):
        domain_name = data.get("domain_name")
        question = self.parse_question(data[plan_item.TYPE_RAISE])
        return plan_item.Raise(domain_name, question)

    def parse_respond_plan_item(self, data):
        question = self.parse_question(data)
        return plan_item.Respond(question)

    def parse_bind_plan_item(self, data):
        question = self.parse_question(data)
        return plan_item.Bind(question)

    def parse_do_plan_item(self, data):
        action = self.parse_action(data)
        return plan_item.Do(action)

    def parse_log_plan_item(self, data):
        message = data
        return plan_item.Log(message)

    def parse_action_performed_plan_item(self, data):
        return plan_item.GoalPerformed(data)

    def parse_action_aborted_plan_item(self, data):
        return plan_item.GoalAborted(data)

    def parse_change_ddd_plan_item(self, data):
        return plan_item.ChangeDDD(data)

    def parse_get_done_plan_item(self, data):
        action = self.parse_action(data[plan_item.TYPE_GET_DONE])
        try:
            step = int(data.get("step"))
        except TypeError:
            step = None

        return plan_item.GetDone(action, step)

    def parse_end_turn_plan_item(self, data):
        timeout = float(data)
        return plan_item.EndTurn(timeout)

    def parse_reset_domain_query(self, data):
        query = self.parse_question(data)
        return plan_item.ResetDomainQuery(query)

    def parse_iterate_plan_item(self, iterator_name):
        return plan_item.Iterate(iterator_name)

    def parse_action_report_plan_item(self, result_proposition):
        return plan_item.ActionReport(self.parse_proposition(result_proposition))

    def parse_emit_move_plan_item(self, data):
        prereport_move = self.parse_prereport_move(data[plan_item.TYPE_EMIT_MOVE])
        return plan_item.EmitMove(prereport_move)

    def parse_invoke_domain_query(self, data):
        issue = self.parse_question(data)
        return plan_item.InvokeDomainQuery(issue)

    def parse_forget_plan_item(self, data):
        if "_type" in data.keys():
            proposition = self.parse_proposition(data)
            return plan_item.Forget(proposition)
        else:
            predicate = self.parse_predicate(data)
            return plan_item.Forget(predicate)

    def parse_forget_shared_plan_item(self, data):
        if "_type" in data.keys():
            proposition = self.parse_proposition(data)
            return plan_item.ForgetShared(proposition)
        else:
            predicate = self.parse_predicate(data)
            return plan_item.ForgetShared(predicate)

    def parse_forget_all_plan_item(self, _data):
        return plan_item.ForgetAll()

    def parse_forget_issue_plan_item(self, data):
        question = self.parse_question(data)
        return plan_item.ForgetIssue(question)

    def parse_assume_plan_item(self, data):
        proposition = self.parse_proposition(data)
        return plan_item.Assume(proposition)

    def parse_assume_issue_plan_item(self, data):
        question = self.parse_question(data[plan_item.TYPE_ASSUME_ISSUE])
        return plan_item.AssumeIssue(question, data["insist"])

    def parse_assume_shared_plan_item(self, data):
        proposition = self.parse_proposition(data)
        return plan_item.AssumeShared(proposition)

    def parse_emit_icm_plan_item(self, data):
        icm = self.parse_move(data)
        return plan_item.EmitIcm(icm)

    def parse_jumpto_plan_item(self, data):
        goal = self.parse_goal(data)
        return plan_item.JumpTo(goal)

    def parse_if_then_else_plan_item(self, data):
        cond = self.parse(data["condition"])
        consequent = [self.parse(element) for element in data["consequent"]]
        alternative = [self.parse(element) for element in data["alternative"]]
        return plan_item.IfThenElse(cond, consequent, alternative)

    def parse_invoke_service_query_plan_item(self, data):
        issue = self.parse_question(data["issue"])
        min_results = int(data["min_results"])
        max_results = int(data["max_results"])
        return plan_item.InvokeServiceQuery(issue, min_results=min_results, max_results=max_results)

    def parse_invoke_domain_query_plan_item(self, data):
        issue = self.parse_question(data["issue"])
        min_results = int(data["min_results"])
        max_results = int(data["max_results"])
        return plan_item.InvokeDomainQuery(issue, min_results=min_results, max_results=max_results)

    def parse_invoke_service_action_plan_item(self, data):
        service_action = data["service_action"]
        preconfirm = data["preconfirm"]
        postconfirm = data["postconfirm"]
        downdate_plan = data["downdate_plan"]
        ontology_name = data["ontology"]
        return plan_item.InvokeServiceAction(ontology_name, service_action, preconfirm, postconfirm, downdate_plan)

    def parse_question_report_plan_item(self, data):
        result_proposition = self.parse_proposition(data[plan_item.TYPE_QUESTION_REPORT])
        return plan_item.QuestionReport(result_proposition)

    def parse_service_report_plan_item(self, data):
        result_proposition = self.parse_proposition(data[plan_item.TYPE_SERVICE_REPORT])
        return plan_item.ServiceReport(result_proposition)

    def parse_question(self, data):
        if data["_type"] == Question.TYPE_WH:
            lambda_abstraction = self.parse_lambda_abstraction(data["_content"])
            return WhQuestion(lambda_abstraction)
        if data["_type"] == Question.TYPE_KPQ:
            embedded_question = self.parse_question(data["_content"])
            return KnowledgePreconditionQuestion(embedded_question)
        if data["_type"] == Question.TYPE_YESNO:
            proposition = self.parse(data["_content"])
            return YesNoQuestion(proposition)
        if data["_type"] == Question.TYPE_ALT:
            p_set = self.parse_proposition_set(data["_content"])
            return AltQuestion(p_set)
        if data["_type"] == Question.TYPE_CONSEQUENT:
            lambda_abstraction = self.parse_lambda_abstraction(data["_content"])
            return ConsequentQuestion(lambda_abstraction)
        raise JSONParseFailure(f"Cannot parse {data} as question.")

    def parse_lambda_abstraction(self, content):
        if content["type_"] == LambdaAbstractedProposition.LAMBDA_ABSTRACTED_PREDICATE_PROPOSITION:
            return self._create_lambda_abstracted_predicate_proposition(content)

        if content["type_"] == LambdaAbstractedProposition.LAMBDA_ABSTRACTED_GOAL_PROPOSITION:
            return LambdaAbstractedGoalProposition()

        if content["type_"] == LambdaAbstractedProposition.LAMBDA_ABSTRACTED_IMPLICATION_PROPOSITION_FOR_CONSEQUENT:
            ontology_name = content["_ontology_name"]
            antecedent = self.parse_proposition(content["_antecedent"])
            predicate = self.parse_predicate(content["_consequent_predicate"])
            return LambdaAbstractedImplicationPropositionForConsequent(antecedent, predicate, ontology_name)
        raise JSONParseFailure(f"Cannot parse {content} as lambda abstraction.")

    def _create_lambda_abstracted_predicate_proposition(self, content):
        predicate = self.parse_predicate(content["predicate"])
        ontology_name = content["_ontology_name"]
        return LambdaAbstractedPredicateProposition(predicate, ontology_name)

    def parse_predicate(self, data):
        return create_predicate(data["name"], data)

    def parse_proposition_set(self, data):
        proposition_list = data["_propositions"]
        propositions = [self.parse_proposition(proposition) for proposition in proposition_list]
        polarity = data["_polarity"]
        return PropositionSet(propositions, polarity)

    def parse_set(self, data):
        result_set = Set([self.parse(element) for element in data])
        return result_set

    def parse_openqueue(self, input):
        front_content = [self.parse(item) for item in input["front_content"]]
        back_content = [self.parse(item) for item in input["back_content"]]
        unshifted_as_json = input["unshifted_content"]
        if unshifted_as_json:
            unshifted_content = [self.parse(item) for item in unshifted_as_json]
        else:
            unshifted_content = None
        return OpenQueue(front_content, back_content, unshifted_content)

    def parse_move(self, data):
        def get_float(string):
            if string is not None:
                return float(string)
            return None

        move_args = {
            "understanding_confidence": get_float(data.get("understanding_confidence")),
            "speaker": data.get("speaker"),
            "ddd_name": data.get("ddd"),
            "perception_confidence": get_float(data.get("perception_confidence"))
        }
        non_icm_args = {
            "modality": data.get("modality"),
            "weighted_understanding_confidence": get_float(data.get("weighted_understanding_confidence")),
            "utterance": data.get("utterance")
        } | move_args

        icm_args = {
            "polarity": data.get("polarity"),
        } | move_args

        if data["move_type"] == move.Move.ANSWER:
            return self.parse_answer_move(data, non_icm_args)
        if data["move_type"] == move.Move.REQUEST:
            return self.parse_request_move(data, non_icm_args)
        if data["move_type"] == move.Move.ASK:
            return self.parse_ask_move(data, non_icm_args)
        if data["move_type"] == move.ICMMove.ACC:
            return self.parse_acc_icm(data, icm_args)
        if data["move_type"] == move.ICMMove.ACCOMMODATE:
            return self.parse_accommodate_icm(data, icm_args)
        if data["move_type"] == move.ICMMove.RESUME:
            return self.parse_resume_icm(data, icm_args)
        if data["move_type"] == move.ICMMove.RERAISE:
            return self.parse_reraise_icm(data, icm_args)
        if data["move_type"] == move.ICMMove.PER:
            return self.parse_per_icm(data, icm_args)
        if data["move_type"] == move.ICMMove.UND:
            return self.parse_und_icm(data, icm_args)
        if data["move_type"] == move.ICMMove.SEM:
            return self.parse_sem_icm(data, icm_args)
        if data["move_type"] == move.ICMMove.LOADPLAN:
            return self.parse_loadplan_icm(data, icm_args)
        if data["move_type"] == move.ICMMove.CARDINAL_SEQUENCING:
            return self.parse_cardinal_sequencing_icm(data, icm_args)
        if data["move_type"] == move.ICMMove.REPORT_INFERENCE:
            return self.parse_report_inference_icm(data, icm_args)
        if data["move_type"] == move.Move.GREET:
            return self.parse_greet_move(non_icm_args)
        if data["move_type"] == move.Move.INSULT:
            return self.parse_insult_move(non_icm_args)
        if data["move_type"] == move.Move.INSULT_RESPONSE:
            return self.parse_insult_response_move(non_icm_args)
        if data["move_type"] == move.Move.MUTE:
            return self.parse_mute_move(non_icm_args)
        if data["move_type"] == move.Move.UNMUTE:
            return self.parse_unmute_move(non_icm_args)
        if data["move_type"] == move.Move.PREREPORT:
            return self.parse_prereport_move(data)
        if data["move_type"] == move.Move.REPORT:
            return self.parse_report_move(data, non_icm_args)
        if data["move_type"] == move.Move.QUIT:
            return self.parse_quit_move(non_icm_args)
        if data["move_type"] == move.Move.THANK_YOU:
            return self.parse_thank_you_move(non_icm_args)
        if data["move_type"] == move.Move.THANK_YOU_RESPONSE:
            return self.parse_thank_you_response_move(non_icm_args)
        raise JSONParseFailure(f"cannot parse {data} as move.")

    def parse_action(self, data):
        action_name = data["value"]
        ontology_name = data["_ontology_name"]
        return Action(action_name, ontology_name)

    def parse_answer_move(self, data, kwargs):
        content = self.parse(data["content"])
        return move.AnswerMove(content, **kwargs)

    def parse_request_move(self, data, kwargs):
        action = self.parse_action(data["content"])
        return move.RequestMove(action, **kwargs)

    def parse_proposition(self, data):
        if data["_type"] == Proposition.PREDICATE:
            return self._create_predicate_proposition(data)
        if data["_type"] == Proposition.GOAL:
            goal = self.parse_goal(data["_content"])
            polarity = data.get("_polarity")
            return GoalProposition(goal, polarity)
        if data["_type"] == Proposition.PRECONFIRMATION:
            service_action = data.get("service_action")
            polarity = data.get("polarity")
            arguments = [self.parse_proposition(proposition) for proposition in data.get("_arguments")]
            ontology_name = data["_ontology_name"]
            return PreconfirmationProposition(ontology_name, service_action, arguments, polarity)
        if data["_type"] == Proposition.SERVICE_RESULT:
            service_action = data.get("service_action")
            action_outcome = self.parse(data.get("result"))
            arguments = [self.parse_proposition(proposition) for proposition in data.get("arguments")]
            ontology_name = data.get("ontology_name")
            return ServiceResultProposition(ontology_name, service_action, arguments, action_outcome)
        if data["_type"] == Proposition.UNDERSTANDING:
            content = self.parse(data["_content"])
            polarity = data.get("_polarity")
            speaker = data.get("_speaker")
            return UnderstandingProposition(speaker, content, polarity)
        if data["_type"] == Proposition.RESOLVEDNESS:
            issue = self.parse_question(data["issue"])
            return ResolvednessProposition(issue)
        if data["_type"] == Proposition.KNOWLEDGE_PRECONDITION:
            question = self.parse_question(data["_content"])
            polarity = data.get("_polarity")
            return KnowledgePreconditionProposition(question, polarity)
        if data["_type"] == Proposition.ACTION_STATUS:
            status = self.parse_action_status(data["_status"])
            action = self.parse_action(data["_content"])
            return ActionStatusProposition(action, status)
        if data["_type"] == Proposition.SERVICE_ACTION_STARTED:
            service_action = data["service_action"]
            parameters = [self.parse_proposition(proposition) for proposition in data.get("parameters")]
            ontology_name = data.get("ontology_name")
            return ServiceActionStartedProposition(ontology_name, service_action, parameters)
        if data["_type"] == Proposition.SERVICE_ACTION_TERMINATED:
            service_action = data["service_action"]
            polarity = data.get("_polarity")
            ontology_name = data["_ontology_name"]
            return ServiceActionTerminatedProposition(ontology_name, service_action, polarity)
        if data["_type"] == Proposition.PREREPORT:
            service_action = data["service_action"]
            argument_list = [self.parse_proposition(proposition) for proposition in data["argument_set"]]
            ontology_name = data["_ontology_name"]
            return PrereportProposition(ontology_name, service_action, argument_list)
        if data["_type"] == Proposition.REJECTED:
            polarity = data.get("_polarity")
            reason_for_rejection = data.get("reason_for_rejection")
            combination = self.parse(data["rejected_combination"])
            return RejectedPropositions(combination, polarity, reason_for_rejection)
        if data["_type"] == Proposition.IMPLICATION:
            antecedent = self.parse_proposition(data["_antecedent"])
            consequent = self.parse_proposition(data["_consequent"])
            return ImplicationProposition(antecedent, consequent)
        if data["_type"] == Proposition.NUMBER_OF_ALTERNATIVES:
            individual = self.parse_individual(data["_content"])
            return NumberOfAlternativesProposition(individual)
        if data["_type"] == Proposition.QUESTION_STATUS:
            question = self.parse_question(data["_content"])
            status = self.parse_action_status(data["_status"])
            return QuestionStatusProposition(question, status)
        if data["_type"] == Proposition.MUTE:
            return MuteProposition()
        if data["_type"] == Proposition.UNMUTE:
            return UnmuteProposition()
        if data["_type"] == Proposition.QUIT:
            return QuitProposition()
        raise JSONParseFailure(f"Cannot parse {data} as proposition.")

    def _create_predicate_proposition(self, data):
        if data["predicate"]["sort"]["_name"] == "boolean":
            individual = None
        elif data["predicate"]["sort"]["_name"] == "string" and data["individual"] is None:
            individual = None
        else:
            individual = self.parse_individual(data["individual"])
        predicate = self.parse_predicate(data["predicate"])
        polarity = data.get("_polarity")
        return PredicateProposition(predicate, individual, polarity)

    def parse_individual(self, data):
        if data is None:
            return None
        sort_data = data["sort"]
        value_data = data["value"]
        value, sort = create_individual(value_data, sort_data)
        polarity = data["polarity"]
        ontology_name = data["_ontology_name"]
        if polarity == Polarity.POS:
            return Individual(ontology_name, value, sort)
        return NegativeIndividual(ontology_name, value, sort)

    def parse_goal(self, data):
        if data is None:
            return None
        if data.get("_goal_type") == Goal.PERFORM_GOAL:
            target = data.get("_target")
            action = self.parse_action(data["_content"])
            goal = PerformGoal(action, target=target)
            return goal
        if data.get("_goal_type") == Goal.HANDLE_GOAL:
            event = data.get("_device_event")
            ontology_name = data.get("_ontology_name")
            return HandleGoal(ontology_name, event)
        if data.get("_goal_type") == Goal.RESOLVE_GOAL:
            target = data.get("_target")
            question = self.parse_question(data["_content"])
            return ResolveGoal(question, target=target)
        raise JSONParseFailure(f"could not parse {data} as goal.")

    def parse_acc_icm(self, data, kwargs):
        if data.get("content"):
            content = self.parse(data["content"])
            content_speaker = data.get("content_speaker")
            return move.ICMMoveWithContent(move.ICMMove.ACC, content, content_speaker=content_speaker, **kwargs)

        return move.ICMMove(move.ICMMove.ACC, **kwargs)

    def parse_accommodate_icm(self, data, kwargs):
        if data.get("content"):
            content = self.parse(data["content"])
            content_speaker = data.get("content_speaker")
            return move.ICMMoveWithContent(move.ICMMove.ACCOMMODATE, content, content_speaker=content_speaker, **kwargs)
        else:
            return move.ICMMove(move.ICMMove.ACCOMMODATE, **kwargs)

    def parse_resume_icm(self, data, kwargs):
        goal = self.parse_goal(data["content"])
        content_speaker = data.get("content_speaker")
        return move.ICMMoveWithContent(move.ICMMove.RESUME, goal, content_speaker=content_speaker, **kwargs)

    def parse_reraise_icm(self, data, kwargs):
        if data.get("content"):
            action = self.parse(data["content"])
            content_speaker = data.get("content_speaker")
            return move.ICMMoveWithContent(move.ICMMove.RERAISE, action, content_speaker=content_speaker, **kwargs)
        else:
            return move.ICMMove(move.ICMMove.RERAISE, **kwargs)

    def parse_per_icm(self, data, kwargs):
        string = data.get("content", None)
        if string:
            content_speaker = data.get("content_speaker")
            return move.ICMMoveWithContent(move.ICMMove.PER, string, content_speaker=content_speaker, **kwargs)
        return move.ICMMove(move.ICMMove.PER, **kwargs)

    def parse_und_icm(self, data, kwargs):
        content_data = data.get("content")
        if content_data:
            content = self.parse(content_data)
            content_speaker = data.get("content_speaker")
            return move.ICMMoveWithSemanticContent(move.ICMMove.UND, content, content_speaker=content_speaker, **kwargs)
        else:
            return move.ICMMove(move.ICMMove.UND, **kwargs)

    def parse_sem_icm(self, data, kwargs):
        content_data = data.get("content")
        if content_data:
            content = self.parse_proposition(content_data)
            content_speaker = data.get("content_speaker")
            return move.ICMMoveWithSemanticContent(move.ICMMove.SEM, content, content_speaker=content_speaker, **kwargs)
        content_speaker = data.get("content_speaker")
        return move.ICMMove(move.ICMMove.SEM, **kwargs)

    def parse_loadplan_icm(self, data, kwargs):
        return move.ICMMove(move.ICMMove.LOADPLAN, **kwargs)

    def parse_cardinal_sequencing_icm(self, data, kwargs):
        content_speaker = data.get("content_speaker")
        return move.ICMMoveWithContent(
            move.ICMMove.CARDINAL_SEQUENCING, int(data["content"]), content_speaker=content_speaker, **kwargs
        )

    def parse_report_inference_icm(self, data, kwargs):
        content_data = data.get("content")
        if content_data:
            content = self.parse(content_data)
            content_speaker = data.get("content_speaker")
            return move.ICMMoveWithSemanticContent(
                move.ICMMove.REPORT_INFERENCE, content, content_speaker=content_speaker, **kwargs
            )

    def parse_greet_move(self, kwargs):
        return move.GreetMove(**kwargs)

    def parse_insult_move(self, kwargs):
        return move.InsultMove(**kwargs)

    def parse_insult_response_move(self, kwargs):
        return move.InsultResponseMove(**kwargs)

    def parse_mute_move(self, kwargs):
        return move.MuteMove(**kwargs)

    def parse_unmute_move(self, kwargs):
        return move.UnmuteMove(**kwargs)

    def parse_quit_move(self, kwargs):
        return move.QuitMove(**kwargs)

    def parse_thank_you_move(self, kwargs):
        return move.ThankYouMove(**kwargs)

    def parse_thank_you_response_move(self, kwargs):
        return move.ThankYouResponseMove(**kwargs)

    def parse_prereport_move(self, data):
        service_action = data["service_action"]
        arguments = data["arguments"]
        argument_list = [self.parse_proposition(argument) for argument in arguments]
        ontology_name = data["ontology_name"]
        return move.PrereportMove(ontology_name, service_action, argument_list)

    def parse_report_move(self, data, kwargs):
        content = self.parse(data["content"])
        return move.ReportMove(content, **kwargs)

    def parse_ask_move(self, data, kwargs):
        question = self.parse_question(data["content"])
        return move.AskMove(question, **kwargs)

    def parse_service_action_outcome(self, json):
        if json["service_action_outcome"]:
            return SuccessfulServiceAction()
        return FailedServiceAction(json["failure_reason"])

    def parse_action_status(self, json):
        if json["action_status"] == ActionStatus.DONE:
            return Done()
        if json["action_status"] == ActionStatus.ABORTED:
            return Aborted(json["reason"])


class CheckingJSONParser(NonCheckingJSONParser):
    def __init__(self, ddd_name, ontology, domain_name):
        self.ontology = ontology
        self.ontology_name = ontology.get_name()
        self.domain_name = domain_name

    def parse_action(self, data):
        action_name = data["value"]
        return self.ontology.create_action(action_name)

    def _create_predicate_proposition(self, data):
        if data["predicate"]["sort"]["_name"] == "boolean":
            individual = None
        elif data["predicate"]["sort"]["_name"] == "string" and data["individual"] is None:
            individual = None
        else:
            individual = self.parse_individual(data["individual"])
        predicate = self.parse_predicate(data["predicate"])
        polarity = data.get("_polarity")
        return self.ontology.create_predicate_proposition(predicate, individual, polarity)

    def parse_predicate(self, data):
        predicate_name = data["name"]
        return self.ontology.get_predicate(predicate_name)

    def parse_findout_plan_item(self, data):
        question = self.parse_question(data[plan_item.TYPE_FINDOUT])
        allow_answer_from_pcom = data.get("allow_answer_from_pcom", False)
        return plan_item.Findout(self.domain_name, question, allow_answer_from_pcom)

    def parse_raise_plan_item(self, data):
        question = self.parse_question(data[plan_item.TYPE_RAISE])
        return plan_item.Raise(self.domain_name, question)

    def parse_individual(self, data):
        def extract(string, prefix, suffix):
            if value.startswith(prefix) and value.endswith(suffix):
                return value[len(prefix):len(value) - len(suffix)]

        def extract_datetime_value():
            return extract(value, "datetime(", ")")

        def extract_person_name_value():
            return extract(value, "person_name(", ")")

        if data is None:
            return None
        sort = self.ontology.get_sort(data["sort"]["_name"])
        value = data["value"]
        polarity = data["polarity"]
        if sort.is_datetime_sort():
            value = DateTime(extract_datetime_value())
        if sort.is_real_sort():
            value = float(value)
        if sort.is_person_name_sort():
            value = PersonName(extract_person_name_value())
        if sort.is_integer_sort():
            value = int(value)
        if polarity == Polarity.POS:
            return self.ontology.create_individual(value, sort)
        return self.ontology.create_negative_individual(value)

    def _create_lambda_abstracted_predicate_proposition(self, content):
        predicate = self.parse_predicate(content["predicate"])
        return self.ontology.create_lambda_abstracted_predicate_proposition(predicate)


class JSONTISParser():
    def __init__(self):
        self._parser = NonCheckingJSONParser()

    def parse(self, input):
        if "openqueue" in input:
            return self._parser.parse_openqueue(input["openqueue"])
        if "set" in input:
            return self._parser.parse_set(input["set"])
        if "stack" in input:
            return self.parse_stack(input)
        else:
            raise JSONParseFailure(f"cannot parse {input}.")

    def parse_stack_set(self, input):
        return stack.StackSet([self._parser.parse(item) for item in reversed(input["stackset"])])

    def parse_qud(self, input):
        contents = input["qud"]
        questions = self.parse_stack_set(contents)
        turn_information = {
            self._parser.parse(item["key"]): int(item["value"])
            for item in contents["turn_information"]
        }
        return stack.QUD(questions, turn_information)

    def parse_stack(self, input):
        return stack.Stack([self._parser.parse(item) for item in reversed(input["stack"])])

    def parse_entities(self, input):
        return Set([Entity.create_from_json(entity_as_dict) for entity_as_dict in input["set"]])

    def parse_iterators(self, input):
        return self._deserialize_iterators(input)

    def _deserialize_iterators(self, query_list):
        def try_parsing_as_list(list_or_single):
            try:
                return [self._parser.parse(value_as_json) for value_as_json in list_or_single]
            except Exception:
                return []

        def parse_as_single(single):
            return self._parser.parse(single)

        def deserialize_query(query_or_iterator):
            key_as_json = query_or_iterator["key"]
            value_as_json = query_or_iterator["value"]

            if value_as_json == []:
                value = []
            else:
                value = try_parsing_as_list(value_as_json)
                if not value:
                    value = parse_as_single(value_as_json)

            try:
                key = self._parser.parse(key_as_json)
            except AttributeError:
                key = key_as_json

            return key, value

        queries = {}
        for query_as_json in query_list:
            k, v = deserialize_query(query_as_json)
            queries[k] = v
        return queries

    def parse_last_hint_for_question(self, input):
        return {self._parser.parse(kv_dict["key"]): int(kv_dict["value"]) for kv_dict in input}

    def parse_plan(self, input):
        return JSONDomainParser().parse_plan(input)

    def parse_alts(self, input):
        return [alt for alt in input]
