import logging

from spacy.tokens import Doc as doc
from spacy.tokens import Token
from typing import Any, Dict, List

logger = logging.getLogger("global_logger")

class Sentence:
    def __init__(self, text: str, dependency_tree: doc, nlp):
        self.__text = text
        self.__dependency_tree = dependency_tree
        self.__nlp = nlp
        self.__root = [x for x in self.__dependency_tree if x.dep_ == "ROOT"][0]
        self.__split_result = []
        assert Sentence.check_format(text, dependency_tree, nlp), "wrong sentence!"

    @property
    def text(self) -> str:
        return self.__text

    @property
    def dependency_tree(self) -> doc:
        return self.__dependency_tree

    @property
    def root(self) -> Token:
        return self.__root

    @property
    def action(self) -> str:
        return self.__root.text

    @property
    def objects(self) -> List[str]:
        object_results = []
        mod_results = []
        for x in self.__dependency_tree:
            if "obj" in x.dep_:
                object_results.append("".join(Sentence.concatenate_words(x, [])).strip())
            elif "mod" in x.dep_:
                mod_list = Sentence.concatenate_words(x, [])
                if len(mod_list) > 1:
                    mod_results.append("".join(mod_list).strip())

        results = object_results + mod_results
        results = [x for x in results if not any(y in x and y != x for y in results)]
        return results


    def split_sentence(self) -> List["Sentence"]:
        u = self.root
        logger.info("sentence: " + self.text)
        self.__split_result = []
        self.__dfs_split(u)
        for a in self.__split_result:
            logger.info(" sub: " + a.text)
        return self.__split_result

    def __dfs_split(self, u:Token):
        conj = []
        for child in u.children:
            if child.dep_ == "conj":
                conj.append(child)
        subtree = "".join(Sentence.concatenate_words(u, passed=["conj", "cc"])).strip().strip()
        subtree = Sentence.create(subtree, self.__nlp(subtree), self.__nlp)
        if subtree != None:
            self.__split_result.append(subtree)
        for child in conj:
            self.__dfs_split(child)

    @staticmethod
    def create(text: str, dependency_tree: doc, nlp):
        if not Sentence.check_format(text, dependency_tree, nlp):
            return None
        return Sentence(text, dependency_tree, nlp)

    @staticmethod
    def check_format(text, dependency_tree, nlp):
        has_predicate = False
        has_object = False
        for token in dependency_tree:
            if token.dep_ == "ROOT" and token.pos_ == "VERB":
                has_predicate = True
            if "obj" in token.dep_:
                has_object = True
        return has_predicate and has_object

    @staticmethod
    def concatenate_words(u: Token, passed):
        words = []
        for child in u.lefts:
            if child.dep_ not in passed:
                words.extend(Sentence.concatenate_words(child, passed))
        words.append(u.text_with_ws)
        for child in u.rights:
            if child.dep_ not in passed:
                words.extend(Sentence.concatenate_words(child, passed))

        return words