from allennlp.common.util import JsonDict
from allennlp.data import Instance

def get_fields_to_compare(inputs: JsonDict, instance: Instance, input_field_to_attack: str) -> JsonDict:
    """
    Gets a list of the fields that should be checked for equality after an attack is performed.

    Parameters
    ----------
    inputs : ``JsonDict``
        The input you want to attack, similar to the argument to a Predictor, e.g., predict_json().
    instance : ``Instance``
        A labeled instance that is output from json_to_labeled_instances().
    input_field_to_attack : ``str``
        The key in the inputs JsonDict you want to attack, e.g., tokens.

    Returns
    -------
    fields : ``JsonDict``
        The fields that must be compared for equality.
    """
    # TODO(mattg): this really should live on the Predictor.  We have some messy stuff for, e.g.,
    # reading comprehension models, and the interpret code can't really know about the internals of
    # that (or at least it shouldn't now, and once we split out the reading comprehension repo, it
    # really *can't*).
    fields_to_compare = {
            key: instance[key]
            for key in instance.fields
            if key not in inputs and key != input_field_to_attack and key != 'metadata' and key != 'output'
    }
    return fields_to_compare

def instance_has_changed(instance: Instance, fields_to_compare: JsonDict):
    if 'clusters' in fields_to_compare:
        # Coref needs a special case here, apparently.  I (mattg) am not sure why the check below
        # doesn't catch this case; TODO: look into this.
        original_clusters = set(tuple(l) for l in fields_to_compare['clusters'])
        new_clusters = set(tuple(l) for l in instance['clusters'])  # type: ignore
        return original_clusters != new_clusters
    if any(instance[field] != fields_to_compare[field] for field in fields_to_compare):
        return True
    return False
