/*
 * -------------------------------------------------------------------------
 *      $Id: Pick.java,v 1.3 2004/05/26 18:23:29 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.
 *--------------------------------------------------------------------------
 */

package com.imsl.demo.Pick;
import com.imsl.demo.gallery.Describe;
import com.imsl.math.PrintMatrix;
import com.imsl.math.PrintMatrixFormat;
import com.imsl.stat.Covariances;
import com.imsl.stat.NormTwoSample;
import com.imsl.chart.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.text.*;
import javax.swing.*;

/**
 *
 * @author  brophy
 * @created April 24, 2001
 */
public class Pick extends JFrameChart implements MouseListener {

    static final java.text.DecimalFormat formatStat = new java.text.DecimalFormat("0.000");
    private Selector    selector;
    private Selector    selectorRectangle;
    private Selector    selectorPolygon;
    private Report      report;
    private AxisXY      axis;
    private double      x[], y[];
    private double      xSelected[], ySelected[];
    private double      xNotSelected[], yNotSelected[];
    private Data        dataNotSelected;
    private Data        dataSelected;
    private int         w;

    /**
     *  Creates a new instance of Pick
     */
    public Pick(boolean exitOnClose) {
        setTitle("Select Points");
        if (!exitOnClose) {
            // remove the WindowListener,  installed by JFrameChart, that
            // exits the application when the window is closed.
            Object l[] = getListeners(java.awt.event.WindowListener.class);
            for (int k = 0;  k < l.length;  k++) {
                removeWindowListener((java.awt.event.WindowListener)l[k]);
            }
        }

        Describe des = new Describe(this, "/com/imsl/demo/Pick/Pick.html");
        des.show();
        Dimension ds = des.getSize();

        Dimension ss = getToolkit().getScreenSize();
        w = Math.min(ss.width/2, ss.height-ds.height-32);
        setSize(w, w);
        int xloc = Math.min(ss.width-ds.width, w-305);  // leave room for report window
        setLocation(xloc, ds.height);

        Chart chart = getChart();
        axis = new AxisXY(chart);
        selectorRectangle = new Selector();
        selectorPolygon = new SelectorPolygon();
        initButtons();
        report = new Report();
        getPanel().addMouseListener(this);

        int n = 200;
        x = new double[n];
        y = new double[n];
        for (int k = 0;  k < n;  k++) {
            x[k] = Math.random();
            y[k] = Math.random();
        }
        axis.setDataType(Data.DATA_TYPE_MARKER);
        dataSelected = new Data(axis, x, y);
        dataSelected.setMarkerColor("blue");
        dataSelected.setMarkerType(Data.MARKER_TYPE_FILLED_CIRCLE);
        dataNotSelected = new Data(axis, x, y);
        dataNotSelected.setMarkerColor("red");
        dataNotSelected.setMarkerType(Data.MARKER_TYPE_HOLLOW_SQUARE);
        select(null);
    }


    /**
     *  Initialize the buttons to select rectangular or polgonal sector.
     */
    private void initButtons() {
        ButtonGroup bg = new ButtonGroup();
        JPanel panelControl = new JPanel();
        ((FlowLayout)panelControl.getLayout()).setAlignment(FlowLayout.LEADING);
        panelControl.add(new JLabel("Selector: "));

        JToggleButton buttonRectangle = new JToggleButton("Rectangle");
        panelControl.add(buttonRectangle);
        bg.add(buttonRectangle);
        buttonRectangle.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                selector = selectorRectangle;
            }
        });

        JToggleButton buttonPolygon = new JToggleButton("Polygon");
        panelControl.add(buttonPolygon);
        bg.add(buttonPolygon);
        buttonPolygon.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                selector = selectorPolygon;
            }
        });

        buttonRectangle.setSelected(true);
        selector = selectorRectangle;

        getContentPane().add(panelControl, java.awt.BorderLayout.NORTH);
    }

    public void mouseClicked(MouseEvent mouseEvent) {
    }

    public void mouseEntered(MouseEvent mouseEvent) {
    }

    public void mouseExited(MouseEvent mouseEvent) {
    }

    public void mousePressed(MouseEvent mouseEvent) {
        if (mouseEvent.getButton() == 1) {
            report.setVisible(false);
            report.setVisible(false);
            select(null);
            repaint();
            selector.start(mouseEvent);
            getPanel().addMouseMotionListener(selector);
            double user[] = {0,0};
            axis.mapDeviceToUser(mouseEvent.getX(), mouseEvent.getY(), user);
        }
    }

    public void mouseReleased(MouseEvent mouseEvent) {
        if (mouseEvent.getButton() == 1) {
            selector.stop(mouseEvent);
            getPanel().removeMouseMotionListener(selector);
            select(selector.getSelectedShape());
            repaint();
            new Thread(new Runnable() {
                public void run() {
                    try {
                        updateReport();
                    } catch (Exception e) {
                        JOptionPane.showMessageDialog(report, e.getMessage(), "Exception", JOptionPane.ERROR_MESSAGE);
                    }
                }
            }).start();
        }
    }


    private void select(Shape shape) {
        int dev[] = {0,0};
        int nSelected = 0;
        for (int k = 0;  k < x.length;  k++) {
            axis.mapUserToDevice(x[k], y[k], dev);
            if (shape != null  &&  shape.contains(dev[0], dev[1])) nSelected++;
        }
        xSelected = new double[nSelected];
        ySelected = new double[nSelected];
        xNotSelected = new double[x.length-nSelected];
        yNotSelected = new double[y.length-nSelected];
        int iSelected = 0;
        int iNotSelected = 0;

        for (int k = 0;  k < x.length;  k++) {
            axis.mapUserToDevice(x[k], y[k], dev);
            if (shape != null  &&  shape.contains(dev[0], dev[1])) {
                xSelected[iSelected] = x[k];
                ySelected[iSelected++] = y[k];
            } else {
                xNotSelected[iNotSelected] = x[k];
                yNotSelected[iNotSelected++] = y[k];
            }
        }
        dataSelected.setX(xSelected);
        dataSelected.setY(ySelected);
        dataNotSelected.setX(xNotSelected);
        dataNotSelected.setY(yNotSelected);
    }

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

    private void reportInterval(PrintStream ps, String format, double v0, double v1) {
        MessageFormat mf = new MessageFormat(format+" = [<b>{0}</b>, <b>{1}</b>]");
        Object args[] = {new Double(v0), new Double(v1)};
        ps.print(mf.format(args));
        ps.println("<br>");
    }

    private void reportHeading(PrintStream ps, String format) {
        ps.println("<h3><font color='blue'>"+format+"</font></h3>");
    }


    void updateReport() throws Exception {
        if (xSelected.length == 0) {
            JOptionPane.showMessageDialog(report, "No Points Selected", "Pick", JOptionPane.WARNING_MESSAGE);
            return;
        }

        if (xSelected.length == 1) {
            JOptionPane.showMessageDialog(report, "Only One Point Selected", "Pick", JOptionPane.WARNING_MESSAGE);
            return;
        }

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(baos);

        /*
         *  Variance-Covariance matrix
         */
        Covariances co = new Covariances(new double[][]{xSelected,ySelected});
        reportHeading(ps, "Variance-Covariance Matrix");
        PrintMatrix pm = new PrintMatrix(ps);
        NumberFormat nf = NumberFormat.getInstance();
        nf.setMinimumFractionDigits(4);
        PrintMatrixFormat pmf = new PrintMatrixFormat();
        pmf.setNumberFormat(nf);
        pm.setMatrixType(PrintMatrix.UPPER_TRIANGULAR);
        double covar[][] = co.compute(Covariances.VARIANCE_COVARIANCE_MATRIX);
        pm.printHTML(pmf, covar, 2, 2) ;

        /*
         * NormTwoSample
         */
        NormTwoSample nts = new NormTwoSample(xSelected, ySelected);
        double mean = nts.getDiffMean();
        reportHeading(ps, "Norm Two Sample");
        reportValue(ps, "mean(x1)-mean(x2)", mean);
        reportValue(ps, "mean(x1)", nts.getMeanX() );
        reportValue(ps, "mean(x2)", nts.getMeanY() );

        double pVar = nts.getPooledVariance();
        reportValue(ps, "pooled variance", pVar);

        double loCI = nts.getLowerCIDiff();
        double upCI = nts.getUpperCIDiff();
        reportInterval(ps, "95% CI for the mean", loCI, upCI);

        loCI = nts.getLowerCIDiff();
        upCI = nts.getUpperCIDiff();
        reportInterval(ps, "95% CI for the ueq mean", loCI, upCI);

        reportHeading(ps, "T Test Results");
        double tDF = nts.getTTestDF();
        double tT = nts.getTTest();
        double tPval = nts.getTTestP();
        reportValue(ps, "T default", tDF);
        reportValue(ps, "t", tT);
        reportValue(ps, "p-value", tPval);

        double stdevX = nts.getStdDevX();
        double stdevY = nts.getStdDevY();
        reportValue(ps, "stdev(x1)", stdevX);
        reportValue(ps, "stdev(x2)", stdevY);

        /*
         * Generate a report of the points selected.
         */
        reportHeading(ps, "Selected <b>"+xSelected.length+"</b> Points");

        ps.println("<table border='1'>");
        ps.println("<tr><td align='center'><i>k</i></td><td align='center'><i>x</i></td><td align='center'><i>y</i></td></tr>");
        for (int k = 0;  k < xSelected.length;  k++) {
            ps.println("<tr><td align='center'><i>");
            ps.println(k);
            ps.println("</i></td><td>");
            ps.println(formatStat.format(xSelected[k]));
            ps.println("</td><td>");
            ps.println(formatStat.format(ySelected[k]));
            ps.println("</td></tr>\r\n");
        }
        ps.println("</table></body></html>");


        report.setText(baos.toString());
        Dimension size = getSize();
        Point loc = getLocationOnScreen();
        report.setLocation(loc.x+size.width,loc.y);
        report.show();
        report.repaint();
    }



    static public void main(String args[]) {
        boolean exitOnClose = true;
        if (args.length > 0  && args[0].equals("-noexit"))  exitOnClose = false;
        new Pick(exitOnClose).show();
    }

    /**
     *  Select points using a rectangle.
     */
    class Selector implements MouseMotionListener {
        protected final Color colorFill = new Color(255,0,255,128);
        protected final Color colorOutline = new Color(0,255,0);
        protected int     xA, yA, xB, yB;

        void start(MouseEvent mouseEvent) {
            xA = xB = mouseEvent.getX();
            yA = yB = mouseEvent.getY();
        }

        void stop(MouseEvent mouseEvent) {
            paint(mouseEvent, false);
        }

        public void mouseDragged(MouseEvent mouseEvent) {
            paint(mouseEvent, true);
        }

        public void mouseMoved(MouseEvent mouseEvent) {
        }

        protected void paint(MouseEvent mouseEvent, boolean erase) {
            Graphics2D g = (Graphics2D)Pick.this.getPanel().getGraphics();
            if (erase)  paint(g);
            updateShape(mouseEvent);
            paint(g);
            g.dispose();
        }

        /**
         * Draw the selected shape.
         */
        protected void paint(Graphics2D g) {
            Shape shape = getSelectedShape();
            g.setXORMode(colorOutline);
            g.draw(shape);
            g.setXORMode(colorFill);
            g.fill(shape);
        }

        protected void updateShape(MouseEvent mouseEvent) {
            xB = mouseEvent.getX();
            yB = mouseEvent.getY();
        }

        Shape getSelectedShape() {
            return new Rectangle(Math.min(xA,xB), Math.min(yA,yB), Math.abs(xB-xA), Math.abs(yB-yA));
        }
    }


    /**
     *  Select points using a polygon.
     */
    class SelectorPolygon extends Selector {
        protected Polygon   polygon;

        void start(MouseEvent mouseEvent) {
            super.start(mouseEvent);
            polygon = new Polygon();
            polygon.addPoint(xA, yA);
        }

        protected void updateShape(MouseEvent mouseEvent) {
            polygon.addPoint(mouseEvent.getX(), mouseEvent.getY());
        }

        Shape getSelectedShape() {
            return polygon;
        }
    }


    private class Report extends JDialog {
        private JEditorPane jEditorPane;

        public Report() {
            super(Pick.this, false);
            setTitle("Report");
            jEditorPane = new javax.swing.JEditorPane();
            jEditorPane.setContentType("text/html");
            jEditorPane.setEditable(false);
            JScrollPane scroll = new JScrollPane(jEditorPane);
            getContentPane().add(scroll);
            scroll.setPreferredSize(new Dimension(300, w-27));
            pack();
        }

        void setText(String text) {
            jEditorPane.setText(text);
        }
    }
}