/*
 * -------------------------------------------------------------------------
 *      $Id: Database.java,v 1.8 2004/05/26 18:54:55 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 java.util.*;
import java.io.*;
import javax.swing.JOptionPane;

/**
 *
 * @author  brophy
 * @created January 24, 2002
 */
class Database implements Serializable {
    static public final int INTERVAL_YEAR = 0;
    static public final int INTERVAL_MONTH = 1;
    static public final int INTERVAL_QUARTER = 2;
    static public final int INTERVAL_WEEK = 3;
    static public final int INTERVAL_ALL = 4;

    ///static private final long serialVersionUID = -4975277050625648285L;
    private HashMap     hashSeries;

    /** Creates new Database */
    public Database() {
        try {
            hashSeries = new HashMap(10);
            readAll("spx");
            readAll("ftse");
            readAll("n225");
            readAll("tnx");
            readAll("xau");
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, e.toString(), "alert", JOptionPane.ERROR_MESSAGE);
        }
    }

    void save(String filename) throws IOException {
        FileOutputStream fos = new FileOutputStream(filename);
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(this);
        oos.close();
        fos.close();
    }

    static Database load(String filename) throws IOException {
        try {
            FileInputStream fis = new FileInputStream(filename);
            ObjectInputStream ois = new ObjectInputStream(fis);
            Database db = (Database)ois.readObject();
            ois.close();
            fis.close();
            return db;
        } catch (ClassNotFoundException e) {
            return null;
        }
    }

    public String[] getTickers() {
        Set keys = hashSeries.keySet();
        SortedSet ss = new TreeSet();
        Iterator iter = keys.iterator();
        while (iter.hasNext()) {
            ss.add(iter.next());
        }
        return (String[])ss.toArray(new String[0]);
    }

    public Series getSeries(String ticker) {
        return (Series)hashSeries.get(ticker);
    }

    public Series getSeries(String ticker, int period, int interval) {
        Series daily = getSeries(ticker);
        ListSeries listSeries = new ListSeries();
        GregorianCalendar calStart = new GregorianCalendar();
        switch (interval) {
            case INTERVAL_ALL:
                calStart.add(GregorianCalendar.YEAR, -200);
                break;
            case INTERVAL_MONTH:
                calStart.add(GregorianCalendar.MONTH, -1);
                break;
            case INTERVAL_YEAR:
                calStart.add(GregorianCalendar.YEAR, -1);
                break;
            case INTERVAL_QUARTER:
                calStart.add(GregorianCalendar.MONTH, -3);
                break;
            case INTERVAL_WEEK:
                calStart.add(GregorianCalendar.DATE, -7);
                break;
        }
        GregorianCalendar cal = new GregorianCalendar();
        int lastPeriod = -1;
        double open = 0;
        double close = 0;
        double high = 0;
        double low = 0;
        Date startDate = null;
        for (int k = 0;  k < daily.date.length;  k++) {
            Date date = new java.util.Date((long)daily.date[k]);
            cal.setTime(date);
            if (cal.before(calStart))  continue;
            int thisPeriod = cal.get(period);
            if (thisPeriod != lastPeriod) {
                if (lastPeriod != -1) {
                    listSeries.add(startDate, high, low, close, open);
                }
                startDate = date;
                open = daily.open[k];
                close = daily.close[k];
                high = daily.high[k];
                low = daily.low[k];
            } else {
                close = daily.close[k];
                high = Math.max(high,daily.high[k]);
                low = Math.min(low,daily.low[k]);
            }
            lastPeriod = thisPeriod;
        }
        listSeries.add(startDate, high, low, close, open);
        return new Series(listSeries);
    }


    private void readAll(String ticker) throws Exception {
        // read the data
        ListSeries listSeries = (ListSeries)hashSeries.get(ticker);
        if (listSeries == null) {
            listSeries = new ListSeries();
            hashSeries.put(ticker, listSeries);
        }
        String s = "com/imsl/demo/risk/" + ticker + ".csv";
        java.io.InputStream is = getClass().getClassLoader().getResourceAsStream(s);
        ReadYahooCSV rsd = new ReadYahooCSV(is);
        while (rsd.next()) {
            listSeries.listDate.add(rsd.getObject("Date"));
            listSeries.listOpen.add(rsd.getObject("Open"));
            listSeries.listHigh.add(rsd.getObject("High"));
            listSeries.listLow.add(rsd.getObject("Low"));
            listSeries.listClose.add(rsd.getObject("Close"));
        }
        rsd.close();
        hashSeries.put(ticker, new Series(listSeries));
    }


    public double[][] getReturns() {
        String tickers[] = getTickers();
        int nTickers = tickers.length;
        Database.Series series[] = new Database.Series[nTickers];
        for (int k = 0;  k < nTickers;  k++) {
            series[k] = getSeries(tickers[k]);
        }
        List list = new ArrayList(1000);
        int index[] = new int[nTickers];
        //double lastDate = new java.util.Date(100,0,1).getTime();
        NEXT_OBS:
        while (true) {
            if (index[0] >= series[0].date.length)  break NEXT_OBS;
            double date = series[0].date[index[0]];
            //if (date < lastDate)  break;
            for (int k = 1;  k < nTickers;  k++) {
                while (true) {
                    if (index[k] >= series[k].date.length)  break NEXT_OBS;
                    double d2 = series[k].date[index[k]];
                    if (d2 == date)  break;
                    if (d2 > date) {
                        index[k]++;
                        continue NEXT_OBS;
                    } else if (d2 < date) {
                        for (int j = 0;  j < k;  j++) {
                            index[j]++;
                        }
                        continue NEXT_OBS;
                    }
                }
            }
            // if here we know same date exists in all tickers
            double x[] = new double[nTickers+1];
            for (int k = 0;  k < nTickers;  k++) {
                x[k] = series[k].close[index[k]];
            }
            list.add(x);
            for (int k = 0;  k < nTickers;  k++)  index[k]++;
        }
        int nObs = list.size();
        double obsLast[] = (double[])list.get(0);
        double returns[][] = new double[nObs-1][nTickers];
        for (int j = 1;  j < nObs;  j++) {
            double obs[] = (double[])list.get(j);
            for (int k = 0;  k < nTickers;  k++) {
                returns[j-1][k] = (obs[k]-obsLast[k])/obsLast[k];
            }
            obsLast = (double[])obs;
        }
        return returns;
    }



    static private class ListSeries {
        List  listDate;
        List  listHigh;
        List  listLow;
        List  listClose;
        List  listOpen;

        ListSeries() {
            listDate = new ArrayList(500);
            listHigh = new ArrayList(500);
            listLow = new ArrayList(500);
            listClose = new ArrayList(500);
            listOpen = new ArrayList(500);
        }

        void add(java.util.Date date, double high, double low, double close, double open) {
            listDate.add(date);
            listHigh.add(new Double(high));
            listLow.add(new Double(low));
            listClose.add(new Double(close));
            listOpen.add(new Double(open));
        }
    }


    static class Series implements Serializable {
        static private final long serialVersionUID = 6083341146689785913L;
        String  ticker;
        double  date[];
        double  high[];
        double  low[];
        double  close[];
        double  open[];

        Series(ListSeries listSeries) {
            int n = listSeries.listDate.size();
            date = new double[n];
            high = new double[n];
            low = new double[n];
            close = new double[n];
            open = new double[n];
            for (int k = 0;  k < n;  k++) {
                date[k] = ((java.util.Date)listSeries.listDate.get(k)).getTime();
                high[k] = ((Double)listSeries.listHigh.get(k)).doubleValue();
                low[k] = ((Double)listSeries.listLow.get(k)).doubleValue();
                close[k] = ((Double)listSeries.listClose.get(k)).doubleValue();
                open[k] = ((Double)listSeries.listOpen.get(k)).doubleValue();
            }
        }
    }
}