/*
 * -------------------------------------------------------------------------
 * $Id: SunSpotAspect.java,v 1.3 2004/05/26 17:57:13 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.Aspect;
import com.imsl.chart.*;
import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.util.*;
import javax.swing.*;

/**
 *  A modified version of the SunSpots ARMA demo that shows a method of 
 *  dynamically resizing a JFrameChart's aspect ratio based on the data
 *  contained therein.
 *
 *@author     JGH
 *@created    Modified October 9, 2002
 */
public class SunSpotAspect extends JFrameChart
implements ActionListener, MouseMotionListener {
    private JTextField displayField;
    private SimpleDateFormat dateFormat, yearFormat;
    private JButton ar;

    private NumberFormat intFormat;
    private Container cp;

    private Chart chart;
    private AxisXY axis;
    private Data data;

    private boolean arflag;
    private double[] current, x, y, xAxisLabels;
    private int num;
    private final double maxx, minx, maxy, miny, hDataLength, vDataLength;
    private java.awt.Dimension describeSize, screenSize, windowSize, defaultSize;


    /**
     *  Constructor for the SunSpotAspect object
     *
     *@param  exitOnClose  if not set, remove JFrameChart WindowListener
     */
    public SunSpotAspect(boolean exitOnClose) {
        super();
        setTitle("Aspect Ratio");
        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]);
            }
        }
        com.imsl.demo.gallery.Describe des = 
            new com.imsl.demo.gallery.Describe(this, "/com/imsl/demo/Aspect/Aspect.html");
        des.show();
        describeSize = des.getSize();

        screenSize = getToolkit().getScreenSize();
        int h = Math.min(screenSize.width/2, screenSize.height-describeSize.height-32);
        int w = (int)(h/0.8);
        defaultSize = new java.awt.Dimension(w,h);
        windowSize = new java.awt.Dimension(defaultSize);
        setSize(windowSize);
        setResizable(false);
        setLocation(screenSize.width-describeSize.width, describeSize.height);
        
        dateFormat = new SimpleDateFormat("M/yyyy");
        intFormat = NumberFormat.getIntegerInstance();

        // set default data
        y = new double[]{100.8, 81.6, 66.5, 34.8, 30.6, 7, 19.8, 92.5,
                154.4, 125.9, 84.8, 68.1, 38.5, 22.8, 10.2, 24.1, 82.9,
                132, 130.9, 118.1, 89.9, 66.6, 60, 46.9, 41, 21.3, 16,
                6.4, 4.1, 6.8, 14.5, 34, 45, 43.1, 47.5, 42.2, 28.1, 10.1,
                8.1, 2.5, 0, 1.4, 5, 12.2, 13.9, 35.4, 45.8, 41.1, 30.4,
                23.9, 15.7, 6.6, 4, 1.8, 8.5, 16.6, 36.3, 49.7, 62.5, 67,
                71, 47.8, 27.5, 8.5, 13.2, 56.9, 121.5, 138.3, 103.2,
                85.8, 63.2, 36.8, 24.2, 10.7, 15, 40.1, 61.5, 98.5, 124.3,
                95.9, 66.5, 64.5, 54.2, 39, 20.6, 6.7, 4.3, 22.8, 54.8,
                93.8, 95.7, 77.2, 59.1, 44, 47, 30.5, 16.3, 7.3, 37.3, 73.9};
        x = new double[y.length];
        for (int i = 0; i < y.length; i++) {
            x[i] = 1770.0 + i;
        }

        xAxisLabels = new double[y.length];
        for (int i = 0; i < y.length; i++) {
            xAxisLabels[i] = (new GregorianCalendar(1770 + i, 0, 1)).getTimeInMillis();
        }

        num = 0;
        minx = 1770;
        maxx = 1869.0;
        miny = 0.0;
        maxy = 154.0;
        hDataLength = maxx - minx;
        vDataLength = maxy - miny;
        arflag = false;
        current = new double[2];

        createChart();
        drawGraph();
        createComponents();
    }


    /**
     *  Shows a new SunSpotAspect
     *
     *@param  args  -noexit if passed, JFrameChart window listener isn't removed
     *@since
     */
    public static void main(String args[]) {
        boolean exitOnClose = true;
        if (args.length > 0 && args[0].equals("-noexit")) {
            exitOnClose = false;
        }
        new SunSpotAspect(exitOnClose).show();
    }


    public void actionPerformed(ActionEvent e) {
        if (e.getActionCommand().equals("Reset Aspect Ratio")) {
            windowSize = defaultSize;
            //windowSize.width = defaultSize.width;
            //windowSize.height = defaultSize.height;
            setLocation(screenSize.width-describeSize.width, describeSize.height);
            ar.setText("Correct Aspect Ratio");
        } else if (e.getActionCommand().equals("Correct Aspect Ratio")) {
            modifyAR();
            setLocation(Math.max(0,screenSize.width-windowSize.width), describeSize.height);
            ar.setText("Reset Aspect Ratio");
        }
        update();
    }

    public void mouseDragged(MouseEvent e) { }

    public void mouseMoved(MouseEvent e) {
        axis.mapDeviceToUser(e.getX(), e.getY(), current);
        displayField.setText(getDescription());
    }

    private String getDescription() {
        DecimalFormat nf = new DecimalFormat("0.0000");
        StringBuffer sb = new StringBuffer();
        sb.append("Cursor At:  X = "
                 + dateFormat.format(new Date((long) current[0]))
                 + "  Y = " + nf.format(current[1]));
        return sb.toString();
    }

    private void createChart() {
        chart = this.getChart();
        axis = new AxisXY(chart);
        axis.getAxisX().getAxisLabel().setTextFormat(dateFormat);
        axis.getAxisX().getAxisLabel().setTextAngle(90);
        axis.getAxisX().getAxisTitle().setTitle("Year");
        axis.getAxisY().getAxisLabel().setTextFormat(intFormat);
        axis.getAxisY().getAxisTitle().setTitle("Number of Sun Spots");
        chart.setTickLength(1.5);
        this.getPanel().addMouseMotionListener(this);
    }


    private void createComponents() {
        Container cp = this.getContentPane();
        ar = new JButton("Correct Aspect Ratio");
        ar.addActionListener(this);

        JPanel buttonPanel = new JPanel();
        buttonPanel.add(ar);

        JPanel displayPanel = new JPanel();
        displayField = new JTextField("Current position:  X = ??  Y = ??", 53);
        displayField.setBorder(null);
        displayField.setEditable(false);
        displayPanel.add(displayField);

        cp.add(buttonPanel, BorderLayout.NORTH);
        cp.add(displayPanel, BorderLayout.SOUTH);
    }


    private void drawGraph() {
        axis.getAxisX().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
        axis.getAxisX().setWindow(
                (new GregorianCalendar(1770, 0, 1)).getTimeInMillis(),
                (new GregorianCalendar(1870, 0, 1)).getTimeInMillis());
        axis.getAxisY().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
        axis.getAxisY().setWindow(0.0, 158.0);
        axis.setViewport(.1, .9, .05, .8);
        
        data = new Data(axis, xAxisLabels, y);
        data.setTitle("Actual data");
        data.setDataType(Data.DATA_TYPE_LINE | Data.DATA_TYPE_MARKER);
        data.setMarkerType(Data.MARKER_TYPE_FILLED_CIRCLE);
        data.setMarkerSize(0.4);
        data.setMarkerColor(Color.red);
        data.setLineColor(Color.blue);
    }


    private void modifyAR() {
        double[] x2 = new double[y.length];
        double[] devX = new double[y.length];
        double[] devY = new double[y.length];
        int[] devXY = new int[2];
        double startYear = 1770.0;
        for (int i = 0; i < y.length; i++) {
            x2[i] = startYear + i;
        }
        double banking = 100.0;
        long iterations = 1;
        while (banking > 45.0 && iterations < 8) {
            System.out.println("\niteration: " + iterations);
            for (int i = 0; i < y.length; i++) {
                axis.mapUserToDevice(x2[i], y[i], devXY);
                devX[i] = (double) devXY[0];
                devY[i] = (double) devXY[1];
                if (Math.IEEEremainder((double) i, 25.0) == 0.0) {
                    System.out.println("device coords[" + i + "] = " + devX[i] + ", "
                             + devY[i] + "," + (windowSize.height - devY[i]));
                    System.out.println("x[" + i + "], y[" + i + "] = " + x2[i] + ", " + y[i]);
                }
            }
            double maxdevX;
            double mindevX;
            double maxdevY;
            double mindevY;
            maxdevX = mindevX = maxdevY = mindevY = 0.0;
            for (int i = 0; i < y.length; i++) {
                if (devX[i] < mindevX) {
                    mindevX = devX[i];
                }
                if (devX[i] > maxdevX) {
                    maxdevX = devX[i];
                }
                if (devY[i] < mindevY) {
                    mindevY = devY[i];
                }
                if (devY[i] > maxdevY) {
                    maxdevY = devY[i];
                }
            }
            System.out.println("mindevX = " + mindevX);
            System.out.println("maxdevX = " + maxdevX);
            System.out.println("mindevY = " + mindevY);
            System.out.println("maxdevY = " + maxdevY);
            double hPixelLength = maxdevX - mindevX;
            double vPixelLength = maxdevY - mindevY;
            System.out.println("hPixelLength = " + hPixelLength);
            System.out.println("vPixelLength = " + vPixelLength);
            double aspectRatio = vPixelLength / hPixelLength;
            double aspectRatio2 = aspectRatio * aspectRatio;
            double vconv = vPixelLength / vDataLength;
            double hconv = hPixelLength / hDataLength;
            double temp = 1.0 / vDataLength;
            System.out.println("aspectRatio = " + aspectRatio);
            System.out.println("aspectRatio2 = " + aspectRatio2);
            System.out.println("vconv = " + vconv);
            System.out.println("hconv = " + hconv);
            System.out.println("temp = " + temp);
            double[] ydiffs = new double[y.length - 1];
            for (int i = 1; i < y.length; i++) {
                ydiffs[i - 1] = Math.abs(y[i] - y[i - 1]);
                if (Math.IEEEremainder((double) i, 20.0) == 0.0) {
                    System.out.println("ydiffs[" + (i - 1) + "] = " + ydiffs[i - 1]);
                }
            }
            double[] vbar = new double[ydiffs.length];
            double[] vbar2 = new double[ydiffs.length];
            for (int i = 0; i < ydiffs.length; i++) {
                vbar[i] = ydiffs[i] * temp;
                vbar2[i] = vbar[i] * vbar[i];
                if (Math.IEEEremainder((double) i, 20.0) == 0.0) {
                    System.out.println("vbar[" + i + "] = " + vbar[i]);
                    System.out.println("vbar2[" + i + "] = " + vbar2[i]);
                }
            }
            temp = 1.0 / hDataLength;
            double[] xdiffs = new double[y.length - 1];
            for (int i = 1; i < y.length; i++) {
                xdiffs[i - 1] = Math.abs(x2[i] - x2[i - 1]);
            }
            double[] hbar = new double[ydiffs.length];
            double[] hbar2 = new double[ydiffs.length];
            for (int i = 0; i < ydiffs.length; i++) {
                hbar[i] = xdiffs[i] * temp;
                hbar2[i] = hbar[i] * hbar[i];
                if (Math.IEEEremainder((double) i, 20.0) == 0.0) {
                    System.out.println("hbar[" + i + "] = " + hbar[i]);
                    System.out.println("hbar2[" + i + "] = " + hbar2[i]);
                }
            }
            double[] root = new double[ydiffs.length];
            for (int i = 0; i < ydiffs.length; i++) {
                root[i] = hbar2[i] + (aspectRatio2 * vbar2[i]);
                root[i] = Math.sqrt(root[i]);
                if (Math.IEEEremainder((double) i, 20.0) == 0.0) {
                    System.out.println("root[" + i + "] = " + root[i]);
                }
            }
            double denominator = 0.0;
            for (int i = 0; i < ydiffs.length; i++) {
                denominator = denominator + root[i];
            }
            double numerator = 0.0;
            for (int i = 0; i < ydiffs.length; i++) {
                numerator = numerator + (Math.atan(aspectRatio * vbar[i] / hbar[i]) * root[i]);
            }
            System.out.println("denominator = " + denominator);
            System.out.println("numerator = " + numerator);
            banking = Math.toDegrees(numerator / denominator);
            System.out.println("banking = " + banking);
            if (banking > 45.0) {
                if (iterations > 3) {
                    windowSize.width += 105;
                } else {
                    windowSize.height -= 100;
                }
            }
            iterations++;
            System.out.println("windowSize = " + windowSize);
        }
    }


    private void update() {
        if (data != null) {
            data.remove();
        }
        drawGraph();
        setSize(windowSize);
        invalidate();
        validate();
        repaint();
    }
}