/*
 * $Id: ChartGenerator.java,v 1.12 2007/10/31 11:03:47 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: ChartGenerator.java,v $
 * Revision 1.12  2007/10/31 11:03:47  marbobo
 * + Bugfix
 *
 * Revision 1.11  2007/10/30 12:43:27  marbobo
 * + removed unnecessary statements
 *
 * Revision 1.10  2006/03/23 13:40:54  marbobo
 * + Unbenutzte Imports entfernt und alle nicht verwendete Elemente auskommentiert
 *
 * Revision 1.9  2006/02/15 16:54:48  jonyp
 * *** empty log message ***
 *
 * Revision 1.8  2005/10/04 09:50:59  jonyp
 * bug fix: use HOUR_OF_DAY instead of HOUR to display correct values on timeaxis
 *
 * Revision 1.7  2005/09/16 10:19:45  jonyp
 * valueAxis.setRange: add 10% in order to have some margin from max value for display of chart
 *
 * Revision 1.6  2005/09/15 14:43:53  jonyp
 * *** empty log message ***
 *
 * Revision 1.5  2005/09/14 15:50:29  jonyp
 * change some formats for tooltips & urls
 *
 * Revision 1.4  2005/09/14 09:32:25  jonyp
 * dataSeries.add ==> dataSeries.addOrUpdate
 *
 * Revision 1.3  2005/09/13 15:34:48  jonyp
 * make code stable
 *
 * Revision 1.1  2005/09/09 14:20:03  jonyp
 * first strike
 */

package vkc.statistics;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.GregorianCalendar;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.entity.StandardEntityCollection;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.servlet.ServletUtilities;
import org.jfree.chart.urls.TimeSeriesURLGenerator;
import org.jfree.data.jdbc.JDBCCategoryDataset;
import org.jfree.data.time.Day;
import org.jfree.data.time.Hour;
import org.jfree.data.time.Month;
import org.jfree.data.time.RegularTimePeriod;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;

import vkc.exception.DBException;
import vkc.statistics.VKCStatisticsRetriever;
import vkc.statistics.VKCDocumentStatistics;
import vkc.util.StringUtils;
import vkc.view.WebShare;

public class ChartGenerator {
    public ChartGenerator() {

    }

    public final static String generateTimeSeriesChart(HttpServletRequest request, PrintWriter pw, boolean cumulate) throws IOException, DBException {
        // get current date
        Integer year = !StringUtils.isEmpty(request.getParameter("year")) ? new Integer(request.getParameter("year")) : null;
        Integer month = !StringUtils.isEmpty(request.getParameter("month")) ? new Integer(request.getParameter("month")) : null;
        Integer day = !StringUtils.isEmpty(request.getParameter("day")) ? new Integer(request.getParameter("day")) : null;
        // boolean all = request.getParameter("all") != null ? true : false;

        GregorianCalendar from = new GregorianCalendar();
        GregorianCalendar until = new GregorianCalendar();

        int calendarField = 0;
        int step = 1;
        int count = 0;
        String dateAxisFormat = "";
        String toolTipFormat = "";
        TimeSeries dataSerieCountDocs = null;
        // TimeSeries dataSerieAmountDocs = null;
        String drillDownURL = null;
        String drillDownTimeSpec = null;
        int maxCountDocs = 0;

        String numberAxisLegend = "";
        String dateAxisLegend = "";

        if (year != null) {
            if (month != null) {
                if (day != null) {
                    // retrieve every hour per day
                    calendarField = Calendar.HOUR_OF_DAY;
                    count = 24;
                    dateAxisFormat = "hh";
                    toolTipFormat = "dd.MM.yyyy hh";

                    from.set(Calendar.YEAR, year.intValue());
                    from.set(Calendar.MONTH, month.intValue() - 1);
                    from.set(Calendar.DAY_OF_MONTH, day.intValue());
                    from.set(Calendar.HOUR_OF_DAY, 0);
                    from.set(Calendar.MINUTE, 0);
                    from.set(Calendar.SECOND, 0);

                    until.set(Calendar.YEAR, year.intValue());
                    until.set(Calendar.MONTH, month.intValue() - 1);
                    until.set(Calendar.DAY_OF_MONTH, day.intValue());
                    until.set(Calendar.HOUR_OF_DAY, 23);
                    until.set(Calendar.MINUTE, 59);
                    until.set(Calendar.SECOND, 59);
                    until.set(Calendar.MILLISECOND, 999);

                    dataSerieCountDocs = new TimeSeries(Integer.toString(from.get(Calendar.YEAR)), Hour.class);
                    // dataSerieAmountDocs = new TimeSeries(Integer.toString(from.get(Calendar.YEAR)), Hour.class);
                    // dateAxisLegend = new String(year) + " / " + new String(month) + " / " + new String(day);

                    // no further drill down
                } else {
                    // retrieve every day per month
                    calendarField = Calendar.DAY_OF_MONTH;
                    count = from.getActualMaximum(Calendar.DAY_OF_MONTH);
                    dateAxisFormat = "dd";
                    toolTipFormat = "dd.MM.yyyy";

                    from.set(Calendar.YEAR, year.intValue());
                    from.set(Calendar.MONTH, month.intValue() - 1);
                    from.set(Calendar.DAY_OF_MONTH, 1);
                    from.set(Calendar.HOUR_OF_DAY, 0);
                    from.set(Calendar.MINUTE, 0);
                    from.set(Calendar.SECOND, 0);
                    from.set(Calendar.MILLISECOND, 0);

                    until.set(Calendar.YEAR, year.intValue());
                    until.set(Calendar.MONTH, month.intValue() - 1);
                    until.set(Calendar.DAY_OF_MONTH, count);
                    until.set(Calendar.HOUR_OF_DAY, 0);
                    until.set(Calendar.MINUTE, 0);
                    until.set(Calendar.SECOND, 0);
                    until.set(Calendar.MILLISECOND, 0);

                    dataSerieCountDocs = new TimeSeries(Integer.toString(from.get(Calendar.YEAR)), Day.class);
                    // dataSerieAmountDocs = new TimeSeries(Integer.toString(from.get(Calendar.YEAR)), Day.class);

                    // dateAxisLegend = new String(year) + " / " + new String(month);
                    drillDownURL = "/showStatistics.do?year=" + year + "&month=" + month + "&cumulate=" + (cumulate ? "true" : "false");
                    drillDownTimeSpec = "day";
                }
            } else {
                // retrieve every month per year
                calendarField = Calendar.MONTH;
                count = 12;
                dateAxisFormat = "MM";
                toolTipFormat = "MM.yyyy";

                from.set(Calendar.YEAR, year.intValue());
                from.set(Calendar.MONTH, 0);
                from.set(Calendar.DAY_OF_MONTH, 1);
                from.set(Calendar.HOUR_OF_DAY, 0);
                from.set(Calendar.MINUTE, 0);
                from.set(Calendar.SECOND, 0);
                from.set(Calendar.MILLISECOND, 0);

                until.set(Calendar.YEAR, year.intValue());
                until.set(Calendar.MONTH, 11);
                until.set(Calendar.DAY_OF_MONTH, 31);
                until.set(Calendar.HOUR_OF_DAY, 0);
                until.set(Calendar.MINUTE, 0);
                until.set(Calendar.SECOND, 0);
                until.set(Calendar.MILLISECOND, 0);

                dataSerieCountDocs = new TimeSeries(Integer.toString(from.get(Calendar.YEAR)), Month.class);
                // dataSerieAmountDocs = new TimeSeries(Integer.toString(from.get(Calendar.YEAR)), Month.class);

                // dateAxisLegend = new String(year);
                drillDownURL = "/showStatistics.do?year=" + year + "&cumulate=" + (cumulate ? "true" : "false");
                drillDownTimeSpec = "month";
            }
        } else {

            // no parameters given...
            // ...take current year and current month
            calendarField = Calendar.DAY_OF_MONTH;
            count = from.getActualMaximum(Calendar.DAY_OF_MONTH);
            dateAxisFormat = "dd";
            toolTipFormat = "dd.MM.yyyy";

            from.set(Calendar.DAY_OF_MONTH, 1);
            from.set(Calendar.HOUR_OF_DAY, 0);
            from.set(Calendar.MINUTE, 0);
            from.set(Calendar.SECOND, 0);
            from.set(Calendar.MILLISECOND, 0);

            until.set(Calendar.DAY_OF_MONTH, count);
            until.set(Calendar.HOUR_OF_DAY, 0);
            until.set(Calendar.MINUTE, 0);
            until.set(Calendar.SECOND, 0);
            until.set(Calendar.MILLISECOND, 0);

            dataSerieCountDocs = new TimeSeries(Integer.toString(from.get(Calendar.YEAR)), Day.class);
            // dataSerieAmountDocs = new TimeSeries(Integer.toString(from.get(Calendar.YEAR)), Day.class);

            drillDownURL = "/showStatistics.do?year=" + from.get(Calendar.YEAR) + "&month=" + (from.get(Calendar.MONTH) + 1) + "&cumulate=" + (cumulate ? "true" : "false");
            drillDownTimeSpec = "day";

            /*
             * calendarField = Calendar.YEAR; count = 12; dateAxisFormat = "yyyy"; toolTipFormat = "yyyy"; from.set(Calendar.YEAR, 2004); from.set(Calendar.MONTH, 0); from.set(Calendar.DAY_OF_MONTH,
             * 1); from.set(Calendar.HOUR_OF_DAY, 0); from.set(Calendar.MINUTE, 0); from.set(Calendar.SECOND, 0); from.set(Calendar.MILLISECOND, 0); until.set(Calendar.YEAR, 2006);
             * until.set(Calendar.MONTH, 11); until.set(Calendar.DAY_OF_MONTH, 31); until.set(Calendar.HOUR_OF_DAY, 0); until.set(Calendar.MINUTE, 0); until.set(Calendar.SECOND, 0);
             * until.set(Calendar.MILLISECOND, 0); dataSerieCountDocs = new TimeSeries(Integer.toString(from.get(Calendar.YEAR)), Year.class); dataSerieAmountDocs = new
             * TimeSeries(Integer.toString(from.get(Calendar.YEAR)), Year.class); //dateAxisLegend = Integer.toString(from.get(Calendar.YEAR)) + " / " + Integer.toString(from.get(Calendar.MONTH)+1);
             * drillDownURL = "/showStatistics.do?year=" + Integer.toString(from.get(Calendar.YEAR)); drillDownTimeSpec = "month";
             */
        }

        // get document statistics and fill series...
        List documentData = VKCStatisticsRetriever.retrieveDocumentStatisticSeries((GregorianCalendar) from.clone(), calendarField, step, count, null);
        RegularTimePeriod period = null;

        int sumCountDocs = 0;
        Iterator iter = documentData.iterator();

        while (iter.hasNext()) {
            VKCDocumentStatistics docStat = (VKCDocumentStatistics) iter.next();

            if (year != null) {
                if (month != null) {
                    if (day != null) {
                        // period = hour
                        period = new Hour(docStat.getFrom().get(Calendar.HOUR_OF_DAY), docStat.getFrom().get(Calendar.DAY_OF_MONTH), docStat.getFrom().get(Calendar.MONTH) + 1, docStat.getFrom().get(
                                Calendar.YEAR));
                    } else {
                        // period = day
                        period = new Day(docStat.getFrom().get(Calendar.DAY_OF_MONTH), docStat.getFrom().get(Calendar.MONTH) + 1, docStat.getFrom().get(Calendar.YEAR));
                    }
                } else {
                    // period = month
                    period = new Month(docStat.getFrom().get(Calendar.MONTH) + 1, docStat.getFrom().get(Calendar.YEAR));
                }
            } else {
                // no parameters given... take current year and month
                // ...so period = day
                period = new Day(docStat.getFrom().get(Calendar.DAY_OF_MONTH), docStat.getFrom().get(Calendar.MONTH) + 1, docStat.getFrom().get(Calendar.YEAR));
            }

            if (cumulate == true) {
                // cumulate
                sumCountDocs += docStat.getCountDocs();
            } else {
                sumCountDocs = docStat.getCountDocs();
            }

            // add period to data...
            dataSerieCountDocs.add(period, sumCountDocs);
            // dataSerieAmountDocs.add(period, docStat.getAmountDocsInMB());

            // find out max value...
            if (sumCountDocs > maxCountDocs) {
                maxCountDocs = sumCountDocs;
            }

            // System.out.println("Series: Start=> " + docStat.getFrom().getTime() + " Value=> " + docStat.getCountDocs());
        }

        TimeSeriesCollection tsc = new TimeSeriesCollection();
        tsc.addSeries(dataSerieCountDocs);
        // tsc.addSeries(dataSerieAmountDocs);

        StandardXYToolTipGenerator ttgCountDocs = new StandardXYToolTipGenerator("{1}: {2}", new SimpleDateFormat(toolTipFormat), NumberFormat.getInstance());
        /*
         * StandardXYToolTipGenerator ttgAmountDocs = new StandardXYToolTipGenerator( "{1}: {2}MB", new SimpleDateFormat( toolTipFormat ), NumberFormat.getInstance());
         */
        TimeSeriesURLGenerator urlg = drillDownURL != null ? new TimeSeriesURLGenerator(new SimpleDateFormat(dateAxisFormat), WebShare.getContextPath() + drillDownURL, "serie", drillDownTimeSpec)
                : null;

        // create the chart...
        DateAxis dateAxis = new DateAxis(dateAxisLegend);
        dateAxis.setDateFormatOverride(new SimpleDateFormat(dateAxisFormat));
        dateAxis.setRange(from.getTime(), until.getTime());

        NumberAxis countDocValueAxis = new NumberAxis(numberAxisLegend);
        countDocValueAxis.setNumberFormatOverride((new DecimalFormat("##.##")));

        if (maxCountDocs != 0) {
            // add 10% in order to have some margin from max value for display of chart
            countDocValueAxis.setRange(0, maxCountDocs * 1.10);
        } else {
            // default...
            countDocValueAxis.setRange(0, 5);
        }

        NumberAxis amountDocValueAxis = new NumberAxis(numberAxisLegend);
        amountDocValueAxis.setNumberFormatOverride((new DecimalFormat("##.##")));

        /*
         * StandardXYItemRenderer renderer = new StandardXYItemRenderer( StandardXYItemRenderer.LINES + StandardXYItemRenderer.SHAPES, ttg, urlg); XYPlot plot = new XYPlot(tsc, dateAxis,
         * countDocValueAxis, renderer); JFreeChart chart = new JFreeChart("", JFreeChart.DEFAULT_TITLE_FONT, plot, true);
         */

        StandardXYItemRenderer countDocsRenderer = new StandardXYItemRenderer(StandardXYItemRenderer.LINES + StandardXYItemRenderer.SHAPES, ttgCountDocs, urlg);
        /*
         * StandardXYItemRenderer amountDocsRenderer = new StandardXYItemRenderer( StandardXYItemRenderer.LINES + StandardXYItemRenderer.SHAPES, ttgAmountDocs, null);
         */
        XYPlot plot = new XYPlot(tsc, dateAxis, countDocValueAxis, countDocsRenderer);
        /*
         * plot.setRangeAxis(1, amountDocValueAxis); plot.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_RIGHT); plot.setRenderer(1, amountDocsRenderer);
         */
        JFreeChart chart = new JFreeChart("", JFreeChart.DEFAULT_TITLE_FONT, plot, true);

        chart.setBackgroundPaint(new java.awt.Color(242, 242, 242));
        chart.removeLegend();

        // Write the chart image to the temporary directory
        ChartRenderingInfo info = new ChartRenderingInfo(new StandardEntityCollection());
        String filename = ServletUtilities.saveChartAsPNG(chart, 500, 300, info, request.getSession());

        // Write the image map to the PrintWriter
        ChartUtilities.writeImageMap(pw, filename, info, false);
        pw.flush();

        return filename;
    }

    public JFreeChart generateBarChart(HttpServletRequest request) throws DBException, SQLException {
        // Create and populate a CategoryDataset
        /*
         * List docTypeStats = VKCStatisticsRetriever.retrieveDocTypeStatistics(null, null, null); Iterator iter = docTypeStats.listIterator(); DefaultCategoryDataset dataset = new
         * DefaultCategoryDataset(); while (iter.hasNext()) { VKCDocTypeStatistics dts = (VKCDocTypeStatistics)iter.next(); dataset.addValue(new Long(dts.getCountDocType()), "Count",
         * dts.getExtension()); }
         */

        String sqlStatement = "     SELECT documentTypes.extension, COUNT(documents.typeId) AS count_type" + "       FROM documents "
                + " RIGHT JOIN documentTypes ON documents.typeId = documentTypes.docTypeId" + "   GROUP BY documentTypes.extension";
        JDBCCategoryDataset dataset = new JDBCCategoryDataset(WebShare.database.getConnection(), sqlStatement);

        // Create the chart object
        // CategoryAxis categoryAxis = new CategoryAxis("");
        // ValueAxis valueAxis = new NumberAxis("");
        BarRenderer renderer = new BarRenderer();
        renderer.setItemURLGenerator(null);
        renderer.setToolTipGenerator(null);

        // Plot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, renderer);
        // JFreeChart chart = new JFreeChart("", JFreeChart.DEFAULT_TITLE_FONT, plot, false);

        JFreeChart chart = ChartFactory.createBarChart("Anzahl publizierter Dokumente", "Tag", "Anzahl", dataset, PlotOrientation.VERTICAL, true, false, false);

        chart.setBackgroundPaint(java.awt.Color.white);

        return chart;
    }
}