/*
 * -------------------------------------------------------------------------
 *      $Id: Simulate.java,v 1.6 2004/05/26 19:03:12 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.risk;
import com.imsl.chart.*;
import com.imsl.math.Cholesky;
import com.imsl.stat.*;
import com.imsl.IMSLException;
import java.text.*;
import java.awt.GridBagConstraints;
import javax.swing.*;

/**
 *
 * @author  brophy
 * @created January 31, 2002
 */
public class Simulate extends JFrameChart {
    private Database    db;
    private AxisXY      axis;
    private Bar         bar;
    private Statistics  statistics;
    private int         nSamples = 100000;
    private double      portfolioValues[] = {1000, 500, 1000, 0, 0};
    private Cholesky    chol;
    private double      bins[];
    private Summary     summary;
    private EditPortfolio editPortfolio;
    private MonteCarloProgress progress;
    private boolean     cancelMonteCarlo;
    private PortfolioPie portfolioPie;

    /** Creates new ChartReturns */
    public Simulate(Database db, double covar[][]) throws IMSLException {
        this.db = db;
        setTitle("Risk Analysis");

        Object l[] = getListeners(java.awt.event.WindowListener.class);
        for (int k = 0;  k < l.length;  k++) {
            removeWindowListener((java.awt.event.WindowListener)l[k]);
        }

        Chart chart = getChart();
        chart.setTextColor("purple");
        //chart.getBackground().setFillColor(new java.awt.Color(240,240,240));
        axis = new AxisXY(chart);
        axis.getAxisX().getAxisTitle().setTitle("Daily Returns");
        axis.getAxisY().getAxisTitle().setTitle("Probability");
        axis.getAxisX().getAxisLabel().setTextFormat(new java.text.DecimalFormat("0.0%"));
        axis.getAxisY().getAxisLabel().setTextFormat(new java.text.DecimalFormat("0%"));
        axis.getAxisX().getGrid().setPaint(true);
        axis.getAxisY().getGrid().setPaint(true);
        axis.getAxisX().getGrid().setLineColor("lightGray");
        axis.getAxisY().getGrid().setLineColor("lightGray");

        statistics = new Statistics();
        statistics.setLocation(25,500);
        statistics.show();

        chol = new Cholesky(covar);
        compute();

        editPortfolio = new EditPortfolio(db.getTickers());
        editPortfolio.setLocation(25,100);
        //editPortfolio.show();

        portfolioPie = new PortfolioPie(this);
        portfolioPie.update(db.getTickers(), portfolioValues);

        //com.imsl.math.PrintMatrixFormat pmf = new com.imsl.math.PrintMatrixFormat();
        //pmf.setNumberFormat(new java.text.DecimalFormat("0.000E0"));
        //new com.imsl.math.PrintMatrix().printHTML(pmf,covar,covar.length,covar.length);
    }


    public void setVisible(boolean vis) {
        super.setVisible(vis);
        if (vis) {
            java.awt.Point chartLoc = getLocationOnScreen();
            java.awt.Dimension statSize = statistics.getSize();
            statistics.setLocation(chartLoc.x-statSize.width,chartLoc.y);

            java.awt.Dimension portSize = editPortfolio.getSize();
            editPortfolio.setLocation(chartLoc.x,chartLoc.y-portSize.height);

            //java.awt.Dimension pieSize = portfolioPie.getSize();
            //portfolioPie.setLocation(chartLoc.x-pieSize.width,chartLoc.y-pieSize.height);
            portfolioPie.setSize(225,225);
            portfolioPie.setLocation(chartLoc.x-225,chartLoc.y-225);
        }
        statistics.setVisible(vis);
        editPortfolio.setVisible(vis);
        portfolioPie.setVisible(vis);
    }

    public void cancelMonteCarlo() {
        cancelMonteCarlo = true;
    }


    private void compute() {
        cancelMonteCarlo = false;
        progress = new MonteCarloProgress(this, 0, nSamples);
        progress.setLocationRelativeTo(this);
        progress.setVisible(true);
        new Thread(new Runnable() {
            public void run() {
                runMonteCarlo();
            }
        }).start();
    }

    public void runMonteCarlo() {
        int nVariables = portfolioValues.length;
        int nBins = 50;
        double max = 0.05;

        summary = new Summary();
        com.imsl.stat.Random random = new com.imsl.stat.Random();
        double center = portfolio(new double[nVariables]);

        // Setup the bins
        bins = new double[nBins];
        final double dx = 2.*max/nBins;
        double x[] = new double[nBins];
        for (int k = 0;  k < nBins;  k++) {
            x[k] = -max + (k+0.5)*dx;
        }

        for (int p = 0;  p < nSamples;  p++) {
            double r[] = random.nextMultivariateNormal(nVariables, chol);
            double value = portfolio(r);
            double t = (value-center)/center;
            summary.update(t);
            if (p%10000 == 0)  progress.setValue(p);
            if (cancelMonteCarlo) {
                progress.setVisible(false);
                return;
            }
            int j = (int)Math.round((t+max-0.5*dx)/dx);
            if (j >= 0  &&  j < nBins)  bins[j]++;
        }

        final double xx[] = x;
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                updateChart(xx, dx);
            }
        });
    }

    private void updateChart(final double x[], double dx) {
        progress.setVisible(false);

        //getChart().getChartTitle().setTitle();
        if (bar != null)  bar.remove();
        for (int k = 0;  k < bins.length;  k++)  bins[k] /= nSamples;
        bar = new Bar(axis, x, bins);
        bar.setBarType(Bar.BAR_TYPE_VERTICAL);
        bar.setFillColor("green");
        bar.setBarWidth(0.5*dx);
        axis.getAxisY().setWindow(0, 0.10);
        axis.getAxisY().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
        repaint();

        StringBuffer sb = new StringBuffer(2048);
        sb.append("<html><body>");
        sb.append("<p align='center'><b>Returns</b></p>");
        sb.append("<table>");
        appendRow(sb, "Samples", nSamples);
        appendRow(sb, "Minimum", summary.getMinimum());
        appendRow(sb, "Maximum", summary.getMaximum());
        appendRow(sb, "Mean", summary.getMean());
        appendRow(sb, "Standard Deviation", summary.getStandardDeviation());
        appendRow(sb, "Variance", summary.getVariance());
        appendRow(sb, "Skewness", summary.getSkewness());
        appendRow(sb, "Kurtosis", summary.getKurtosis());
        sb.append("</table></body></html>");
        statistics.setText(sb.toString());
        statistics.repaint();
    }

    static final DecimalFormat formatStat = new DecimalFormat("0.00000E0");

    private void appendRow(StringBuffer sb, String title, double value) {
        sb.append("<tr><td align='right'><i>");
        sb.append(title);
        sb.append("</i></td><td align='left'>");
        sb.append(formatStat.format(value));
        sb.append("</td><tr>");
    }


    double portfolio(double returns[]) {
        double sum = 0.0;
        for (int k = 0;  k < returns.length;  k++) {
            sum += portfolioValues[k] * (1.0+returns[k]);
        }
        return sum;
    }


    private class Statistics extends JDialog {
        private JEditorPane jEditorPane;

        public Statistics() {
            super(Simulate.this, false);
            setTitle("Summary Statistics");
            jEditorPane = new JEditorPane();
            jEditorPane.setContentType("text/html");
            jEditorPane.setPreferredSize(new java.awt.Dimension(250,375));
            getContentPane().add(jEditorPane);
            pack();
        }

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


    private class EditPortfolio extends JDialog {
        private JTextField  jTextFieldSamples;
        private JTextField  jTextField[];

        EditPortfolio(String tickers[]) {
            setTitle("Edit");

            getContentPane().setLayout(new java.awt.GridBagLayout());
            GridBagConstraints gridBagConstraints = new GridBagConstraints();

            JLabel jLabel = new JLabel("Samples");
            jLabel.setHorizontalAlignment(SwingConstants.RIGHT);
            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
            getContentPane().add(jLabel, gridBagConstraints);

            jTextFieldSamples = new JTextField(Integer.toString(nSamples));
            jTextFieldSamples.setHorizontalAlignment(JTextField.RIGHT);
            jTextFieldSamples.setPreferredSize(new java.awt.Dimension(100,30));
            gridBagConstraints = new GridBagConstraints();
            gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
            gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
            gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
            getContentPane().add(jTextFieldSamples, gridBagConstraints);

            jTextField = new JTextField[tickers.length];
            for (int k = 0;  k < tickers.length;  k++) {
                jLabel = new JLabel(tickers[k]);
                jLabel.setHorizontalAlignment(SwingConstants.RIGHT);
                gridBagConstraints = new java.awt.GridBagConstraints();
                gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
                getContentPane().add(jLabel, gridBagConstraints);

                jTextField[k] = new JTextField(Double.toString(portfolioValues[k]));
                jTextField[k].setHorizontalAlignment(JTextField.RIGHT);
                jTextField[k].setPreferredSize(new java.awt.Dimension(100,30));
                gridBagConstraints = new GridBagConstraints();
                gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
                gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
                gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
                getContentPane().add(jTextField[k], gridBagConstraints);
            }

            JButton jButton = new JButton("Update");
            gridBagConstraints = new GridBagConstraints();
            gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
            getContentPane().add(jButton, gridBagConstraints);
            jButton.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    update();
                }
            });

            pack();
        }

        private void update() {
            nSamples = Integer.valueOf(jTextFieldSamples.getText()).intValue();
            for (int k = 0;  k < jTextField.length;  k++) {
                portfolioValues[k] = Double.valueOf(jTextField[k].getText()).doubleValue();
            }
            portfolioPie.update(db.getTickers(), portfolioValues);
            Simulate.this.compute();
            Simulate.this.repaint();
        }
    }
}