/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package it.cnr.iasi.giant.thread;

import cytoscape.Cytoscape;
import it.cnr.iasi.giant.action.RunClusteringAction;
import it.cnr.iasi.giant.algorithm.clustering.KMeansOriginal;
import it.cnr.iasi.giant.algorithm.distance.GraphUtil;
import it.cnr.iasi.giant.frame.KMeansInitFrame;
import it.cnr.iasi.giant.io.IOWriter;
import it.cnr.iasi.giant.util.ClusterValidation;
import it.cnr.iasi.giant.util.ClusteringUtil;
import it.cnr.iasi.giant.util.Helper;
import java.awt.Dimension;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import net.sf.javaml.clustering.Clusterer;
import net.sf.javaml.core.Dataset;
import net.sf.javaml.core.Instance;
import net.sf.javaml.tools.data.FileHandler;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

/**
 *
 * @author Fabio Cumbo
 * @info GIANT
 * @version v1.0
 * @organization IASI-CNR : Istituto di Analisi dei Sistemi ed Informatica 
 *                  "Antonio Ruberti" - Consiglio Nazionale delle Ricerche
 * 
 */
public class KMeansCascadeThread extends Thread {
    
    public KMeansCascadeThread() {}
    
    @Override
    public void run() {
        int k_min = (Integer)(KMeansInitFrame.getKmin_snipper().getValue());
        int k_max = (Integer)KMeansInitFrame.getKmax_snipper().getValue();
        int iter = (Integer)KMeansInitFrame.getIterCal_snipper().getValue();
        IOWriter writer = new IOWriter();
        
        int[][] distanceMatrix = ClusteringUtil.getDistMatrix();
        
        if (k_min>0 && k_max>0 && k_min<k_max && iter>0) {
            
            KMeansInitFrame.appendInConsole("COMPUTING DISTANCE MATRIX: please wait....");
                                    
            if (ClusteringUtil.getLastMetric().equals("NULL")) {
                if (KMeansInitFrame.getSp_matrix_radio().isSelected()) {
                        try {
                            GraphUtil graph = new GraphUtil(NetworkLoaderThread.getDegreeMap().keySet().size(), NetworkLoaderThread.getAdjMatrix());
                            distanceMatrix = ClusteringUtil.getDistanceMatrix("SP", graph, null);
                            ClusteringUtil.setTmpDistanceFile(writer.writeTmpDistanceMatrixFile(distanceMatrix));
                            ClusteringUtil.setMatrix(true);
                            ClusteringUtil.setLastMetric("SP");
                        } catch (IOException ex) {
                            Logger.getLogger(KMeansKThread.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                    else if (KMeansInitFrame.getHamming_matrix_radio().isSelected()) {
                        try {
                            distanceMatrix = ClusteringUtil.getDistanceMatrix("HAMMING", null, NetworkLoaderThread.getAdjMatrix());
                            ClusteringUtil.setTmpDistanceFile(writer.writeTmpDistanceMatrixFile(distanceMatrix));
                            ClusteringUtil.setMatrix(true);
                            ClusteringUtil.setLastMetric("HAMMING");
                        } catch (IOException ex) {
                            Logger.getLogger(KMeansKThread.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
            }
            else if (ClusteringUtil.getLastMetric().equals("SP")) {
                if (KMeansInitFrame.getHamming_matrix_radio().isSelected()) {
                        try {
                            distanceMatrix = ClusteringUtil.getDistanceMatrix("HAMMING", null, NetworkLoaderThread.getAdjMatrix());
                            ClusteringUtil.setTmpDistanceFile(writer.writeTmpDistanceMatrixFile(distanceMatrix));
                            ClusteringUtil.setMatrix(true);
                            ClusteringUtil.setLastMetric("HAMMING");
                        } catch (IOException ex) {
                            Logger.getLogger(KMeansKThread.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
            }
            else if (ClusteringUtil.getLastMetric().equals("HAMMING")) {
                if (KMeansInitFrame.getSp_matrix_radio().isSelected()) {
                        try {
                            GraphUtil graph = new GraphUtil(NetworkLoaderThread.getDegreeMap().keySet().size(), NetworkLoaderThread.getAdjMatrix());
                            distanceMatrix = ClusteringUtil.getDistanceMatrix("SP", graph, null);
                            ClusteringUtil.setTmpDistanceFile(writer.writeTmpDistanceMatrixFile(distanceMatrix));
                            ClusteringUtil.setMatrix(true);
                            ClusteringUtil.setLastMetric("SP");
                        } catch (IOException ex) {
                            Logger.getLogger(KMeansKThread.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
            }
            
            if (!ClusteringUtil.getTmpDistanceFile().equals("NULL")) {
                
                KMeansInitFrame.appendInConsole("COMPUTING DISTANCE MATRIX: completed!");
                KMeansInitFrame.appendInConsole("---------------------------------------------------------");
                
                try {
                                        
                    HashMap<Double, Double> k2validation = new HashMap<Double, Double>();
                    HashMap<Double, Double> k2sse = new HashMap<Double,Double>();
                    HashMap<Integer, HashMap<Integer, ArrayList<String>>> kclusters = new HashMap<Integer, HashMap<Integer, ArrayList<String>>>();
                    ArrayList<String> list = new ArrayList<String>(NetworkLoaderThread.getDegreeMap().keySet());    
                    ClusterValidation validation = new ClusterValidation();
                    
                    Dataset data = FileHandler.loadDataset(new File(ClusteringUtil.getTmpDistanceFile()), Cytoscape.getCurrentNetwork().nodesList().size()-1, " ");
                    for (int k=k_min; k<=k_max; k++) {
                        Clusterer km = new KMeansOriginal(k, iter);
                        
                        KMeansInitFrame.appendInConsole("KMEANS: starting KMeans algorithm at " + Helper.getCurrentTime());
                        KMeansInitFrame.appendInConsole("KMEANS: k = " + k + " - iterations = " + iter);
                        KMeansInitFrame.appendInConsole("---------------------------------------------------------");
                        KMeansInitFrame.appendInConsole("KMEANS: computing...");
                        
                        /*
                        * Cluster the data, it will be returned as an array of data sets, with
                        * each dataset representing a cluster
                        */

                        Dataset[] result = km.cluster(data);
                        System.out.println("Cluster count: " + result.length);
                        
                        //EXTRACT CLUSTERS FROM DATASET[]
                        //SET CLUSTERS

                        HashMap<Integer, ArrayList<String>> clusters = new HashMap<Integer, ArrayList<String>>();

                        int lb = Integer.MAX_VALUE;
                        for (int i=0; i<result.length; i++) {
                            Dataset ds = result[i];
                            Instance inst = ds.get(0);
                            if (lb > inst.getID())
                                lb = inst.getID();
                        }
                        int ub = lb + NetworkLoaderThread.getDegreeMap().keySet().size() -1;
                        
                        HashMap<Integer, Integer> newMap = RunClusteringAction.getNewMap(ub, lb);
                        HashMap<Integer, ArrayList<Integer>> forValidation = new HashMap<Integer, ArrayList<Integer>>();
                        
                        for (int i=0; i<result.length; i++) {
                            Dataset ds = result[i];
                            ArrayList<String> tmpString = new ArrayList<String>();
                            ArrayList<Integer> tmpInteger = new ArrayList<Integer>();
                            for (int j=0; j<ds.size(); j++) {
                                Instance inst = ds.get(j);
                                System.out.print(inst.getID() + " ");
                                tmpString.add(list.get(newMap.get(inst.getID())));
                                tmpInteger.add(newMap.get(inst.getID()));
                            }
                            clusters.put(i, tmpString);
                            forValidation.put(i, tmpInteger);
                            System.out.println();
                        }
                        
                        kclusters.put(k, clusters);
                        k2validation.put((double)k, validation.getValidation(forValidation, distanceMatrix));
                        k2sse.put((double)k, validation.getSSE(result));
                        
                        KMeansInitFrame.appendInConsole("KMEANS: completed at " + Helper.getCurrentTime());
                        KMeansInitFrame.appendInConsole("CLUSTERS: " + clusters.size());
                        for (int t=0; t<clusters.size(); t++)
                            KMeansInitFrame.appendInConsole("cluster " + (t+1) + "  -  size: " + clusters.get(t).size());
                        KMeansInitFrame.appendInConsole("---------------------------------------------------------");
                            
                    }
                    
                    ClusterLoaderThread.setClustersKRange(kclusters);
                    //PLOT VALIDATION
                    plot("VALIDATION", k2validation);
                    //PLOT SSE
                    plot("SSE", k2sse);
                                        
                } catch (IOException ex) {
                    KMeansInitFrame.appendInConsole("INTERNAL ERROR: please retry...");
                    KMeansInitFrame.appendInConsole("---------------------------------------------------------");
                }
                
            }
            else {
                KMeansInitFrame.appendInConsole("INTERNAL ERROR: please retry...");
                KMeansInitFrame.appendInConsole("---------------------------------------------------------");
            }
        }
        
        else {
            KMeansInitFrame.appendInConsole("INTERNAL ERROR: invalid k or iterations");
            KMeansInitFrame.appendInConsole("---------------------------------------------------------");
        }
                
    }
    
    private void plot(String title, HashMap<Double, Double> map) {
        final XYDataset dataset = createDataset(map);
        JFreeChart chart = ChartFactory.createXYLineChart(title, "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false);
        
        XYPlot xyPlot = chart.getXYPlot();
        xyPlot.setDomainCrosshairVisible(true);
        xyPlot.setRangeCrosshairVisible(true);
        final ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setPreferredSize(new java.awt.Dimension(450, 280));
        
        JFrame resultFrame = new JFrame();
        resultFrame.setContentPane(chartPanel);
        resultFrame.setSize(new Dimension(450, 280));
        resultFrame.setVisible(true);
    }
    
    private XYDataset createDataset(HashMap<Double, Double> map) {
	XYSeries series = new XYSeries("eval");
        
        for (double x: map.keySet()) {
            if (x!=Double.POSITIVE_INFINITY && map.get(x)!=Double.POSITIVE_INFINITY &&
                x!=Double.NEGATIVE_INFINITY && map.get(x)!=Double.NEGATIVE_INFINITY &&
                x!=Double.NaN && map.get(x)!=Double.NaN) {
                series.add(x, map.get(x));
            }
        }
        
        XYSeriesCollection dataset = new XYSeriesCollection();
        dataset.addSeries(series);      
        return dataset;
    }
    
}
