import pymongo
import network_constants as netconst
from database.MongoRepositoryBase import MongoRepositoryBase
import json

from db.agi.agi_repository import AgiRepository

from db.entities import *
from typing import List


class Repository(MongoRepositoryBase):
    '''This is a repository for MongoDb.'''

    def __init__(self):
        super().__init__(netconst.COMMUNITY_DETECTION_DB_HOSTNAME,
                         netconst.COMMUNITY_DETECTION_DB_PORT,
                         'communityDetectionDb')

        self._location_collection = 'location'
        self._location_cluster_collection = 'location_cluster'
        self._time_cluster_collection = 'time_cluster'
        self._user_cluster_graph_collection = 'user_cluster_graph'
        self._layer_collection = 'layer'
        self._clusterset_collection = 'cluster_set'

        self.agi_repo = AgiRepository()

#region Location
    def add_location(self, location: Location):
        super().insert_entry(self._location_collection, location.to_serializable_dict())

    def get_locations(self) -> List[Location]:
        locations = super().get_entries(self._location_collection)
        return [Location(l) for l in locations]

    def get_agi_locations(self) -> List[Location]:
        agi_locations = self.agi_repo.getLocations()
        return [Location(agi_loc) for agi_loc in agi_locations]
#endregion

#region Specific Clusters
    def add_location_cluster(self, cluster: LocationCluster):
        super().insert_entry(self._location_cluster_collection,
                             cluster.to_serializable_dict(for_db=True))

    def get_location_clusters(self) -> List[LocationCluster]:
        clusters = super().get_entries(self._location_cluster_collection)
        return [LocationCluster(location_dict=c, from_db=True) for c in clusters]

    def add_time_cluster(self, cluster: TimeCluster):
        super().insert_entry(self._time_cluster_collection,
                             cluster.to_serializable_dict(for_db=True))

    def get_time_clusters(self) -> List[TimeCluster]:
        clusters = super().get_entries(self._time_cluster_collection)
        return [TimeCluster(time_dict=c, from_db=True) for c in clusters]
#endregion

#region Cluster Graph
    def add_user_cluster_graph(self, user_graph: UserClusterGraph):
        super().insert_entry(self._user_cluster_graph_collection,
                             user_graph.to_serializable_dict(for_db=True))

    def get_user_cluster_graphs(self) -> List[UserClusterGraph]:
        user_graphs = super().get_entries(self._user_cluster_graph_collection)
        return [UserClusterGraph(dict_=u, from_db=True) for u in user_graphs]
#endregion

#region Layers
    def add_layer(self, layer: Layer):
        super().insert_entry(self._layer_collection, layer.to_serializable_dict())

    def get_layers(self) -> List[Layer]:
        entries = super().get_entries(self._layer_collection)
        return [Layer(e) for e in entries]

    def get_layer_names(self) -> List[str]:
        entries = super().get_entries(self._layer_collection, projection={'layer_name': 1})
        return [e['layer_name'] for e in entries]

    def get_layer(self, layer_name) -> Layer:
        entries = super().get_entries(self._layer_collection, selection={'layer_name': layer_name})
        entries = [Layer(e) for e in entries]

        if entries is not None and len(entries) > 0:
            return entries[0]
        else:
            return None
#endregion

#region ClusterSet
    def add_clusterset(self, cluster_set: ClusterSet):
        super().insert_entry(self._clusterset_collection, cluster_set.to_serializable_dict())

    def get_clustersets(self) -> List[ClusterSet]:
        '''Returns all clustersets.'''
        entries = super().get_entries(self._clusterset_collection)
        return [ClusterSet(cluster_set_dict=e) for e in entries]

    def get_clusterset_names(self) -> List[str]:
        '''Returns the names of all clustersets.'''
        entries = super().get_entries(self._clusterset_collection, projection={'layer_name': 1})
        return [e['layer_name'] for e in entries]

    def get_clusterset(self, layer_name) -> ClusterSet:
        '''Returns a single clusterset with the given name or None otherwise.'''
        entries = super().get_entries(self._clusterset_collection, selection={'layer_name': layer_name})
        entries = [ClusterSet(cluster_set_dict=e) for e in entries]

        if entries is not None and len(entries) > 0:
            return entries[0]
        else:
            return None
#endregion
