from dendropy import Tree, Node

__author__ = 'Gosia'
from projekt1.Cluster import Cluster

class Converter:

    #Metoda zwracająca listę klastrów dla drzewa
    def get_cluster_list(self,tree):
        return self.prepare_cluster_list_for_node(tree.seed_node)

    #Przygotowanie listy klastrów, jako reprezanetacji drzewa - rekurancja
    def prepare_cluster_list_for_node(self, node):
        cluster_list = []
        #Jeżeli element drzewa jest liściem, to do listy klastrów dodajemy klaster, który ma nazwę tego liścia - taxon i tego liscia w środku
        if node.is_leaf():
            taxon = node.taxon
            cluster = Cluster(taxon, node)
            cluster_list.append(cluster)
        else:
            #Jeżeli dany węzeł drzewa jest węzłem, to do listy dodawany jest klaster z brakiem nazwy - taxon, a lista to lista wszystkich liści, do których można od niego dojść
            leafs = node.leaf_nodes()
            taxon = node.taxon
            cluster = Cluster(taxon,leafs)
            childs = node.child_nodes()
            cluster_list.append(cluster)
            #Następnie dla każdego jego dziecka wykonywana jest ta sama funkcja
            for child in childs:
                child_cluster = self.prepare_cluster_list_for_node(child)
                cluster_list += child_cluster

        #Na koniec zwracana jest lista klastrów
        return cluster_list

    #Konwersja drzewa złożonego z klastrów na drzewo grafowe
    def get_dendropy_tree(self, cluster_tree):

        consensus_tree_dendropy =  Tree()
        #Rozróżnienie liści od węzłów i zapisanie ich do dwóch osobnych tablic
        leafs = []
        nodes = []
        for cluster in cluster_tree.get_cluster_list():
            if cluster.taxon is not None:
                leafs.append(cluster)
            if cluster.taxon is None:
                nodes.append(cluster)
        #Posortowanie listy węzłów po to, aby zacząć przeszukiwać je od tych, które mają najmniej liści i budować drzewo od dołu
        nodes.sort(key=lambda x: len(x.clusters))
        #Tablica tymczasowych utworzonych już węzłów, z których budowane jest drzewo
        created_nodes = []
        for node in nodes:

            #Dla każego znalezionego węzła, tworzony jest węzeł drzewa
            # a następnie dodawane są do niego jego liście

            created_node = Node()
            for leaf in node.clusters:
                #Jeżeli jego liści nie jest na liście lisci, oznacza to, że został już wcześniej zużyty
                #Czyli znajduje się już w tymczasowym węźle i należy jako dziecko dodać ten tymczasowy węzeł
                if not self.is_leaf_in_leafs(leaf,leafs):
                    # Należy znaleźć stworzony węzeł z liściem i dodać go jako dziecko do nowego węzła
                    # A zużyty węzeł usunąć
                    sub_created_node, created_nodes = self.find_created_node_with_leaf(created_nodes, leaf)
                    if sub_created_node is not None:
                        created_node.add_child(sub_created_node)
                else:
                    #jeżeli liśc nie został jeszcze zużyty, to należy go dodać jako dziecko węzła
                    # i usunąć z listy liści
                    created_node.add_child(leaf)
                    leafs = self.remove_leaf_from_list(leaf,leafs)

            created_nodes.append(created_node)

        # Finalnie, węzeł ze wszystkimi liściami oraz węzłami będzie na pierwszym i jedynym miejscu w liście tymczasowych
        tree = Tree(seed_node=created_nodes[0])
        return tree

    #Wyszukanie w liście tymczasowych węzłów drzewa wezła, które zawiera szukany lisć
    #A następnie usunięcie tego węzła z listy tymczasowych węzłów ponieważ został on już zużyty
    def find_created_node_with_leaf(self, created_nodes, leaf):

        for created_node in created_nodes:
            for leaf_of_creaded_node in created_node.leaf_nodes():
                if leaf_of_creaded_node.taxon.label is leaf.taxon.label:
                    created_nodes.remove(created_node)
                    return created_node, created_nodes

        return None, created_nodes

    #Sprawdzenie czy liść znajduje się na liści liści
    def is_leaf_in_leafs(self, leaf, leafs):

        for leaf_tmp in leafs:
            if leaf_tmp.taxon.label is leaf.taxon.label:
                return True

        return False

    #Usuniecie liścia z listy liści i zwrócenie tej listy
    def remove_leaf_from_list(self,leaf,leafs):

        for leaf_in_list in leafs:
            if leaf_in_list.taxon.label is leaf.taxon.label:
                leafs.remove(leaf_in_list)

        return  leafs




