/* Copyright (C) 2009  CSE,IIT Bombay  http://www.cse.iitb.ac.in

This file is part of the ConStore open source storage facility for concept-nets.

ConStore is free software and distributed under the 
Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License;
you can copy, distribute and transmit the work
with the work attribution in the manner specified by the author or licensor.
You may not use this work for commercial purposes and may not alter, 
transform, or build upon this work.

Please refer the legal code of the license, available at
http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode

ConStore 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.  */

package iitb.con.clustering;

import iitb.con.core.Instance;
import iitb.con.core.Relation;
import iitb.con.core.RelationInstance;
import iitb.con.net.ConStore;
import iitb.con.net.FileConceptNet;
import iitb.con.query.Query;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Utitliy to cluster the entity instances of concept-net based on 
 * their relationships. 
 *  
 * @author Prathab K
 *
 */
public class InstanceCluster {

    static FileConceptNet conceptNet = null;
    static Query query = null;
    
    public static void cluster(String conceptNetName, short clusterDiameter) {
        try {
            
            conceptNet = (FileConceptNet) ConStore.open(conceptNetName,"rw");
        
            query = conceptNet.query();
            
            System.out.println("Retrieving the relations...");
            List<RelationInstance> relations = query.getAllRelationInstances();
            
            Map<Integer,List<ClusterNode>> graph = new HashMap<Integer,List<ClusterNode>>();
            
            Map<Integer, ClusterNode> cnodeMap = new HashMap<Integer,ClusterNode>();
            
            List<ClusterNode> cnodeTemp = new ArrayList<ClusterNode>();
            
            List<Integer> entityIds = query.getAllEntityInstanceIds();
            for(Integer id : entityIds) {
                cnodeMap.put(id, new ClusterNode(id));
            }
            
            //Concept-net is represented as graph.
            //Graph is represented similar to adjacency list 
            //using hash map.
            for(RelationInstance relation : relations) {
                addToGraph(graph, cnodeMap, relation.leftInstanceId,relation.rightInstanceId);
                if(relation.getDirection() == Relation.BIDIRECTIONAL) {
                    addToGraph(graph, cnodeMap, relation.rightInstanceId,relation.leftInstanceId);
                }
            }

            System.out.println("Performing clustering...");
            
            CliqueClustering cc = new CliqueClustering();
            
            //Start instance is randomly chosen
            cc.cluster(graph, cnodeMap.get(entityIds.get(0)), clusterDiameter);
            
            List<RelationInstance> relationInstances = query.getAllRelationInstances();
            List<Instance> instances = new ArrayList<Instance>();
            
            System.out.println("Ordering the instances...");
            for(Integer i : cnodeMap.keySet()) {
                ClusterNode cnode = cnodeMap.get(i);
                cnodeTemp.add(cnode);
            }
            
            Collections.sort(cnodeTemp);
            for(ClusterNode cn : cnodeTemp) {
                instances.add(query.getInstance(cn.key));
            }
            
            conceptNet.reInitializeNetFiles(null);
            conceptNet.close();
            conceptNet = (FileConceptNet) ConStore.open(conceptNetName,"rw");
            query = conceptNet.query();
            
            System.out.println("Storing the instances...");
            for(Instance instance : instances) {
                if(instance != null)
                    conceptNet.addInstance(instance, cnodeMap.get(instance.id).clusterId);
            }
            
            //Cluster the relation if required. Here it is not clustered
            for(RelationInstance r : relationInstances)
                conceptNet.add(r);
            
            conceptNet.commit();
            conceptNet.close();
            
            System.out.println("Successfully clustered");
            
            
        }catch (FileNotFoundException fe) {
            fe.printStackTrace();
        }catch (IOException ie) {
            ie.printStackTrace();
        }
    }
    
    private static void addToGraph(Map<Integer,List<ClusterNode>> graph, Map<Integer, ClusterNode> cnodeMap, 
                                    int leftId, int rightId) {
        ClusterNode rightClusterNode = cnodeMap.get(rightId);
        if(rightClusterNode != null) {
            if(graph.containsKey(leftId)) {
                List<ClusterNode> list = graph.get(leftId);
                list.add(rightClusterNode);
            }else {
                List<ClusterNode> list = new ArrayList<ClusterNode>();
                list.add(rightClusterNode);
                graph.put(leftId, list);
            }
        }
        //System.out.println(leftId + "-----> " + rightId);
    }

    public static void main(String args[]) {
        if(args.length < 2) {
            System.out.println("Usage: CInstanceCluster <conceptnet-name> <cluster-diameter>");
        }
        cluster(args[0], Short.parseShort(args[1]));
    }
    
}