/*
 * WANDORA
 * Knowledge Extraction, Management, and Publishing Application
 * http://wandora.org
 * 
 * Copyright (C) 2004-2023 Wandora Team
 * 
 * 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/>.
 *
 * 
 * HistogramPanel.java
 *
 * Created on 29. toukokuuta 2007, 10:54
 */

package org.wandora.application.tools.statistics;




import java.awt.Color;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.io.PrintWriter;
import java.io.StringWriter;

import javax.swing.JDialog;
import javax.swing.JPanel;

import org.wandora.application.gui.simple.SimpleButton;
import org.wandora.utils.ClipboardBox;
import org.wandora.utils.swing.GuiTools;




/**
 *
 * @author  olli
 */
public class HistogramPanel extends javax.swing.JPanel {

	private static final long serialVersionUID = 1L;

	private int[] data;
    private int maxValue;
    private int dataSize;
    private int zoomFactor;
    private int mouseOverIndex;
    private double average;
    private int median;
    private String infoString;
    private Dialog parentDialog;
    
    private String infoPattern;
    private String mousePattern;
    
    public static final String defaultInfoPattern="Sum = %1$d ; Sum values = %2$d ; Ave = %3$.2f ; Median = %4$d";
    public static final String defaultMousePattern="%1$d, %2$d";
    
    /** Creates new form HistogramPanel */
    public HistogramPanel(int[] data) {
        this(data,null,defaultInfoPattern,defaultMousePattern);
    }
    public HistogramPanel(int[] data,Dialog parentDialog,String infoPattern,String mousePattern) {
        this.data=data;
        this.parentDialog=parentDialog;
        this.infoPattern=infoPattern;
        this.mousePattern=mousePattern;
        this.maxValue=1;
        this.dataSize=0;
        
        long sumNodes=0;
        long sumLinks=0;
        
        for(int i=0;i<data.length;i++){
            sumLinks+=i*data[i];
            sumNodes+=data[i];
            if(data[i]>0) dataSize=i;
            if(data[i]>maxValue) maxValue=data[i];
        }      
        dataSize++;
        
        if(sumNodes>0) average=(double)sumLinks/(double)sumNodes;
        long sum2=0;
        for(int i=0;i<data.length;i++){
            sum2+=data[i];
            if(sum2>sumNodes/2){
                median=i;
                break;
            }
        }
        
        StringWriter sw=new StringWriter();
        PrintWriter pw=new PrintWriter(sw);
        pw.printf(infoPattern,sumNodes, sumLinks, average, median);
        infoString=sw.toString();
        
        zoomFactor=1;
        mouseOverIndex=-1;
        initComponents();
        if(parentDialog==null) closeButton.setVisible(false);
        infoLabel.setText(infoString);
        updateContentSize();
    }
    
    public static void showHistogramPanel(int[] data,Frame parent,String title,boolean modal){
        showHistogramPanel(data,parent,title,modal,defaultInfoPattern,defaultMousePattern);
    }
    public static void showHistogramPanel(int[] data,Frame parent,String title,boolean modal,String infoPattern,String mousePattern){
        JDialog jd=new JDialog(parent,title,modal);
        jd.getContentPane().setLayout(new GridBagLayout());
        HistogramPanel p=new HistogramPanel(data,jd,infoPattern,mousePattern);
        GridBagConstraints gbc=new GridBagConstraints();
        gbc.fill=gbc.BOTH;
        gbc.gridx=0;
        gbc.gridy=0;
        gbc.weightx=1.0;
        gbc.weighty=1.0;
        jd.getContentPane().add(p,gbc);
        jd.setSize(700,400);
        GuiTools.centerWindow(jd,parent);
        jd.setVisible(true);
    }
    
    private void updateContentSize(){
        int h=scrollPane.getViewport().getHeight();
        contentPanel.setMinimumSize(new Dimension(zoomFactor*dataSize,h));
        contentPanel.setMaximumSize(new Dimension(zoomFactor*dataSize,h));
        contentPanel.setPreferredSize(new Dimension(zoomFactor*dataSize,h));
        contentPanel.revalidate();
        contentPanel.repaint();
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        jPanel2 = new javax.swing.JPanel();
        infoLabel = new org.wandora.application.gui.simple.SimpleLabel();
        mouseLabel = new org.wandora.application.gui.simple.SimpleLabel();
        scrollPane = new javax.swing.JScrollPane();
        contentPanel = new ContentPanel();
        jPanel3 = new javax.swing.JPanel();
        jPanel4 = new javax.swing.JPanel();
        zoomOutButton = new SimpleButton();
        zoomInButton = new SimpleButton();
        copyButton = new SimpleButton();
        closeButton = new SimpleButton();

        addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentResized(java.awt.event.ComponentEvent evt) {
                formComponentResized(evt);
            }
        });
        setLayout(new java.awt.GridBagLayout());

        jPanel2.setLayout(new java.awt.GridBagLayout());

        infoLabel.setText(" ");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        jPanel2.add(infoLabel, gridBagConstraints);

        mouseLabel.setText(" ");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        jPanel2.add(mouseLabel, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(4, 4, 0, 4);
        add(jPanel2, gridBagConstraints);

        scrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        scrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);

        contentPanel.setPreferredSize(new java.awt.Dimension(400, 240));
        contentPanel.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseEntered(java.awt.event.MouseEvent evt) {
                contentPanelMouseEntered(evt);
            }
            public void mouseExited(java.awt.event.MouseEvent evt) {
                contentPanelMouseExited(evt);
            }
        });
        contentPanel.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
            public void mouseMoved(java.awt.event.MouseEvent evt) {
                contentPanelMouseMoved(evt);
            }
        });
        contentPanel.setLayout(new java.awt.BorderLayout());
        scrollPane.setViewportView(contentPanel);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
        add(scrollPane, gridBagConstraints);

        jPanel3.setLayout(new java.awt.GridBagLayout());

        jPanel4.setLayout(new java.awt.GridBagLayout());

        zoomOutButton.setText("-");
        zoomOutButton.setToolTipText("Zoom out...");
        zoomOutButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                zoomOutButtonActionPerformed(evt);
            }
        });
        jPanel4.add(zoomOutButton, new java.awt.GridBagConstraints());

        zoomInButton.setText("+");
        zoomInButton.setToolTipText("Zoom in...");
        zoomInButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                zoomInButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
        jPanel4.add(zoomInButton, gridBagConstraints);

        copyButton.setText("Copy to clipboard");
        copyButton.setToolTipText("Copy histogram data to system clipboard...");
        copyButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                copyButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
        jPanel4.add(copyButton, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        jPanel3.add(jPanel4, gridBagConstraints);

        closeButton.setText("Close");
        closeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                closeButtonActionPerformed(evt);
            }
        });
        jPanel3.add(closeButton, new java.awt.GridBagConstraints());

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 4);
        add(jPanel3, gridBagConstraints);
    }// </editor-fold>//GEN-END:initComponents

    private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed
        parentDialog.setVisible(false);
    }//GEN-LAST:event_closeButtonActionPerformed

    private void copyButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_copyButtonActionPerformed
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<dataSize;i++){
            sb.append(i+"\t"+data[i]+"\n");
        }
        ClipboardBox.setClipboard(sb.toString());
    }//GEN-LAST:event_copyButtonActionPerformed

    private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized
        updateContentSize();
    }//GEN-LAST:event_formComponentResized

    private void zoomInButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed
        zoomFactor++;
        updateContentSize();
    }//GEN-LAST:event_zoomInButtonActionPerformed

    private void zoomOutButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed
        if(zoomFactor>1){
            zoomFactor--;
            updateContentSize();
        }
    }//GEN-LAST:event_zoomOutButtonActionPerformed

    private void contentPanelMouseMoved(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_contentPanelMouseMoved
        int x=evt.getX();
        mouseOverIndex=x/zoomFactor;
        if(mouseOverIndex<data.length && mouseOverIndex>=0){
            StringWriter sw=new StringWriter();
            PrintWriter pw=new PrintWriter(sw);
            pw.printf(mousePattern,mouseOverIndex,data[mouseOverIndex]);
            mouseLabel.setText(sw.toString());
        }
        else{
            mouseOverIndex=-1;
            mouseLabel.setText(" ");
        }
        contentPanel.repaint();
    }//GEN-LAST:event_contentPanelMouseMoved

    private void contentPanelMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_contentPanelMouseExited
        mouseOverIndex=-1;
        mouseLabel.setText(" ");
    }//GEN-LAST:event_contentPanelMouseExited

    private void contentPanelMouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_contentPanelMouseEntered

    }//GEN-LAST:event_contentPanelMouseEntered
        
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton closeButton;
    private javax.swing.JPanel contentPanel;
    private javax.swing.JButton copyButton;
    private javax.swing.JLabel infoLabel;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JPanel jPanel4;
    private javax.swing.JLabel mouseLabel;
    private javax.swing.JScrollPane scrollPane;
    private javax.swing.JButton zoomInButton;
    private javax.swing.JButton zoomOutButton;
    // End of variables declaration//GEN-END:variables
    
    private class ContentPanel extends JPanel {

		private static final long serialVersionUID = 1L;

		private Color barColor=new Color(0,0,0);
        private Color bgColor=new Color(255,255,255);
        private Color mouseBgColor=new Color(255,128,128);
        private Color mouseColor=new Color(128,0,0);
        private Color averageBgColor=new Color(128,255,128);
        private Color averageColor=new Color(0,128,0);
        private Color medianBgColor=new Color(128,255,255);
        private Color medianColor=new Color(0,128,128);
        
        public ContentPanel(){
        }
        public void paint(Graphics g){
            Graphics2D g2=(Graphics2D)g;
            
            int h=this.getHeight();
            double offs=Math.log(10);
            double f=(h-1)/(Math.log(maxValue+10)-offs);

            g2.setColor(bgColor);
            g2.fillRect(0,0,this.getWidth(),this.getHeight());
            Color bg=null;
            Color fg=null;
            int ave=(int)(average+0.5);
            for(int i=0;i<dataSize;i++){
                int v=(int)Math.ceil(f*(Math.log(data[i]+10)-offs));
                if(i==mouseOverIndex){
                    bg=mouseBgColor;
                    fg=mouseColor;
                }
                else if(i==ave){
                    bg=averageBgColor;
                    fg=averageColor;
                }
                else if(i==median){
                    bg=medianBgColor;
                    fg=medianColor;
                }
                else {
                    bg=bgColor;
                    fg=barColor;
                }
                if(bg!=bgColor) {
                    g2.setColor(bg);
                    g2.fillRect(i*zoomFactor,0,zoomFactor,h-v);
                }
                g2.setColor(fg);
                g2.fillRect(i*zoomFactor,h-v,zoomFactor,v);
            }
        }
    }
}
