#
# <one line to give the library's name and an idea of what it does.>
# Copyright (C) 2014  <copyright holder> <email>
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 
#

import logging
import abc, sys
import networkx
import scipy
from sklearn.cluster import KMeans
from sklearn.metrics import pairwise_distances_argmin_min
from collections import defaultdict
from itertools import combinations
from reducers import KMeansClusteringReducer

class SemGraph(object):
  """!
  This class is the wrapper for networkx.Graph class and represents any
  of contextual graph used into Grafon. It can be plWordNet graph and/or
  SUMO ontology graph.

  One of the main ideas is to gets the minimal subgraph(s) of built graph.
  Use the reducers_chain method to reduce graph to with given nodes.
  """

  def __init__(self, g = None):
    """!
    Constructor only set stored graph to given and initializes the undirected
    graph to None if graph has not been given or to undeirected in case
    if the graph is given.

    @param g - networkx direcred graph (MultiDiGraph or DiGraph) 
               as @default value is used None
    """
    self._g = g
    if self._g:
      self._undirected_g = self._g.to_undirected()
    else:
      self._undirected_g = None

  def G(self):
    """!
    Returns directed graph stored as a member self._g
    @returns directed graph self._g
    """
    return self._g

  def u_G(self):
    """!
    @returns undirected graph self._g
    """
    return self._undirected_g

  @abc.abstractmethod
  def build_graph(self):
    """!
    It is abstract class which must be reimplemented in all childes.
    The idea of this method is to build the graph and alsa set 
    undirected graph as undirected! This method should set the (diredted) 
    graph as the self._g and undirected to self._undirected_g. It sould not 
    return the value.
    """
    raise NotImplementedError("build_graph is the abstract method!")

  def reducers_chain(self, nodes, reducers):
    """!
    Reduce the graph step by step by using a chain of reducers.

    The original graph is reduced by the first reducer. Then the output of the
    previous reducer is the input of the next reducer. If the chain is over,
    the final reduced graph is returned.

    @param text_nodes nodes from a text.
    @type text_nodes: grafon.structures.textnodes.TextNodes
    @param reducers a sequence of reducers that will reduce the graph one after
      another.
    @type reducers: list of grafon.graphs.reducers.ReducerInterface children
    """
    logging.info('~\t reducer {}'.format(reducers[0]))
    reduced_graph = reducers[0].reduce(self.G(), nodes)
    for reducer in reducers[1:]:
      logging.info('~\t reducer {}'.format(reducer))
      reduced_graph = reducer.reduce(reduced_graph, nodes)
    return reduced_graph