/*
 * -------------------------------------------------------------------------
 *      $Id: HCluster.java,v 1.3 2006/03/24 19:41:48 estewart Exp $
 * -------------------------------------------------------------------------
 *      Copyright (c) 2006 Visual Numerics Inc. All Rights Reserved.
 *
 *      This software is confidential information which is proprietary to
 *      and a trade secret of Visual Numerics, Inc.  Use, duplication or
 *      disclosure is subject to the terms of an appropriate license
 *      agreement.
 *
 *      VISUAL NUMERICS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
 *      SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING
 *      BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 *      FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. VISUAL
 *      NUMERICS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 *      AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR
 *      ITS DERIVATIVES.
 *--------------------------------------------------------------------------
 */

/*
 * HCluster.java
 *
 * Created on December 27, 2005, 2:53 PM
 */
package com.imsl.demo.cluster;

import java.io.*;
import java.util.*;
import javax.swing.*;
import java.awt.Color;
import com.imsl.chart.*;
import com.imsl.stat.*;

public class HCluster extends JPanel {

    private Chart chart;
    private AxisXY axis, axisH, axisV;
    private double[][] data, data2, data3;
    private String[] labelX, labelY;
    private int yMin = 0;
    private int yMax = 118;
    private int xMin = 0;
    private int xMax = 60;
    private JFrame parentFrame;
    private JPanelChart jPanelChart;
    private ClusterHierarchical clink, clink2;
    private ClusterReport report;
    private int optionDiss = 0;
    private int optionCH = 2;
    private JButton runButton;
    private JComboBox jComboBoxDiss, jComboBoxCH;
    private JToggleButton reportToggleButton;
    private java.awt.Font labelFont = new java.awt.Font("Arial",java.awt.Font.PLAIN,6);
    private Colormap cmap = Colormap.RED_TEMPERATURE;            // new MavaColormap()
    private Color[] cArray = new Color[] {Color.BLUE, Color.GREEN, Color.RED, Color.ORANGE};
    private boolean showHighlight = false;
    private boolean showLabels = false;

    /** Creates new form HCluster */
    public HCluster(JFrame parent) {
        this.parentFrame = parent;
        try {
            initComponents();
            getData();
            setChart();

            setPreferredSize(new java.awt.Dimension(parent.getSize().width, (int)(0.95*parent.getSize().height)));
            jPanelChart.setPreferredSize(new java.awt.Dimension(parent.getSize().width, (int)(0.75*parent.getSize().height)));
        } catch (Exception e) {
            e.printStackTrace();
        }
        report = new ClusterReport(parentFrame, "ClusterHierarchical Report", reportToggleButton);
        java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
        java.io.PrintStream ps = new java.io.PrintStream(baos);
        ps.println("<h3><font color='blue'>Row Labels</font></h3>");
        for (int i=0; i<labelY.length; i++) {
            ps.print("<b>"+(i+1)+": </b>"+labelY[i]+"<br>");
        }
        ps.println("<h3><font color='blue'>Column Labels</font></h3>");
        for (int i=0; i<labelX.length; i++) {
            ps.print("<b>"+(i+1)+": </b>"+labelX[i]+"<br>");
        }
        report.setText(baos.toString());
    }

    // clear and load data sets determined by the menu selection
    public void getData() throws IOException, java.text.ParseException {
        labelX = new String[xMax];
        labelY = new String[yMax];
        data = new double[xMax][yMax];

        // read first data set
        InputStream is = getClass().getResourceAsStream("a_matrix118s.csv");
        Reader fr = new InputStreamReader(is);
        LineNumberReader lnr = new LineNumberReader(fr);
        int lineNum = 0;

        while (true) {
            String line = lnr.readLine();
            if (line == null)  break;
            lineNum++;
            StringTokenizer st = new StringTokenizer(line,",");
            if (lineNum == 1) {
               int holder = Integer.valueOf(st.nextToken()).intValue();
               for (int i=0; i<xMax; i++) {
                  labelX[i] = st.nextToken();
               }
            } else {
               labelY[yMax+3-lineNum-2] = st.nextToken();
               for (int i=0; i<xMax; i++) {
                  data[i][yMax+3-lineNum-2] = Double.valueOf(st.nextToken()).doubleValue();
               }
            }
        }
    }


    private void clusterData() {
        try {
            Dissimilarities dist = new Dissimilarities(com.imsl.math.Matrix.transpose(data), optionDiss, 0, 1);
            double[][] distanceMatrix = dist.getDistanceMatrix();
            clink = new ClusterHierarchical(dist.getDistanceMatrix(), optionCH, 0);    // 2,0
        } catch (com.imsl.IMSLException e) {
            e.printStackTrace();
        }
    }

    private void clusterData2() {
        try {
            Dissimilarities dist = new Dissimilarities(data2, optionDiss, 0, 1);
            double[][] distanceMatrix = dist.getDistanceMatrix();
            clink2 = new ClusterHierarchical(dist.getDistanceMatrix(), optionCH, 0);    // 2,0
        } catch (com.imsl.IMSLException e) {
            e.printStackTrace();
        }
    }


    private void initComponents() {
        jPanelChart = new com.imsl.chart.JPanelChart();

        JLabel jLabelDiss = new JLabel("Dissimilarities Distance Method: ");
        Label labelDiss[] = {
                new Label("Euclidean distance", 0),
                new Label("Sum of the absolute differences", 1),
                new Label("Maximum difference", 2),
                new Label("Mahalanobis distance", 3),
                new Label("Cosine of the angle between vectors", 4),
                new Label("Angle in radians between the vectors", 5),
                //new Label("Correlation coefficient", 6),
                new Label("Absolute value of the correlation coefficient", 7),
                new Label("Number of exact matches", 8)
                };
        jComboBoxDiss = new JComboBox();
        for (int i = 0; i < labelDiss.length; i++) {
            jComboBoxDiss.addItem(labelDiss[i]);
        }
        jComboBoxDiss.setSelectedIndex(0);
        jComboBoxDiss.addActionListener(
            new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    jComboBoxDissActionPerformed(evt);
                }
            });

        JLabel jLabelCH = new JLabel("Cluster Hierarchical Method: ");
        Label labelCH[] = {
                new Label("Single Linkage (minimum distance)", 0),
                new Label("Complete Linkage (maximum distance)", 1),
                new Label("Average distance within clusters", 2),
                new Label("Average distance between clusters", 3),
                new Label("Ward's method", 4)
                };
        jComboBoxCH = new JComboBox();
        for (int i = 0; i < labelCH.length; i++) {
            jComboBoxCH.addItem(labelCH[i]);
        }
        jComboBoxCH.setSelectedIndex(2);
        jComboBoxCH.addActionListener(
            new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    jComboBoxCHActionPerformed(evt);
                }
            });

        JPanel jPanelTop = new JPanel();
        jPanelTop.setLayout(new BoxLayout(jPanelTop, BoxLayout.Y_AXIS));
        JPanel jPanelDiss = new JPanel(new java.awt.GridLayout(1,2));
        JPanel jPanelCH = new JPanel(new java.awt.GridLayout(1,2));

        runButton = new JButton("Run Analysis");
        runButton.setForeground(new java.awt.Color(0, 153, 0));
        runButton.addActionListener(
            new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    runAnalysis();
                }
            });

        reportToggleButton = new JToggleButton("View Report");
        reportToggleButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                reportToggleButtonActionPerformed(evt);
            }
        });

        JToggleButton highlightToggleButton = new JToggleButton("Highlight Clusters");
        highlightToggleButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                showHighlight = !showHighlight;
            }
        });

        JToggleButton labelToggleButton = new JToggleButton("Show Labels");
        labelToggleButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                showLabels = !showLabels;
            }
        });

        JPanel jPanelButtons = new JPanel();
        jPanelButtons.add(runButton);
        jPanelButtons.add(highlightToggleButton);
        jPanelButtons.add(labelToggleButton);
        jPanelButtons.add(reportToggleButton);

        jPanelDiss.add(jLabelDiss);
        jPanelDiss.add(jComboBoxDiss);
        jPanelTop.add(jPanelDiss);
        jPanelCH.add(jLabelCH);
        jPanelCH.add(jComboBoxCH);
        jPanelTop.add(jPanelCH);
        jPanelTop.add(jPanelButtons);

        add(jPanelTop, java.awt.BorderLayout.NORTH);
        add(jPanelChart);
    }

    private void setChart() {
        jPanelChart.setChart(new Chart());
        chart = jPanelChart.getChart();
        axis = new AxisXY(chart);
        Heatmap heatmap = new Heatmap(axis, 0, xMax-1, 0, yMax-1, -3.0, 12.0, data, cmap);
        heatmap.getHeatmapLegend().setPaint(true);
        heatmap.getHeatmapLegend().setTextFormat("##");
        heatmap.getHeatmapLegend().setTitle("Value");
        heatmap.getHeatmapLegend().setViewport(0.90, 0.98, 0.05, 0.5);

        Axis1D hy = axis.getAxisY();
        hy.setAutoscaleOutput(Axis.AUTOSCALE_OFF);
        hy.setWindow(0,yMax-1);
        //hy.getAxisLabel().setLabels(labelY);
        //hy.getAxisLabel().setFont(labelFont);
        Axis1D hx = axis.getAxisX();
        hx.setAutoscaleOutput(Axis.AUTOSCALE_OFF);
        hx.setWindow(0,xMax-1);
        //hx.getAxisLabel().setLabels(labelX);
        //hx.getAxisLabel().setFont(labelFont);
        //hx.getAxisLabel().setTextAngle(90);
    }

    private void runAnalysis() {
        runButton.setText("Update");
        axis.remove();
        if (axisH != null) axisH.remove();
        if (axisV != null) axisV.remove();

        clusterData();

        Chart chartBogus = new Chart();
        AxisXY axisBogus = new AxisXY(chartBogus);
        Dendrogram den = new Dendrogram(axisBogus, clink, Data.DENDROGRAM_TYPE_HORIZONTAL);
        int[] orderH = den.getOrder();

        data2 = new double[xMax][yMax];
        for (int i=0; i<xMax; i++) {
            for (int j=0; j<yMax; j++) {
                data2[i][j] = data[i][orderH[j]];
            }
        }

        clusterData2();

        den = new Dendrogram(axisBogus, clink2, Data.DENDROGRAM_TYPE_HORIZONTAL);
        int[] orderV = den.getOrder();

        data3 = new double[xMax][yMax];
        for (int i=0; i<xMax; i++) {
            for (int j=0; j<yMax; j++) {
                data3[i][j] = data2[orderV[i]][j];
            }
        }

        String[] labelYsort = new String[yMax];
        String[] labelXsort = new String[xMax];
        for (int i=0; i<yMax; i++) {
            labelYsort[i] = labelY[orderH[i]];
        }
        for (int i=0; i<xMax; i++) {
            labelXsort[i] = labelX[orderV[i]];
        }

        axis = new AxisXY(chart);
        axis.setViewport(0.1,0.75,0.25,0.9);

        Heatmap heatmap = new Heatmap(axis, 0, xMax-1, 0, yMax-1, -3.0, 12.0, data3, cmap);
            Axis1D hy = axis.getAxisY();
            hy.setAutoscaleOutput(Axis.AUTOSCALE_OFF);
            hy.setWindow(0,yMax-1);
            Axis1D hx = axis.getAxisX();
            hx.setAutoscaleOutput(Axis.AUTOSCALE_OFF);
            hx.setWindow(0,xMax-1);
        if (showLabels) {
            hy.getAxisLabel().setLabels(labelYsort);
            hy.getAxisLabel().setFont(labelFont);
            hx.getAxisLabel().setLabels(labelXsort);
            hx.getAxisLabel().setFont(labelFont);
            hx.getAxisLabel().setTextAngle(90);
        } else {
            hy.getAxisLabel().setTextFormat("###");
            hx.getAxisLabel().setTextFormat("###");
        }

        axisH = new AxisXY(chart);
        axisH.setViewport(0.75,0.9,0.25,0.9);
        Dendrogram denH = new Dendrogram(axisH, clink, Data.DENDROGRAM_TYPE_HORIZONTAL);
        if (showHighlight) denH.setLineColor(cArray);
        axisH.getAxisX().getMinorTick().setPaint(false);
        axisH.getAxisX().getMajorTick().setPaint(false);
        axisH.getAxisX().getAxisLabel().setLabels(new String[] {" "});
        axisH.getAxisY().getMinorTick().setPaint(false);
        axisH.getAxisY().getMajorTick().setPaint(false);
        axisH.getAxisY().getAxisLabel().setLabels(new String[] {" "});
        axisH.getAxisX().setPaint(false);

        axisV = new AxisXY(chart);
        axisV.setViewport(0.1,0.75,0.1,0.25);
        Dendrogram denV = new Dendrogram(axisV, clink2, Data.DENDROGRAM_TYPE_VERTICAL);
        if (showHighlight) denV.setLineColor(cArray);
        axisV.getAxisX().getMinorTick().setPaint(false);
        axisV.getAxisX().getMajorTick().setPaint(false);
        axisV.getAxisX().getAxisLabel().setLabels(new String[] {" "});
        axisV.getAxisY().getMinorTick().setPaint(false);
        axisV.getAxisY().getMajorTick().setPaint(false);
        axisV.getAxisY().getAxisLabel().setLabels(new String[] {" "});
        axisV.getAxisY().setPaint(false);

        // create the report
        java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
        java.io.PrintStream ps = new java.io.PrintStream(baos);

        ps.println("<h3><font color='blue'>Row Labels</font></h3>");
        for (int i=0; i<labelYsort.length; i++) {
            ps.print("<b>"+(i+1)+": </b>"+labelYsort[i]+"<br>");
        }

        ps.println("<h3><font color='blue'>Column Labels</font></h3>");
        for (int i=0; i<labelXsort.length; i++) {
            ps.print("<b>"+(i+1)+": </b>"+labelXsort[i]+"<br>");
        }

        double[] cl = clink.getClusterLevel();
        ps.println("<h3><font color='blue'>Cluster Levels, Horizontal</font></h3>");
        for (int i=0; i<cl.length; i++) {
            reportValue(ps, (i+1)+" ", cl[i]);
        }

        cl = clink2.getClusterLevel();
        ps.println("<h3><font color='blue'>Cluster Levels, Vertical</font></h3>");
        for (int i=0; i<cl.length; i++) {
            reportValue(ps, (i+1)+" ", cl[i]);
        }
        report.setText(baos.toString());

        repaint();
    }

    private void reportToggleButtonActionPerformed(java.awt.event.ActionEvent evt) {
        if (reportToggleButton.isSelected()) {
            java.awt.Dimension size = parentFrame.getSize();
            java.awt.Point loc = parentFrame.getLocationOnScreen();
            report.setLocation(loc.x+size.width,loc.y);
            report.show();
        } else {
            report.setVisible(false);
        }
    }


    /**
     *  Dissimilarities Combo Box selection.
     *
     *    @param  evt  The moust event.
     */
    private void jComboBoxDissActionPerformed(java.awt.event.ActionEvent evt) {
        optionDiss = ((Label)jComboBoxDiss.getSelectedItem()).index;
    }

    /**
     *  ClusterHierarchical Combo Box selection.
     *
     *    @param  evt  The moust event.
     */
    private void jComboBoxCHActionPerformed(java.awt.event.ActionEvent evt) {
        optionCH = ((Label)jComboBoxCH.getSelectedItem()).index;
    }


    private void reportValue(java.io.PrintStream ps, String label, double value) {
        java.text.MessageFormat mf = new java.text.MessageFormat(label+" = <b>{0}</b>");
        Object args[] = {new Double(value)};
        ps.print(mf.format(args));
        ps.println("<br>");
    }


    private class MavaColormap implements com.imsl.chart.Colormap {
        int[] c = new int[] {10157607,10092070,10092070,10026534,10026534,10026534,9960998,9960998,9895462,9895462,9895462,9829926,9829926,9764390,9764390,9764390,9698854,9698854,9633318,9633318,9633318,9567782,9567782,9502246,9502246,
                             9502502,9436709,9371172,9305635,9305634,9240097,9174561,9108768,9108767,9043230,8977693,8912157,8912156,8846619,8780826,8780825,8715289,8649752,8584215,8584214,8518421,8452885,8387348,8387347,8321810,
                             8256273,8256273,8124944,8059152,7993359,7927567,7861774,7795982,7730189,7664397,7533068,7467276,7401483,7335691,7269898,7204106,7138313,7072521,6941192,6875400,6809607,6743815,6678022,6612230,6546437,
                             6480645,6415109,6283013,6151173,6084869,5953029,5886725,5754885,5688325,5556485,5490181,5358341,5292037,5160197,5093894,4961798,4829958,4763654,4631814,4565510,4433670,4367110,4235270,4168966,4037126,
                             3970822,3838982,3772679,3640070,3507718,3375366,3242758,3110406,2978054,2845446,2713094,2580742,2448134,2315782,2183430,2051078,1852934,1786118,1653766,1521158,1388806,1256454,1123846,991494,859142,726534,
                             594182,461830,329478,656902,984582,1377799,1705479,2098695,2426376,2754056,3147272,3474953,3868169,4195849,4523530,4916746,5244427,5637643,5965323,6293004,6686220,7013900,7407117,7734797,8062477,8455694,
                             8783374,9176591,9241870,9372942,9504014,9569550,9700622,9831694,9962766,10028302,10159374,10290446,10421518,10487054,10618126,10748941,10814477,10945549,11076621,11207693,11273229,11404301,11535373,
                             11666445,11731981,11863053,11994125,12125197,12124940,12190476,12256012,12321548,12387084,12452620,12518156,12583692,12649228,12714764,12780300,12845836,12845580,12911116,12976652,13042188,13107724,
                             13173260,13238796,13304332,13369868,13435404,13500940,13566476,13632012,13697548,13828620,13894156,14025229,14156301,14221837,14352910,14418446,14549518,14680591,14746127,14877199,15008272,15073808,
                             15204880,15270416,15401489,15532561,15598097,15729170,15794706,15925778,16056851,16122387,16253459,16384532,16384533,16384534,16384791,16384792,16385049,16385050,16450843,16450844,16450845,16451102,
                             16451103,16451360,16516897,16517154,16517155,16517412,16517413,16517414,16583207,16583208,16583465,16583466,16583723,16583724,16649517};

        public java.awt.Color color(double t) {
            int i = (int)(t*255);
            return new java.awt.Color(c[i]);
        }
    }


    /**
     *  Label objects are used in conjunction with the JComboBox.
     *
     *@author     John Brophy
     *@created    September 25, 2002
     */
    private static class Label {
        String label;
        int index;

        /**
         *  Constructor for the Label object.
         *
         *@param  label  The label String.
         *@param  index  The label index.
         */
        Label(String label, int index) {
            this.label = label;
            this.index = index;
        }


        /**
         *  Returns the label object.
         *
         *@return    The label.
         */
        public String toString() {
            return label;
        }
    }
}