content: /* 
   * Copyright (C) 2015 Adrien Guille <adrien.guille@univ-lyon2.fr>
   *
   * 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/>.
   */
  package main.java.fr.ericlab.sondy.algo.eventdetection.mabed;
  
  import main.java.fr.ericlab.sondy.core.app.AppParameters;
  import java.io.Serializable;
  import java.sql.Timestamp;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashMap;
  import org.graphstream.algorithm.ConnectedComponents;
  import org.graphstream.algorithm.ConnectedComponents.ConnectedComponent;
  import org.graphstream.graph.Edge;
  import org.graphstream.graph.Graph;
  import org.graphstream.graph.Node;
  import org.graphstream.graph.implementations.SingleGraph;
  
  /**
   *
   *   @author Adrien GUILLE, ERIC Lab, University of Lyon 2
   *   @email adrien.guille@univ-lyon2.fr
   */
  public class MABEDEventGraph implements Serializable {
      public Graph graph;
      public Graph redundancyGraph;
      public HashMap<String,MABEDEvent> duplicatedEvents;
      double maximumScore;
      double sigma;
      
      public MABEDEventGraph(double ms, Double sig){
          sigma = sig;
          graph = new SingleGraph("");
          redundancyGraph = new SingleGraph("");
          maximumScore = ms;
          duplicatedEvents = new HashMap<>();
      }
      
      public int addEvent(MABEDEvent event){
          int added = 0;
          boolean redundant = false;
          if(graph.getNode(event.mainTerm) != null){
              for(MABEDWeightedTerm wt : event.relatedTerms.list){
                  Node wtNode = graph.getNode(wt.term);
                  if(wtNode != null){
                      if(wtNode.getAttribute("ui.class").equals("mainTerm") && wtNode.hasEdgeFrom(graph.getNode(event.mainTerm).toString())){
                          MABEDEvent event1 = getEvent(wtNode);
                          double intersection = Math.max(event.I.intersectionProportion(event1.I), event1.I.intersectionProportion(event.I));
                          if(intersection > sigma){
                              redundant = true;
                              duplicatedEvents.put(event.mainTerm,event);
                              duplicatedEvents.put(event1.mainTerm,event1);
                              // new way of managing redundancy
                              if(redundancyGraph.getNode(event1.mainTerm) == null){
                                  redundancyGraph.addNode(event1.mainTerm);
                              }
                              if(redundancyGraph.getNode(event.mainTerm) == null){
                                  redundancyGraph.addNode(event.mainTerm);
                              }
                              if(redundancyGraph.getEdge(event.mainTerm+"-"+event1.mainTerm) == null){
                                  redundancyGraph.addEdge(event.mainTerm+"-"+event1.mainTerm, event.mainTerm, event1.mainTerm, false);
                              }
                          }
                      }
                  }
              }
          }
          if(!redundant){
              if(event.mainTerm != null){
                  if(graph.getNode(event.mainTerm) == null){
                      graph.addNode(event.mainTerm);
                  }
                  graph.getNode(event.mainTerm).addAttributes(event.getMainTermAttributes());
                  graph.getNode(event.mainTerm).setAttribute("ui.size",20+(event.score/maximumScore)*10);
                  graph.getNode(event.mainTerm).addAttribute("ui.label", "["+AppParameters.dataset.corpus.convertTimeSliceToDay(event.I.timeSliceA)+"::"+AppParameters.dataset.corpus.convertTimeSliceToDay(event.I.timeSliceB)+"]:"+event.mainTerm);
                  graph.getNode(event.mainTerm).addAttribute("anomaly",event.anomaly);
                  for(MABEDWeightedTerm wt : event.relatedTerms.list){
                      if(wt.term != null){
                          if(graph.getNode(wt.term)==null){
                              graph.addNode(wt.term);
                              graph.getNode(wt.term).addAttribute("ui.label", wt.term);
                              graph.getNode(wt.term).setAttribute("ui.class","relatedTerm");
                          }
                          graph.addEdge("["+event.I.timeSliceA+":"+event.I.timeSliceB+"]"+event.mainTerm+"-"+wt.term,wt.term,event.mainTerm,true);
                          graph.getEdge("["+event.I.timeSliceA+":"+event.I.timeSliceB+"]"+event.mainTerm+"-"+wt.term).addAttribute("weight", wt.weight);
                      }
                  }
              }
              added = 1;
          }
          return added;
      }
      
      public MABEDEventList identifyConnectedComponents(){
          ConnectedComponents ccs = new ConnectedComponents(redundancyGraph);
          ccs.setCountAttribute("component");
          int i = 0;
          MABEDEventList globalEvents = new MABEDEventList();
          for (ConnectedComponent cc : ccs) {
              MABEDEventList ccEvents = new MABEDEventList();
              for(Node node : cc.getEachNode()){
                  MABEDEvent event = duplicatedEvents.get(node.getId());
                  ccEvents.add(event);
                  removeEvent(event);
              }
              ccEvents.sort();
              MABEDEvent mainEvent = ccEvents.list.pop();
              MABEDEvent globalEvent = mainEvent.merge(ccEvents);
              globalEvents.add(globalEvent);
              addEvent(globalEvent);
              i++;
          }
          return globalEvents;
      }
      
      public void removeEvent(MABEDEvent event){
          Node mainNode = graph.getNode(event.mainTerm);
          if(mainNode != null){
              if(mainNode.getAttribute("ui.class").equals("mainTerm")){
                  Collection<Edge> edges = mainNode.getEnteringEdgeSet();
                  if(edges != null){
                      for(Edge edge : edges){
                          if(edge != null){
                              // remove obsolete edge
                              graph.removeEdge(edge);
                              if(edge.getSourceNode().getDegree() == 0){
                                  graph.removeNode(edge.getSourceNode().toString());
                              }
                          }
                      }
                  }
                  if(mainNode.getOutDegree() == 0){
                      graph.removeNode(mainNode);
                  }else{
                      mainNode.setAttribute("ui.class", "relatedTerm");
                  }
              }
          }
      }
      
      public void replaceEvent(Node nodeT0, MABEDEvent t1){
          nodeT0.setAttribute("ui.class","relatedTerm");
          Collection<Edge> edges = nodeT0.getEnteringEdgeSet();
          graph.addNode(t1.mainTerm);
          graph.getNode(t1.mainTerm).addAttributes(t1.getMainTermAttributes());
          graph.getNode(t1.mainTerm).setAttribute("ui.size",20+(t1.score/maximumScore)*10);
          graph.getNode(t1.mainTerm).addAttribute("ui.label", "["+AppParameters.dataset.corpus.convertTimeSliceToDay(t1.I.timeSliceA)+"::"+AppParameters.dataset.corpus.convertTimeSliceToDay(t1.I.timeSliceB)+"]:"+t1.mainTerm);        
          graph.getNode(t1.mainTerm).setAttribute("anomaly",t1.anomaly);
          if(edges != null){
              for(Edge edge : edges){
                  if(edge != null){
                      // remove obsolete edge
                      graph.removeEdge(edge);
                      // add edge toward the new main term
                      if(!t1.mainTerm.contains(edge.getSourceNode().getId())){
                          String edgeId = "["+t1.I.timeSliceA+":"+t1.I.timeSliceB+"]"+t1.mainTerm+"-"+edge.getSourceNode().getId();
                          graph.addEdge(edgeId,edge.getSourceNode().getId(),t1.mainTerm,true);
                          graph.getEdge(edgeId).addAttribute("weight", edge.getAttribute("weight"));
                      }
                      if(edge.getSourceNode().getDegree() == 0){
                          graph.removeNode(edge.getSourceNode().toString());
                      }
                  }
              }
          }
          graph.removeNode(nodeT0);
          for(MABEDWeightedTerm wt : t1.relatedTerms.list){
              if(wt.term != null){
                  if(graph.getNode(wt.term)==null){
                      graph.addNode(wt.term);
                      graph.getNode(wt.term).addAttribute("ui.label", wt.term);
                      graph.getNode(wt.term).setAttribute("ui.class","relatedTerm");
                  }
                  if(graph.getEdge("["+t1.I.timeSliceA+":"+t1.I.timeSliceB+"]"+t1.mainTerm+"-"+wt.term) == null){
                      graph.addEdge("["+t1.I.timeSliceA+":"+t1.I.timeSliceB+"]"+t1.mainTerm+"-"+wt.term,wt.term,t1.mainTerm,true);
                  }
                  graph.getEdge("["+t1.I.timeSliceA+":"+t1.I.timeSliceB+"]"+t1.mainTerm+"-"+wt.term).addAttribute("weight", wt.weight);
              }
          }
  //        if(nodeT0.getDegree() == 0){
  //            System.out.println("   - "+nodeT0.getId()+" is a leaf node.");
  //            graph.removeNode(nodeT0);
  //        } 
     }
      
      public MABEDEvent getEvent(Node mainNode){
          MABEDEvent event = new MABEDEvent();
          if(mainNode.getAttribute("ui.class")!=null && mainNode.getAttribute("ui.class").equals("mainTerm")){
              event = new MABEDEvent(mainNode.getId(), new MABEDTimeInterval((String) mainNode.getAttribute("I")), (double) mainNode.getAttribute("score"), (ArrayList<Double>) mainNode.getAttribute("anomaly"));
              for(Edge edge : mainNode.getEnteringEdgeSet()){
                  event.relatedTerms.add(new MABEDWeightedTerm(edge.getSourceNode().getId(), (double) edge.getAttribute("weight")));
              }
          }
          return event;
      }
      
      public MABEDEventList toEventList(){
          MABEDEventList events = new MABEDEventList();
          events.timeSliceLength = AppParameters.dataset.corpus.timeSliceLength;
          events.corpusStart = new Timestamp(AppParameters.dataset.corpus.start.getTime());
          for(Node node : graph){
              if(node.getAttribute("ui.class")!=null && node.getAttribute("ui.class").equals("mainTerm")){
                  MABEDEvent event = new MABEDEvent(node.getId(), new MABEDTimeInterval((String) node.getAttribute("I")), (double) node.getAttribute("score"), (ArrayList<Double>) node.getAttribute("anomaly"));
                  for(Edge edge : node.getEnteringEdgeSet()){
                      event.relatedTerms.add(new MABEDWeightedTerm(edge.getSourceNode().getId(), (double) edge.getAttribute("weight")));
                  }
                  events.add(event);
              }
          }
          events.sort();
          return events;
      }
      
  }
  