/*
 * -------------------------------------------------------------------------
 *      $Id: LRPanel.java,v 1.4 2006/04/19 19:00:39 estewart Exp $
 * -------------------------------------------------------------------------
 *      Copyright (c) 1999 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.
 *--------------------------------------------------------------------------
 */

/*
 * LRPanel.java
 *
 * Created on August 19, 2003, 10:45 AM
 */

package com.imsl.demo.fitting;

import com.imsl.chart.*;
import java.util.Vector;
import java.awt.event.*;
import javax.swing.event.*;

public class LRPanel extends javax.swing.JPanel implements ActionListener, MouseListener {

    private double intercept, x1, mse;
    private boolean hasIntercept;
    private Chart chart;
    private AxisXY axis;
    private Data point, line;
    private Vector dataX, dataY;
    private double[] xRange, yRange;
    private javax.swing.JFrame parentFrame;
    private javax.swing.JRadioButtonMenuItem[] jRadioButtons;
    private final int numData = 4;
    private final String dataName[] = {"Blank", "Advertising", "Age and Height", "Cigarette Smoke"};


    /** Creates new form LRPanel */
    public LRPanel(javax.swing.JFrame parent) {
        this.parentFrame = parent;

        // Set initial values.
        hasIntercept = true;
        mse = 0.0;
        dataX = new Vector();
        dataY = new Vector();

        initComponents();
        setPreferredSize(new java.awt.Dimension(parent.getSize().width, (int)(0.85*parent.getSize().height)));
        jButtonGenerate.addActionListener(this);
        jButtonEnter.addActionListener(this);
        jButtonReset.addActionListener(this);
        checkBox.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                itemChanged();
            }
        });

        addDataButtons();
        setChart();
        jPanelChart.setPreferredSize(new java.awt.Dimension(parent.getSize().width, (int)(0.66*parent.getSize().height)));
        getData();
        update();
    }

    private void setChart() {
        chart = jPanelChart.getChart();
        chart.getLegend().setPaint(true);
        chart.getLegend().setViewport(0.45,0.55,0.01,0.1);
        axis = new AxisXY(chart);
        axis.setViewport(0.12,0.88,0.05,0.88);

        // Set axes ranges
        axis.getAxisX().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
        axis.getAxisY().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
        axis.getAxisX().setWindow(0.0, 50.0);
        axis.getAxisY().setWindow(0.0, 75.0);
        axis.getAxisX().setTextFormat(new java.text.DecimalFormat("###.##"));
        axis.getAxisY().setTextFormat(new java.text.DecimalFormat("###.##"));

        // Add MouseListeren to the chart.
        jPanelChart.addMouseListener(this);
    }

    // add the available data sets to the dataMenu
    private void addDataButtons() {
        jRadioButtons = new javax.swing.JRadioButtonMenuItem[numData];
        javax.swing.ButtonGroup group = new javax.swing.ButtonGroup();

        for (int i=0; i<numData; i++) {
            jRadioButtons[i] = new javax.swing.JRadioButtonMenuItem();
            jRadioButtons[i].setText(dataName[i]);

            jRadioButtons[i].addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    getData();
                    update();
                }
            });

            group.add(jRadioButtons[i]);
            ((FittingMain)parentFrame).getDataMenu().add(jRadioButtons[i]);
        }
        jRadioButtons[1].setSelected(true);
    }

    // clear and load data sets determined by the menu selection
    public void getData() {
        dataX.clear();
        dataY.clear();
        xRange = new double[2];
        yRange = new double[2];
        String xTitle = "";
        String yTitle = "";
        int selected = -1;
        for (int i=0; i<numData; i++) {
            if (jRadioButtons[i].isSelected()) {
                selected = i;
                break;
            }
        }
        switch (selected) {
            case 0: // blank
                xTitle = "X Value";
                yTitle = "Y Value";
                xRange[0] = 0.0;
                xRange[1] = 50.0;
                yRange[0] = 0.0;
                yRange[1] = 50.0;
                break;
            case 1: /* These data are obtianed from http://www-personal.buseco.monash.edu.au/~hyndman/TSDL/index.htm
                     * which quotes the source as Advertising and sales data Source: Makridakis, Wheelwright and Hyndman (1998).
                     * http://www.businessbookmall.com/zstat1.htm
                     */
                //double x[] = {12,20.5,21,15.5,15.3,23.5,24.5,21.3,23.5,28,24,15.5,17.3,25.3,25,36.5,36.5,29.6,30.5,28,26,21.5,19.7,19,16,20.7,26.5,30.6,32.3,29.5,28.3,31.3,32.2,26.4,23.4,16.4};
                //double y[] = {15,16,18,27,21,49,21,22,28,36,40,3,21,29,62,65,46,44,33,62,22,12,24,3,5,14,36,40,49,7,52,65,17,5,17,1};
                double x[] = {5,2,7,6,10,4,6,5,3,8};
                double y[] = {50,25,80,50,90,30,60,60,40,80};
                for (int i=0; i<x.length; i++) {
                    dataX.add(new Double(x[i]));
                    dataY.add(new Double(y[i]));
                }
                xTitle = "Advertising Expenditure, Thousands of Dollars";
                yTitle = "Sales Revenue, Thousands of Dollars";
                xRange[0] = 0.0;
                xRange[1] = 10.0;
                yRange[0] = 0.0;
                yRange[1] = 100.0;
                break;
            case 2: /* These data female age and weight data from
                     * Lewis, T. and Taylor, L.R. (1967), Introduction to Experimental Ecology, New York: Academic Press, Inc.
                     */
                double x2[] = {143,191,160,157,191,141,185,210,149,169,173,150,144,146,155,183,154,152,148,164,177,183,182,165,163,171,193,169,155,171,140,149,150,140,166,146,139,177,166,184,177,145,167,185,156,191,189,157,171,143,182,154,141,167,141,175,153,185,139,143,147,164,175,170,186,185,168,139,178,147,183,148,144,190,143,147,172,179,142,150,147,182,164,180,161,142,178,145,180,176,180,162,197,182,169,147,197,145,143,147,154,140,178,148,190,186,165,155,210,144,186};
                double y2[] = {56.3,62.5,62,64.5,65.3,61.8,63.3,65.5,64.3,62.3,62.8,61.3,59.5,60,61.3,64.5,60,60.5,60.5,65.3,61.3,66.5,65.5,55.5,56.5,63,59.8,61.5,62.3,62.5,53.8,58.3,59.5,53.5,61.5,56.3,57.5,61.8,59.3,62.3,61.3,59,62.3,60,54.5,63.3,64.3,60.5,61.5,61.5,62,61,56,61,61.3,60.3,63.3,59,61.5,51.3,61.3,58,60.8,64.3,57.8,65.3,61.5,52.8,63.5,55.8,64.3,56.3,55.8,66.8,58.3,59.5,64.8,63,56,54.5,51.5,64,63.3,61.3,59,56.5,61.5,58.8,63.3,61.3,59,58,61.5,58.3,62,59.8,64.8,57.8,55.5,58.3,62.8,60,66.5,59,56.8,57,61.3,66,62,61,63.5};
                for (int i=0; i<x2.length; i++) {
                    dataX.add(new Double(x2[i]));
                    dataY.add(new Double(y2[i]));
                }
                xTitle = "Age, months";
                yTitle = "Height, inches";
                xRange[0] = 100.0;
                xRange[1] = 250.0;
                yRange[0] = 50.0;
                yRange[1] = 75.0;
                break;
            case 3: /* These data are female age and weight data from
                     * Lewis, T. and Taylor, L.R. (1967), Introduction to Experimental Ecology, New York: Academic Press, Inc.
                     */
                double x3[] = {14.1,16,29.8,8,4.1,15,8.8,12.4,16.6,14.9,13.7,15.1,7.8,11.4,9,1,17,12.8,15.8,4.5,14.5,7.3,8.6,15.2,12};
                double y3[] = {13.6,16.6,23.5,10.2,5.4,15,9,12.3,16.3,15.4,13,14.4,10,10.2,9.5,1.5,18.5,12.6,17.5,4.9,15.9,8.5,10.6,13.9,14.9};
                for (int i=0; i<x3.length; i++) {
                    dataX.add(new Double(x3[i]));
                    dataY.add(new Double(y3[i]));
                }
                xTitle = "Tar, mg";
                yTitle = "CO, mg";
                xRange[0] = 0.0;
                xRange[1] = 30.0;
                yRange[0] = 0.0;
                yRange[1] = 30.0;
                break;
        }
        axis.getAxisX().getAxisTitle().setTitle(xTitle);
        axis.getAxisY().getAxisTitle().setTitle(yTitle);
        axis.getAxisX().setWindow(xRange);
        axis.getAxisY().setWindow(yRange);
    }

    // Draw the graph.
    private void drawGraph() {
        if ((dataX.size() != 0) && (dataY.size() != 0)) {
            double[] x = new double[dataX.size()];
            double[] y = new double[dataY.size()];

            for (int i = 0; i < x.length; i++) {
                x[i] = ((Double) dataX.get(i)).doubleValue();
                y[i] = ((Double) dataY.get(i)).doubleValue();
            }

            // Draw the points.
            point = new Data(axis, x, y);
            point.setDataType(Data.DATA_TYPE_MARKER);
            point.setMarkerType(Data.MARKER_TYPE_OCTAGON_X);
            point.setMarkerSize(0.8);
            point.setMarkerColor(java.awt.Color.blue);

            // Draw the regression line.
            getRegCoefficient(x, y);

            ChartFunction fcn = new ChartFunction() {
                public double f(double x) {
                    return x*x1 + intercept;
                }
            };
            line = new Data(axis, fcn, xRange[0], xRange[1]);
            line.setLineColor(java.awt.Color.red);
            line.setTitle("Linear Fit");
            line.setLineWidth(2);
        } else {
            intercept = 0.0;
            x1 = 0.0;
        }
    }

    // Get the coefficients for the regression line.
    // y = a + bx
    private void getRegCoefficient(double[] x, double[] y) {
        com.imsl.stat.LinearRegression reg = new com.imsl.stat.LinearRegression(1, hasIntercept);
        double[] ss = new double[1];

        for (int i = 0; i < y.length; i++) {
            ss[0] = x[i];
            reg.update(ss, y[i]);
        }
        double[] coe = reg.getCoefficients();

        mse = reg.getANOVA().getErrorMeanSquare();

        // Set the values depending on whether intercept is chosen.
        if (hasIntercept) {
            intercept = coe[0];
            x1 = coe[1];
        } else {
            intercept = 0.0;
            x1 = coe[0];
        }
    }

    //  Get the description line.
    private String getDescription() {
        java.text.DecimalFormat formatStat = new java.text.DecimalFormat("0.000");
        StringBuffer sb = new StringBuffer("# of Points:  ");

        if (dataX == null) {
            sb.append(0);
        } else {
            sb.append(dataX.size());
        }

        if (dataX.size() < 3) {
            sb.append("   Error Mean Square:  ??");
        } else {
            sb.append("   Error Mean Square:  " + formatStat.format(mse));
        }

        sb.append("\nRegression Line:  y = (" + formatStat.format(x1) +
        ")x + (" + formatStat.format(intercept) + ")");
        return sb.toString();
    }

    // Redraw the chart.
    public void update() {
        if (line != null) line.remove();
        if (point != null) point.remove();
        drawGraph();
        displayText.setText(getDescription());
        repaint();
    }

    public void setVisible(boolean show) {
        super.setVisible(show);
        if (show) {
            addDataButtons();
        }
    }

    /** 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.
     */
    private void initComponents() {//GEN-BEGIN:initComponents
        jPanel = new javax.swing.JPanel();
        jButtonEnter = new javax.swing.JButton();
        jButtonGenerate = new javax.swing.JButton();
        jLabelPoints = new javax.swing.JLabel();
        numField = new javax.swing.JTextField("6", 4);
        checkBox = new javax.swing.JCheckBox();
        jButtonReset = new javax.swing.JButton();
        displayText = new javax.swing.JTextArea(getDescription(), 2, 30);
        jPanelChart = new com.imsl.chart.JPanelChart();

        setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS));

        setPreferredSize(new java.awt.Dimension(500, 600));
        jButtonEnter.setText("Enter Points");
        jPanel.add(jButtonEnter);

        jButtonGenerate.setText("Generate");
        jPanel.add(jButtonGenerate);

        jLabelPoints.setText("# of Points: ");
        jPanel.add(jLabelPoints);

        numField.setColumns(4);
        numField.setText("6");
        jPanel.add(numField);

        checkBox.setSelected(true);
        checkBox.setText("intercept");
        jPanel.add(checkBox);

        jButtonReset.setText("Reset");
        jPanel.add(jButtonReset);

        add(jPanel);

        displayText.setEditable(false);
        displayText.setPreferredSize(new java.awt.Dimension(500, 16));
        add(displayText);

        jPanelChart.setPreferredSize(new java.awt.Dimension(500, 500));
        add(jPanelChart);

    }//GEN-END:initComponents

    // Implement ActionListener
    public void actionPerformed(ActionEvent e) {
        if (e.getActionCommand().equals("Enter Points")) {
            // Enter points using a text area.
            javax.swing.JTextArea textArea = new javax.swing.JTextArea(10, 5);
            javax.swing.JScrollPane scroll = new javax.swing.JScrollPane(textArea);

            int option = 0;
            try {
                option = javax.swing.JOptionPane.showConfirmDialog(this, scroll,
                    "Enter Points", javax.swing.JOptionPane.OK_CANCEL_OPTION);
                if (option == javax.swing.JOptionPane.CANCEL_OPTION) return;
            } catch (Exception ex) {
            }

            java.util.StringTokenizer lineToken =
                new java.util.StringTokenizer(textArea.getText(), "\n");
            while (lineToken.hasMoreElements()) {
                java.util.StringTokenizer token = new java.util.StringTokenizer(lineToken.nextToken(), "(){}[] ,\t\n");
                // If the line does not contain 2 numbers, then ignore.
                if (token.countTokens() == 2) {
                    try {
                        double x = Double.parseDouble(token.nextToken());
                        double y = Double.parseDouble(token.nextToken());

                        // Ignore any points that are not in the range.
                        if ((x >= xRange[0]) && (x <= xRange[1]) &&
                            (y >= yRange[0]) && (y <= yRange[1])) {
                            dataX.add(new Double(x));
                            dataY.add(new Double(y));
                        }
                    } catch (Exception ex) {
                    }
                }
            }
            update();
        } else if (e.getActionCommand().equals("Generate")) {
            int i = 0;
            try {
                i = Integer.parseInt(numField.getText());
            } catch (Exception exception) {
                i = 6;
                numField.setText("6");
            } finally {
                if (i <= 0) {
                    i = 6;
                    numField.setText("6");
                }
                for (int j = 0; j < i; j++) {
                    dataX.add(new Double(xRange[1] * Math.random() + xRange[0]));
                    dataY.add(new Double(yRange[1] * Math.random() + yRange[0]));
                }
                update();
            }
        } else {
            // Reset button is clicked.  Reset everything to default.
            numField.setText("6");
            checkBox.setSelected(true);
            hasIntercept = true;
            intercept = x1 = 0.0;
            mse = 0.0;
            dataX = new Vector();
            dataY = new Vector();
            update();
            getData();
            update();
        }
    }

    // ItemListener action method
    private void itemChanged() {
        hasIntercept = !hasIntercept;
        update();
    }

    // Implement MouseListener
    public void mouseClicked(MouseEvent e) {
        double user[] = {0,0};
        axis.mapDeviceToUser(e.getX(), e.getY(), user);

        // Do not add the point if it is not in the range.
        if ((user[0] < xRange[0]) || (user[0] > xRange[1]) ||
            (user[1] < yRange[0]) || (user[1] > yRange[1])) return;

        // Button is to add points.  Otherwise, remove points.
        if (e.getModifiers() == MouseEvent.BUTTON1_MASK) {
            dataX.add(new Double(user[0]));
            dataY.add(new Double(user[1]));
            update();
        } else {
            int idx = -1;
            double min = 0.01;
            final double ux = (user[0]-xRange[0])/(double)(xRange[1]-xRange[0]);
            final double uy = (user[1]-yRange[0])/(double)(yRange[1]-yRange[0]);

            for (int i = 0; i < dataX.size(); i++) {
                double x = (((Double)dataX.get(i)).doubleValue()-xRange[0])/
                            (double)(xRange[1]-xRange[0]);
                double y = (((Double)dataY.get(i)).doubleValue()-yRange[0])/
                            (double)(yRange[1]-yRange[0]);
                double dist = Math.sqrt((ux - x)*(ux - x) + (uy - y)*(uy - y));

                if (dist < min) {
                    min = dist;
                    idx = i;
                }
            }

            if (idx != -1) {
                dataX.remove(idx);
                dataY.remove(idx);
                update();
            }
        }
    }

    public void mousePressed(MouseEvent e) {
    }

    public void mouseReleased(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel jLabelPoints;
    private javax.swing.JButton jButtonGenerate;
    private javax.swing.JButton jButtonReset;
    private javax.swing.JTextField numField;
    private com.imsl.chart.JPanelChart jPanelChart;
    private javax.swing.JCheckBox checkBox;
    private javax.swing.JTextArea displayText;
    private javax.swing.JButton jButtonEnter;
    private javax.swing.JPanel jPanel;
    // End of variables declaration//GEN-END:variables
}