from queue import Empty
from time import sleep


class ExplanationGeneratorConsumer:
    """Process to explain the assignments."""
    def __init__(self, validator, features, stop_event):
        self.validator = validator
        self.features = features
        self.stop_event = stop_event

    def explain(self, sample, assignments, explanations_valid, explanations_invalid):
        while not self.stop_event.is_set():
            try:
                assignment = assignments.get(block=False)
            except Empty:
                sleep(0.5)
                continue

            # Check for stop signal
            if assignment is None:
                break

            if self.validator.is_explanation_valid(sample, assignment):
                explanation = self.__shrink_assignment(assignment, sample)
                explanations_valid.put(explanation)
            else:
                explanation = self.__grow_assignment(assignment, sample)
                explanations_invalid.put(explanation)

    def __shrink_assignment(self, assignment, sample):
        """Extract a minimal explanation using greedy deletion."""
        mus = set(assignment)

        for feature in self.features:
            if self.stop_event.is_set():
                break

            if feature not in mus:
                continue

            candidate = mus - {feature}
            if self.validator.is_explanation_valid(sample, list(candidate)):
                mus = candidate

        return sorted(mus)

    def __grow_assignment(self, assignment, sample):
        """Extract a contrastive explanation by growing the assignment."""
        mcs = set(assignment)

        for feature in self.features:
            if self.stop_event.is_set():
                break

            if feature in mcs:
                continue

            candidate = mcs | {feature}
            if not self.validator.is_explanation_valid(sample, list(candidate)):
                mcs = candidate

        return sorted(mcs)
