import copy

import logging

from . import parsing
from . import knowledge_evaluation
from .modifiable_property import is_modifiable_property


def diff_knowledge(before, after):
    import jsondiff
    return jsondiff.diff(before, after)


class KnowledgeBase(object):
    def __init__(self, knowledge, examples=[], trained=[]):
        self.knowledge = copy.copy(knowledge)
        self.examples = copy.copy(examples)
        self.trained = copy.copy(trained)

    def train(self, examples):
        knowledge_before = copy.deepcopy(self.knowledge)

        # Parse everything
        parsed_examples = []
        for example in examples:
            logging.info("\x1b[7;32m> {} \x1b[0m".format(example))
            tokens, decomposition, inferred_tree = parsing.integrate_language(self, example)
            logging.info(tokens)
            result = knowledge_evaluation.integrate_information(self.knowledge, {
                "elements": tokens,
                "decomposition": decomposition,
                "parsed": inferred_tree,
            })

            logging.info("\x1b[7;33m< {} \x1b[0m".format(self.get_value(result)))
            self.act_upon(result)
            logging.info("\x1b[7;34m> set: {} \x1b[0m".format(self.get_value(result)))
            self.examples.append((decomposition, inferred_tree))

            # Reduce values
            self.trained = parsing.reprocess_language_knowledge(self, self.examples)

        knowledge_after = copy.deepcopy(self.knowledge)
        knowledge_diff_getter = lambda: diff_knowledge(knowledge_before,
                                                       knowledge_after)

        return knowledge_diff_getter


    def process(self, row):
        row = row.lower()
        knowledge_before = copy.deepcopy(self.knowledge)
        logging.info("\x1b[7;32m> {} \x1b[0m".format(row))
        tokens = parsing.to_tokens(row)
        tokens, inferred_tree = parsing.get_fit(self, tokens)
        result = knowledge_evaluation.integrate_information(self.knowledge,
                                                          {
                                                              "elements": tokens,
                                                              "parsed": inferred_tree,
                                                          })
        self.act_upon(result)

        knowledge_after = copy.deepcopy(self.knowledge)
        knowledge_diff_getter = lambda: diff_knowledge(knowledge_before,
                                                       knowledge_after)

        return result, inferred_tree, knowledge_diff_getter

    def get_value(self, result):
        if is_modifiable_property(result):
            return result.getter()
        else:
            return result

    def act_upon(self, result):
        if is_modifiable_property(result):
            result.setter()
        else:
            logging.warning("Cannot act upon: {}".format(result))