/*
 * DetailReport.java
 *
 * Created on June 27, 2006, 12:10 PM
 *
 */
package usda.weru.weps.reports;

import com.klg.jclass.table.JCPrintEvent;
import com.klg.jclass.table.JCPrintListener;
import com.klg.jclass.table.JCTableEnum;
import com.klg.jclass.table.TableDataModel;
import com.klg.jclass.table.data.AbstractDataSource;
import java.awt.EventQueue;
import java.awt.Component;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.TFileInputStream;

import java.awt.AWTEvent;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Vector;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import javax.help.CSH;
import javax.help.HelpBroker;
import javax.help.HelpSet;
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.ProgressMonitor;
import org.apache.log4j.Logger;
import org.jdom2.Element;
import org.jfree.data.category.DefaultCategoryDataset;
import usda.weru.util.About;
import usda.weru.util.ConfigData;
import usda.weru.util.ConversionUnit;
import usda.weru.util.Help;
import usda.weru.util.ReportContext;
import usda.weru.util.Util;
import usda.weru.util.table.FilterSet;
import usda.weru.util.table.Helper;
import usda.weru.util.table.ParseEvent;
import usda.weru.util.table.ParseListener;
import usda.weru.util.table.WepsTable;
import usda.weru.util.table.Column;
import usda.weru.util.table.WepsTableEnum;
import usda.weru.util.table.WepsTableMeta;
import usda.weru.util.table.ColumnFilter;
import usda.weru.weps.RunFileData;
import usda.weru.weps.reports.gui.DetailReport_n;

/**
 *
 * @author Joseph Levin
 */
public class DetailReport extends DetailReport_n implements PropertyChangeListener, ParseListener, JCPrintListener {

    private static final long serialVersionUID = 1L;

    /**
     *
     */
    protected static final String c_metaPath = "tables/detail.xml";

    /**
     *
     */
    protected String c_filtersPath = "tables/detail_filters.xml";

    /**
     *
     */
    protected TFile c_runFile;

    /**
     *
     */
    protected List<FilterSet> c_filterSets;

    /**
     *
     */
    protected ProgressMonitor c_progress;

    /**
     *
     */
    protected boolean c_loadingFilters;

    /**
     *
     */
    protected RunFileData c_run;    //Run Info

    /**
     *
     */
    protected String c_client = "";

    /**
     *
     */
    protected String c_farm = "";

    /**
     *
     */
    protected String c_tract = "";

    /**
     *
     */
    protected String c_field = "";

    /**
     *
     */
    protected String c_management = "";

    /**
     *
     */
    protected String c_soil = "";

    /**
     *
     */
    protected String c_angle = "";    //Config info

    /**
     *
     */
    protected String c_rotationDateFormat = "";
    Hashtable<String, String> c_tooltips;

    private final Set<String> quickPlotColumns = new HashSet<String>();

    /**
     * Creates a new instance of DetailReport
     * @param runFile
     */
    public DetailReport(TFile runFile) {
        c_runFile = runFile;
        g_wepsTable.addParseListener(this);
        setTitle("Tabular Detail Report - " + c_runFile.getName());
        setIconImage(About.getWeruIconImage());
        initCSH();

        //setup quickplot dropdown button
        for (QuickPlotCustomizer.Type t : QuickPlotCustomizer.Type.values()) {
            quickPlotTypeMenu.add(createQuickPlotTypeMenuItem(t));
        }

        quickPlotRestore.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                setQuickPlot(getSelectedFilterSet());
            }
        });

        quickPlotSelect.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                setAllQuickPlot(true);
            }
        });

        quickPlotClear.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                setAllQuickPlot(false);
            }
        });

        g_quickplot.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                chartActionPerformed(ae);
            }
        });
    }

    private void initCSH() {
        HelpSet hs = Help.getHelpSet();
        HelpBroker hb = hs.createHelpBroker();
        TFile file = new TFile("cfg", "reportstooltips.cfg");
        c_tooltips = Util.loadToolTipsTable(file);
        Util.loadToolTips((Container) this, file);
        if (hb != null) {
            hb.enableHelpOnButton(g_helpButton, "outDetScreen_html", hs);
            ActionListener aboutHelp = new CSH.DisplayHelpFromSource(hb);
            g_helpButton.addActionListener(aboutHelp);
            g_cshButton.addActionListener(new CSH.DisplayHelpAfterTracking(hs, "javax.help.Popup", null));
        } else {
            g_cshButton.setEnabled(false);
        }

        CSH.setHelpIDString(JTB_main, "outDebButtons_html");

        TableCSHManager cm = new TableCSHManager(g_wepsTable);
        CSH.addManager(cm);

        g_wepsTable.addMouseMotionListener(new MouseMotionListener() {

            @Override
            public void mouseDragged(MouseEvent e) {
            }

            @Override
            public void mouseMoved(MouseEvent me) {
                try {
                    int columnIndex = g_wepsTable.XYToCell(me.getX(), me.getY()).column;
                    Column column = g_wepsTable.getMeta().getColumn(columnIndex);

                    if (column != null) {
                        String paramName = column.getId();
                        String tooltip = c_tooltips.get("RepDetailPanel:" + paramName);
                        if (tooltip != null) {
                            g_wepsTable.setToolTipText("<html>" + tooltip + "</html>");
                            return;
                        }
                    }
                    g_wepsTable.setToolTipText("");
                } catch (Exception e) {
                }
            }
        });
    }

    private QuickPlotCustomizer.Type quickPlotType = QuickPlotCustomizer.Type.Line;
    private final ButtonGroup typeGroup = new ButtonGroup();

    private JMenuItem createQuickPlotTypeMenuItem(final QuickPlotCustomizer.Type type) {
        final JRadioButtonMenuItem result = new JRadioButtonMenuItem(type.title(), quickPlotType.equals(type));
        typeGroup.add(result);

        result.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                quickPlotType = type;
                result.setSelected(true);
            }
        });
        return result;
    }

    private void setQuickPlot(FilterSet filter) {
        setAllQuickPlot(false);
        g_wepsTable.setRepaintEnabled(false);
        try {
            for (String id : filter.quickPlotColumns()) {
                g_wepsTable.getMeta().getColumn(id).setQuickPlotL(true);
            }
        } finally {
            g_wepsTable.setRepaintEnabled(true);
            g_wepsTable.repaint();
        }
    }

    private void setAllQuickPlot(boolean quickplot) {
        g_wepsTable.setRepaintEnabled(false);
        try {
            for (Column column : g_wepsTable.getMeta().getColumns()) {
                column.setQuickPlotL(quickplot);
            }
        } finally {
            g_wepsTable.setRepaintEnabled(true);
            g_wepsTable.repaint();
        }
    }

    /**
     *
     */
    public void display() {
        Thread task = new Thread("DetailReport.display()") {

            @Override
            public void run() {
                try {
                    setEnabled(false);
                    g_wepsTable.setQuickPlot(true);
                    g_wepsTable.setPixelWidth(-1, 20);
                    g_wepsTable.setMinWidth(-1, 20);
                    g_wepsTable.setMaxWidth(-1, 20);
                    loadMeta();

                    loadFilters();
                    loadData();

                    EventQueue.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            //Hack to recalc first data row height.
                            g_wepsTable.setPixelHeight(g_wepsTable.getMeta().getLabelRowCount() - 1, JCTableEnum.VARIABLE);

                            setVisible(true);
                            setEnabled(true);

                        }
                    });

                    requestFocusInWindow();

                    //Awful hack to make sure the icons are drawn
                    //TODO
                    try {

                        for (int i = 0; i < 10; i++) {
                            EventQueue.invokeLater(new Runnable() {

                                @Override
                                public void run() {
                                    g_wepsTable.repaint();

                                }

                            });
                            sleep(200);
                        }
                    } catch (InterruptedException e) {
                        //do nothing
                    }

                    //System.out.println((end - start));
                } catch (ConcurrentModificationException ex) {
                    counter++;
                    if (counter >= 20) {
                        int result = JOptionPane.showConfirmDialog(DetailReport.this,
                                "Detail Report has failed to open 20 times./nWould you like to continue trying?",
                                "Error", JOptionPane.YES_NO_OPTION);
                        if (result == JOptionPane.YES_OPTION) {
                            counter = 0;
                        } else if (result == JOptionPane.NO_OPTION) {
                            return;
                        }
                    }
                    Logger.getLogger(DetailReport.class).error("Failed to open Detail Report " + counter
                            + " times.  Trying again", ex);
                    dispose();
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException ex1) {
                        ex1.printStackTrace();
                    }
                    ReportManager.getDefault().displayReport(c_runFile, ReportManager.REPORT_DETAIL);

                }
            }
        };
        task.start();
    }
    private static int counter = 0;

    private void loadMeta() {
        TFile file = new TFile(c_metaPath);
        WepsTableMeta meta = new WepsTableMeta();
        meta.fromFile(file);
        g_wepsTable.setMeta(meta);
    }

    private void loadFilters() {
        c_loadingFilters = true;
        c_filterSets = new ArrayList<FilterSet>();
        TFile file = new TFile(c_filtersPath);
        Element node = Helper.getRootNode(file);
        List<Element> filtersetList = node.getChildren(WepsTableEnum.XML_filterset);
        FilterSet defaultSet = null;
        try {
            for (Element filterSetNode : filtersetList) {
                FilterSet filterSet = new FilterSet();
                filterSet.fromXml(filterSetNode);
                g_reportDropDown.addItem(filterSet);
                if (defaultSet == null) {
                    defaultSet = filterSet;
                }
            }
        } catch (ConcurrentModificationException ex) {
            System.out.println("************DetailReport.loadFilters()**************");
            ex.printStackTrace();
        }
        if (defaultSet != null) {
            g_reportDropDown.setSelectedItem(defaultSet);
            g_wepsTable.applyFilterSet(defaultSet);
        }
        c_loadingFilters = false;

    }

    private void loadData() {
        ReportContext.enter();
        try {
            c_run = new RunFileData(c_runFile.getAbsolutePath(), true);
            c_run.fireAll(this);
            //        g_wepsTable.setDataSource(new DetailData());
            TFile dataFile = new TFile(c_runFile, "gui1_data.out");
            DetailData data = new DetailData();

            if (data.load(this, dataFile)) {
/**
                 * Note:  Assertions are not enabled.  These will be useless items
                 * unless assertions are enabled.  Thus, they will be commented out unless
                 * the user wishes to enable specific assertions (feed the virtual machine 
                 * the -ea argument).
                 */
//                assert EventQueue.isDispatchThread() : "Not supposed to be called here!";
                g_wepsTable.setDataSource(data);
            }
        } finally {
            ReportContext.exit();
        }

    }

    /**
     *
     * @param event
     */
    @Override
    public void propertyChange(PropertyChangeEvent event) {
        String property = event.getPropertyName();
        Object newValue = event.getNewValue();
        String newStringValue = newValue.toString();
        switch (property) {
            case usda.weru.util.ConfigData.Units:
                g_wepsTable.setUnitsSystem(newStringValue);
                break;
            case RunFileData.UserName:
                c_client = newStringValue;
                break;
            case RunFileData.FarmId:
                c_farm = newStringValue;
                break;
            case RunFileData.TractId:
                c_tract = newStringValue;
                break;
            case RunFileData.FieldId:
                c_field = newStringValue;
                break;
            case RunFileData.ManageFile:
                c_management = new TFile(newStringValue).getName().replace(".man", "").replace(".xml", "");
                break;
            case RunFileData.SoilFile:
                c_soil = new TFile(newStringValue).getName().replace(".ifc", "");
                break;
            case RunFileData.RegionAngle:
                c_angle = newStringValue;
                break;
            case usda.weru.util.ConfigData.FormatOperationDate:
                c_rotationDateFormat = newStringValue;
                break;
            case usda.weru.util.ConfigData.DetailTableFilterFile:
                c_filtersPath = newStringValue;
                break;
        }

    }

    @Override
    public void parse(ParseEvent event) {
        String field = event.getField();
        String system = g_wepsTable.getUnitsSystem();
        switch (field) {
            case "wind_energy_limit":
                switch (system) {
                    case usda.weru.util.Util.SIUnits:
                        event.setParsedExpression("> 8m/s");
                        break;
                    case usda.weru.util.Util.USUnits:
                        event.setParsedExpression("> 18mph");  // Rounded from 17.9 to even value - LEW
                        break;
                }
                break;
            case "snow_depth_limit":
                switch (system) {
                    case usda.weru.util.Util.SIUnits:
                        event.setParsedExpression("> 20mm");
                        break;
                    case usda.weru.util.Util.USUnits:
                        event.setParsedExpression("> 0.75in");  // Rounded to 3/4" - LEW
                        break;
                }
                break;
            case "aggregateslimit":
                switch (system) {
                    case usda.weru.util.Util.SIUnits:
                        event.setParsedExpression("< 0.84mm");
                        break;
                    case usda.weru.util.Util.USUnits:
                        // Will not display in English units, 0.03", since it has always been reported as 0.84mm - LEW
                        event.setParsedExpression("< 0.03in");
                        break;
                }
                break;
            case "run":
                event.setParsedExpression(c_runFile.getName().replace(RunFileData.RunSuffix, ""));
                break;
            case "client":
                event.setParsedExpression(c_client);
                break;
            case "farm":
                event.setParsedExpression(c_farm);
                break;
            case "tract":
                event.setParsedExpression(c_tract);
                break;
            case "field":
                event.setParsedExpression(c_field);
                break;
            case "management":
                event.setParsedExpression(c_management);
                break;
            case "soil":
                event.setParsedExpression(c_soil);
                break;
            case "fieldangle":
                event.setParsedExpression(c_angle);
                break;
        }

    }

    /**
     *
     * @param evt
     */
    @Override
    protected void reportDropDownItemStateChanged(ItemEvent evt) {
        if (c_loadingFilters) {
            return;
        }
        if (evt.getStateChange() != ItemEvent.SELECTED) {
            return;
        }
        FilterSet filterSet = (FilterSet) g_reportDropDown.getSelectedItem();
        g_wepsTable.applyFilterSet(filterSet);
    }

    /**
     *
     * @param evt
     */
    @Override
    protected void csButtonActionPerformed(java.awt.event.ActionEvent evt) {
        ReportManager.getDefault().displayReport(c_runFile, ReportManager.REPORT_CROPSUM);
    }

    /**
     *
     * @param evt
     */
    @Override
    protected void ssButtonActionPerformed(java.awt.event.ActionEvent evt) {
        ReportManager.getDefault().displayReport(c_runFile, ReportManager.REPORT_STIR);
    }

    /**
     *
     * @param evt
     */
    @Override
    protected void ccButtonActionPerformed(java.awt.event.ActionEvent evt)
    {
        ReportManager.getDefault().displayReport(c_runFile, ReportManager.REPORT_COVERCROPDET);
    }

    /**
     *
     * @param evt
     */
    @Override
    protected void ciButtonActionPerformed(java.awt.event.ActionEvent evt)
    {
        ReportManager.getDefault().displayReport(c_runFile, ReportManager.REPORT_CROP_INT_SUM);
    }

    /**
     *
     * @param evt
     */
    @Override
    protected void msButtonActionPerformed(java.awt.event.ActionEvent evt) {
        ReportManager.getDefault().displayReport(c_runFile, ReportManager.REPORT_MANAGEMENT);
    }

    /**
     *
     * @param evt
     */
    @Override
    protected void psButtonActionPerformed(java.awt.event.ActionEvent evt) {
        ReportManager.getDefault().displayReport(c_runFile, ReportManager.REPORT_RUN);
    }

    /**
     *
     * @param evt
     */
    @Override
    protected void cshButtonActionPerformed(java.awt.event.ActionEvent evt) {
    }

    /**
     *
     * @param evt
     */
    @Override
    protected void helpButtonActionPerformed(java.awt.event.ActionEvent evt) {
    }

    /**
     *
     * @param evt
     */
    @Override
    protected void printButtonActionPerformed(java.awt.event.ActionEvent evt) {
        g_wepsTable.printPreview(this);

    }

    /**
     *
     * @param evt
     */
    @Override
    protected void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {
        dispose();
    }

    /**
     *
     * @param event
     */
    public void printEnd(JCPrintEvent event) {
    }

    /**
     *
     * @param event
     */
    @Override
    public void printPageBody(JCPrintEvent event) {
    }

    /**
     *
     * @param e
     */
    @Override
    public void printPageFooter(JCPrintEvent e) {
        Graphics gc = e.getGraphics();
        Rectangle r = gc.getClip().getBounds(); // fix to get rid of dep method
        FontMetrics fm = gc.getFontMetrics();

        String note = "Created: " + new Date(c_runFile.lastModified()).toString() + "   ";

        gc.drawString(note, r.width - fm.stringWidth(note), r.height / 2);
    }

    /**
     *
     * @param e
     */
    @Override
    public void printPageHeader(JCPrintEvent e) {
        Graphics gc = e.getGraphics();

        Rectangle r = gc.getClip().getBounds(); // fix to get rid of dep method
        FontMetrics fm = gc.getFontMetrics();

        String note = "Run: " + c_runFile.getAbsolutePath() + "   ";
        gc.drawString("Detail Report", 0, r.height / 2);
        gc.drawString(note, r.width - fm.stringWidth(note), r.height / 2);
    }

    class DetailData extends AbstractDataSource implements TableDataModel {

        private static final long serialVersionUID = 1L;

        private List<String> c_keyMap;
        private List<Object[]> c_data;

        public DetailData() {
            c_keyMap = new Vector<String>();
            c_data = new Vector<Object[]>();
            c_data = Collections.synchronizedList(c_data);
            c_keyMap = Collections.synchronizedList(c_keyMap);
            fireDataReset();
        }

        @Override
        public int getNumColumns() {
            synchronized (c_keyMap) {
                return c_keyMap.size();
            }
        }

        @Override
        public int getNumRows() {
            synchronized (c_data) {
                return c_data.size();
            }
        }

        @Override
        public Object getTableColumnLabel(int columnIndex) {
            synchronized (c_keyMap) {
                return c_keyMap.get(columnIndex);
            }
        }

        @Override
        public Object getTableDataItem(int rowIndex, int columnIndex) {
            synchronized (c_data) {
                Object[] row = c_data.get(rowIndex);
                return row[columnIndex];
            }

        }

        @Override
        public Object getTableRowLabel(int rowIndex) {
            return null;
        }

        //Load data
        public boolean load(Component parent, TFile file) {
            String inLine;
            boolean success = true;
            //read the file one line at a time into a vector for processing
            ProgressMonitor progress = null;
            try {
                try (InputStreamReader streamReader = new InputStreamReader(new TFileInputStream(file))) {
                    BufferedReader in = new BufferedReader(streamReader);
                    do {
                        inLine = in.readLine();
                        if (inLine != null) {
//                        progress.setProgress((int)channel.position());
                            parseLine(inLine);
                        }
//                    if (progress.isCanceled()){
//                        success = false;
//                        break;
//                    }

                    } while (inLine != null);

                    in.close();
                }

            } catch (FileNotFoundException fnfe) {
                JOptionPane.showMessageDialog(parent, "An expected data file is missing.\n\""
                        + file.getName() + "\" may have been deleted.", "Missing Data File",
                        JOptionPane.ERROR_MESSAGE);
                success = false;
            } catch (OutOfMemoryError oome) {
//                progress.close();
                JOptionPane.showMessageDialog(parent, "The data file is too large.",
                        "Out of Memory", JOptionPane.ERROR_MESSAGE);
                success = false;
            } catch (IOException e) {
                e.printStackTrace();
                success = false;
            }
            fireDataReset();
            return success;

        }

        private void parseLine(String line) {
            //We have the header line.
            if (line.startsWith("key")) {
                StringTokenizer st = new StringTokenizer(line, "|");
                while (st.hasMoreTokens()) {
                    synchronized (c_keyMap) {
                        c_keyMap.add(st.nextToken().trim());
                    }
                }
            } //We have the totals line.
            else {
                StringTokenizer st = new StringTokenizer(line, "|");
                int index = 0;
                Object[] row = null;
                synchronized (c_keyMap) {
                    row = new Object[c_keyMap.size()];
                }
                String lineKey = "";
                boolean skipRow = false;
                synchronized (c_keyMap) {
                    while (st.hasMoreTokens() && index < c_keyMap.size()) {

                        String value = st.nextToken().trim();
                        if (index == 0) {
                            lineKey = value;
                            lineKey = lineKey.trim();
                        }

                        Object newValue = value;

                        if (lineKey.equals("P") && index == 1) {
                            //Month Field
                            newValue = formatDate(value);
                        } else if (index == 2 || index == 3) {
                            if (newValue.toString().indexOf("~") >= 0) {
                                newValue = newValue.toString().replace("~", "\n");
                            }
                            if (newValue.toString().trim().length() == 0) {
                                newValue = null;
                            }

                        } else {
                            try {
                                newValue = new Double(value);
                            } catch (NumberFormatException nfe) {
                                newValue = value;
                            }
                        }

                        row[index] = newValue;
                        index++;
                    }
                }
                if (!skipRow) {
                    synchronized (c_data) {
                        c_data.add(row);
                    }
                }
            }
        }

        private String formatDate(String value) {
            StringTokenizer st = new StringTokenizer(value, "/");
            if (st.countTokens() != 3) {
                return value;
            }
            String days = st.nextToken().trim();
            String month = st.nextToken().trim();
            String year = st.nextToken().trim();

            try {
                String x = (year.equals("13")) ? "12" : "13";
                String temp = month + "/" + x + "/" + year;
                SimpleDateFormat format = new SimpleDateFormat("M/d/y");
                format.setLenient(true);
                Date d = format.parse(temp);
                format.applyPattern(c_rotationDateFormat);
                String outStr = format.format(d);
                if (days.length() > 2) {
                    while (days.length() < 5) {
                        days = " " + days;
                    }
                }
                outStr = outStr.replace(x, days);
                return outStr;
            } catch (ParseException ex) {
                return "#ERR#";
            }
        }
    }

    static class TableCSHManager implements CSH.Manager {

        private final WepsTable c_table;

        public TableCSHManager(WepsTable table) {
            c_table = table;
        }

        private void translateMouseEvent(MouseEvent me) {
            Object parent = me.getSource();
            Component comp = c_table;
            int x = 0;
            int y = 0;
            while (comp != parent) {
                x += comp.getX();
                y += comp.getY();
                comp = comp.getParent();
            }

            me.translatePoint(-x, -y);
        }

        @Override
        public String getHelpIDString(Object comp, AWTEvent evt) {
            if (comp != c_table) {
                return null;
            }
            try {
                if (evt instanceof MouseEvent) {
                    MouseEvent me = (MouseEvent) evt;
                    translateMouseEvent(me);

                    int columnIndex = c_table.XtoColumn(me.getX());
                    Column column = c_table.getMeta().getColumn(columnIndex);
                    String id = column.getId();

                    if (id != null) {
                        id = id.replace(" ", "_");
                        return "detail:" + id + "_html";
                    } else {
                        return null;
                    }
                } else {
                    return null;
                }
            } catch (Exception e) {
                return null;
            }
        }

        @Override
        public HelpSet getHelpSet(java.lang.Object comp, java.awt.AWTEvent e) {
            return Help.getHelpSet();
        }
    }

    /**
     *
     * @param evt
     */
    @Override
    protected void chartActionPerformed(ActionEvent evt) {

        //figure out the columns to plot
        List<String> fields = new ArrayList<String>();
        List<String> fields2 = new ArrayList<String>();
        for (Column column : g_wepsTable.getMeta().getColumns()) {
            if (column.isQuickPlotL() && isColumnVisible(column) && column.getDataType() == WepsTableEnum.DATA_numeric) {
                fields.add(column.getDataKey());
            }
            if(column.isQuickPlotR() && isColumnVisible(column) && column.getDataType() == WepsTableEnum.DATA_numeric) {
                fields2.add(column.getDataKey());
            }
        }

        if (fields.isEmpty() && fields2.isEmpty()) {
            //prompt user to select fields
            JOptionPane.showMessageDialog(this, "Select at least one detail column to plot.",
                    "Quick Plot", JOptionPane.WARNING_MESSAGE);
        } else {
            quickPlot(quickPlotType, fields.toArray(new String[fields.size()]), 
                    fields2.toArray(new String[fields2.size()]));
        }

    }

    private boolean isColumnVisible(Column column) {
        FilterSet filter = getSelectedFilterSet();
        for (ColumnFilter cf : filter.columnFilters()) {
            if (cf.acceptColumn(column)) {
                return true;
            }
        }
        return false;
    }

    /**
     *
     * @return
     */
    public FilterSet getSelectedFilterSet() {
        Object result = g_reportDropDown.getSelectedItem();
        if (result instanceof FilterSet) {
            return (FilterSet) result;
        } else {
            return null;
        }
    }

    private boolean isRowQuickPlot(String key) {
        for (String test : getSelectedFilterSet().quickPlotRows()) {
            if (test != null && test.equals(key)) {
                return true;
            }
        }
        return false;
    }

    /**
     *
     * @param type
     * @param fields
     */
    public void quickPlot(final QuickPlotCustomizer.Type type, final String[] fields, final String[] fields2) {
        //make sure we're not on the event queue!
        if (EventQueue.isDispatchThread()) {
            Thread background = new Thread("Quick Plot: " + Arrays.toString(fields)) {

                @Override
                public void run() {
                    quickPlot(type, fields, fields2);
                }

            };
            background.start();
            return;
        }

        final ReportPack pack = ReportManager.getDefault().getReportPack(
                c_runFile, ReportManager.REPORT_QUICKPLOT, false);

        //make the dataset
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        DefaultCategoryDataset dataset2 = new DefaultCategoryDataset();
        ArrayList<DefaultCategoryDataset> collectionLeft = new ArrayList<DefaultCategoryDataset>();
        ArrayList<DefaultCategoryDataset> collectionRight = new ArrayList<DefaultCategoryDataset>();

//        MessageFormat dateFormat = new MessageFormat("{2} {0}-{1}, {3}");
//        DateFormat dateFormat2 = new SimpleDateFormat("MMM-dd-y");
//        
        MinMax left = new MinMax();
        MinMax right = new MinMax();
        fillDataset(dataset, fields, "Left Axis", left);
        fillDataset(dataset2, fields2, "Right Axis", right);
        fillSubsets(collectionLeft, fields, "Left Axis");
        fillSubsets(collectionRight, fields2, "Right Axis");
        collectionLeft.add(0, dataset);
        collectionRight.add(0, dataset2);
        QuickPlotCustomizer.Type tempType = type;
        //If a stacked bar chart is selected with both datasets having data,
        //we want to convert to a bar chart.
        if(dataset.getColumnCount() != 0 && 
                dataset2.getColumnCount() != 0 &&
                tempType.equals(QuickPlotCustomizer.Type.BarStacked))
        {
            tempType = QuickPlotCustomizer.Type.Bar;
        }
        if(tempType.equals(QuickPlotCustomizer.Type.Bar)) 
        {
            for(int index = 0; index < Math.min(collectionLeft.size(), collectionRight.size()); index ++)
            {
                DefaultCategoryDataset leftD = collectionLeft.get(index);
                DefaultCategoryDataset rightD = collectionRight.get(index);
                rightD = nullIn(leftD, rightD);
                collectionRight.set(index, rightD);
            }
        }

        //add the dataset to the report parameters
        try
        {
            pack.getReportParameters().put(QuickPlotCustomizer.PARAMETER_TYPE, tempType.key());
            pack.getReportParameters().put("LMax", left.getMax());
            pack.getReportParameters().put("RMax", right.getMax());
            pack.getReportParameters().put("LMin", left.getMin());
            pack.getReportParameters().put("RMin", right.getMin());            
            pack.connectQuickPlot(collectionLeft, collectionRight);
        }
        catch(SQLException error)
        {
            System.out.println("We failed at passing the quickplot data to the quickplot report");
        }
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                //todo: just display the frame myself, don't want any of this to be cached by the report manager!
                ReportViewer viewer = new ReportViewer(pack, true);

                //dirty hacks
                String reportTitle = "WEPS Quick Plot";
                Rectangle screen = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
                    Dimension size = viewer.getSize();
                    size.height = screen.height < 800 ? screen.height : 800; //975 x 800
                    size.width = screen.width < 975 ? screen.width : 975;
                    viewer.setSize(size);
                    viewer.setLocation(screen.x, screen.y);

                viewer.setTitle(reportTitle + " : " + Util.purgeExtensions(c_run.fileObject.getName(), ".wjr"));
                viewer.setVisible(true);
            }
        });

    }
    
    /**
     * This code is for filling a dataset with values based off the fields passed in.
     * Separated from charActionPerformed method to reduce copying error.
     * @param dataset
     * @param fields 
     */
    private void fillDataset(DefaultCategoryDataset dataset, final String[] fields, String axis, MinMax lims)
    {
        for (String field : fields) {
//            TimeSeries series = new TimeSeries(field);
            //fill the series with data

            for (int row = 0; row < g_wepsTable.getDataView().getNumRows(); row++) {
                //make sure it's not a row label
                if (g_wepsTable.getDataView().isLabelRow(row)) {
                    continue;
                }

                //test that the row is allowed by the filter in the quick plot
                Object ko = g_wepsTable.getDataView().getObject(row, "key");
                String key = ko instanceof String ? (String) ko : null;
                if (!isRowQuickPlot(key)) {
                    continue;
                }

                Object no = g_wepsTable.getDataView().getObject(row, field);

                if (!(no instanceof Number)) no = 0;

                Column column = g_wepsTable.getMeta().getColumn(field);
                no = column.applyAdjustments(no, ConfigData.getDefault().getData(ConfigData.Units));
                no = column.applyDisplayUnits(no, ConfigData.getDefault().getData(ConfigData.Units));
                Number n = (Number) no;
                if(n.doubleValue() > lims.getMax()) lims.setMax(n.doubleValue());
                if(n.doubleValue() < lims.getMin()) lims.setMin(n.doubleValue());
                String po = g_wepsTable.getDataView().getObject(row, "sd ed mo yr").toString();

                ConversionUnit units = column.getDisplayUnits(g_wepsTable.getUnitsSystem());
                String unitStr = units == null ? "" : units.getAbbreviation();

                String temp = field + "\n" + unitStr + "\n" + axis;

                dataset.addValue(n, temp, po);
            }

        }
    }
    
    /**
     * This code is for filling a dataset with values based off the fields passed in.
     * Separated from charActionPerformed method to reduce copying error.
     * @param dataset
     * @param fields 
     */
    private void fillSubsets(ArrayList<DefaultCategoryDataset> collection, final String[] fields, String axis)
    {
        if(fields.length != 0)
        {
            String field = fields[0];
            initializeYear(collection, field, axis);
        }
        for (int index = 1; index < fields.length; index ++) {
            //We need to have number indexes, so we can initialize years the first go around
            String field = fields[index];
            int count = 0;
            DefaultCategoryDataset dataset = collection.get(0);
            for (int row = 0; row < g_wepsTable.getDataView().getNumRows(); row++) {
                //make sure it's not a row label
                if (g_wepsTable.getDataView().isLabelRow(row)) {
                    continue;
                }

                //test that the row is allowed by the filter in the quick plot
                Object ko = g_wepsTable.getDataView().getObject(row, "key");
                String key = ko instanceof String ? (String) ko : null;
                if (!isRowQuickPlot(key)) {
                    if("Y".equals(key))
                    {
                        count ++;
                        if(count == collection.size()) continue;
                        dataset = collection.get(count);
                    }
                    continue;
                }

                Object no = g_wepsTable.getDataView().getObject(row, field);

                if (!(no instanceof Number)) no = 0; //If it's a null value, we want to stick it on the axis
                                                    // so we don't get wierd date behavior.
                                    
                Column column = g_wepsTable.getMeta().getColumn(field);
                no = column.applyAdjustments(no, ConfigData.getDefault().getData(ConfigData.Units));
                no = column.applyDisplayUnits(no, ConfigData.getDefault().getData(ConfigData.Units));
                Number n = (Number) no;

                String po = g_wepsTable.getDataView().getObject(row, "sd ed mo yr").toString();

                ConversionUnit units = column.getDisplayUnits(g_wepsTable.getUnitsSystem());
                String unitStr = units == null ? "" : units.getAbbreviation();

                String temp = field + "\n" + unitStr + "\n" + axis;

                dataset.addValue(n, temp, po);
            }
        }
    }
    
    private void initializeYear(ArrayList<DefaultCategoryDataset> collection, String field, String axis)
    {
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        for (int row = 0; row < g_wepsTable.getDataView().getNumRows(); row++) {
                //make sure it's not a row label
                if (g_wepsTable.getDataView().isLabelRow(row)) {
                    continue;
                }

                //test that the row is allowed by the filter in the quick plot
                Object ko = g_wepsTable.getDataView().getObject(row, "key");
                String key = ko instanceof String ? (String) ko : null;
                if (!isRowQuickPlot(key)) {
                    if("Y".equals(key))
                    {
                        collection.add(dataset);
                        dataset = new DefaultCategoryDataset();
                    }
                    continue;
                }

                Object no = g_wepsTable.getDataView().getObject(row, field);

                if (no instanceof Number) {

                    Column column = g_wepsTable.getMeta().getColumn(field);
                    no = column.applyAdjustments(no, ConfigData.getDefault().getData(ConfigData.Units));
                    no = column.applyDisplayUnits(no, ConfigData.getDefault().getData(ConfigData.Units));
                    Number n = (Number) no;

                    String po = g_wepsTable.getDataView().getObject(row, "sd ed mo yr").toString();

                    ConversionUnit units = column.getDisplayUnits(g_wepsTable.getUnitsSystem());
                    String unitStr = units == null ? "" : units.getAbbreviation();
                    
                    String temp = field + "\n" + unitStr + "\n" + axis;
                    
                    dataset.addValue(n, temp, po);
                }
            }
    }
    
    /**
     * This code will fill in the first dataset with null values to the right and the second
     * with null values to the right.  It will give names to the null "rows" that start with empty.
     * This will directly mutate dataset, and return the mutated dataset2.
     * @param dataset
     * @param dataset2 
     */
    private DefaultCategoryDataset nullIn(DefaultCategoryDataset dataset, DefaultCategoryDataset dataset2)
    {
        DefaultCategoryDataset temp = new DefaultCategoryDataset();
        for(int data = 0; data < dataset.getRowCount(); data ++)
        {
            for(Object column : dataset.getColumnKeys())
            {
                String colStr = "";
                if(column instanceof String) colStr = (String) column;
                temp.addValue(null, "emptyL" + data, colStr);
            }
        }
        for(Object row : dataset2.getRowKeys())
        {
            for(Object column : dataset2.getColumnKeys())
            {
                String colStr = "";
                String rowStr = "";
                if(column instanceof String) colStr = (String) column;
                if(row instanceof String) rowStr = (String) row;
                temp.addValue(dataset2.getValue(rowStr, colStr), rowStr, 
                        colStr);
            }
        }
        for(int data = 0; data < dataset2.getRowCount(); data ++)
        {
            for(Object column : dataset2.getColumnKeys())
            {
                String colStr = "";
                if(column instanceof String) colStr = (String) column;
                dataset.addValue(null, "emptyR" + data, colStr);
            }
        }
        return temp;
    }

    /*private JRExpression expression(String text, Class c){
     JRDesignExpression result = new JRDesignExpression();
     result.setText(text);
     result.setValueClass(c);
     return result;
     }*/
    
    /**
     * We need this class to pass around the min and max of the datasets
     * That's all it does.
     * 
     * It's a stupid class.
    */
    private class MinMax
    {
        double min = 0.0;
        double max = 0.0;
        double getMin() { return min; }
        double getMax() { return max; }
        void setMax(double input) { max = input; }
        void setMin(double input) { min = input; }
    }
}
