import sys
import os
modules_path = '../../../modules/'
if os.path.exists(modules_path):
    sys.path.insert(1, modules_path)

import json
from db.entities import Layer, Cluster
from typing import List, Dict, Tuple, Any
from db.repository import Repository
from processing.clustering import Clusterer, ClusterResult


repo = Repository()


def run_generic_clustering(selected_use_cases: List[str] = None, selected_use_case_tables: List[str] = None, selected_layer_names: List[str] = None):
    '''Runs the clustering for all layers found in the repository.'''
    all_layers:List[Layer] = repo.get_layers()
    layers = [l for l in all_layers 
        if (selected_use_cases is None or l.use_case in selected_use_cases)
        and (selected_use_case_tables is None or l.use_case_table in selected_use_case_tables)
        and (selected_layer_names is None or l.layer_name in selected_layer_names)
        ]

    for layer in layers:
        print(f"Clustering {layer.use_case}//{layer.use_case_table}//{layer.layer_name}.")

        if layer.properties is None or len(layer.properties) == 0:
            print("skipping, no properties to cluster")
            continue

        try:
            clusters = run_clustering_for_layer(layer)
            store_generic_clusters(clusters, layer.layer_name)
        except Exception as e:
            print(str(e))


def run_clustering_for_layer(layer: Layer) -> List[Cluster]:
    nodes = repo.get_layer_nodes(layer.use_case, layer.use_case_table, layer.layer_name)

    clusterer = Clusterer()
    res: Dict[Any, ClusterResult] = clusterer.cluster_dataset(
        nodes,
        layer.properties
    )

    return [Cluster(layer.use_case, layer.use_case_table, layer.layer_name, 
                    cluster_label=key, nodes=cluster_result.nodes, label=cluster_result.label if key != -1 else 'noise') 
            for key, cluster_result in res.items()]


def store_generic_clusters(clusters: List[Cluster], layer):
    try:
        with open(f'{layer}.json', 'w') as file:
            cluster_dicts = [c.to_serializable_dict(for_db=False) for c in clusters]
            file.write(json.dumps(cluster_dicts))
    except:
        print(f"Error while writing json for {layer}")

    try:
        for cluster in clusters:
            repo.add_cluster(cluster)
    except:
        print(f"Error while storing cluster in db for {layer}")


if __name__ == "__main__":
    # please dont delete all clusters anymore @10.11.2020
    # repo.delete_all_clusters()
    run_generic_clustering(selected_use_cases=[], selected_use_case_tables=[], selected_layer_names=[])
