/*
 */
package usda.weru.erosion;

import java.awt.Color;
import javax.swing.table.AbstractTableModel;
import java.text.DecimalFormat;
import java.util.*;
import javax.swing.*;
import java.beans.*;
import com.klg.jclass.chart3d.*;
import com.klg.jclass.chart3d.data.*;
import com.klg.jclass.chart3d.event.*;
import com.klg.jclass.table.JCCellRange;
import com.klg.jclass.table.JCTable;
import com.klg.jclass.table.JCTableDataListener;
import com.klg.jclass.table.JCTableEnum;
import com.klg.jclass.table.TableDataModel;
import com.klg.jclass.util.legend.JCLegend;
import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.TFileReader;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.swing.border.EtchedBorder;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;

/**
 * @author maxerdwien
 */
public class Surface extends javax.swing.JPanel {

    private static final long serialVersionUID = 1L;

    /**
     *
     */
    protected static final double HOLE_VALUE = HoleValueChart3dDataModel.DEFAULT_HOLE_VALUE;

    /**
     *
     */
    protected static DecimalFormat decimalFormat = new DecimalFormat("0.000");

    /**
     *
     */
    protected JCEditable3dGridDataSource dataModel;

    /**
     *
     */
    protected MyTableModel dataTableModel;
//    protected JTable surfaceTable;

    /**
     *
     */
    protected JCTable surfaceTable;

    /**
     *
     */
    protected javax.swing.JScrollPane surfaceTablePanel;

    // Constraints for data
    /**
     *
     */
    protected static final double X_ORIGIN = 1.571;

    /**
     *
     */
    protected static final double Y_ORIGIN = 0;

    /**
     *
     */
    protected double X_DELTA = 25.0;

    /**
     *
     */
    protected double Y_DELTA = 25.0;

    /**
     *
     */
    protected int X_SIZE = 10;

    /**
     *
     */
    protected int Y_SIZE = 20;

    /**
     *
     */
    public static final Color DEFAULT_BACKGROUND = new Color(166, 202, 240);

    /**
     *
     */
    protected JCChart3d chart3d = null;

    /**
     *
     */
    protected Chart3dDataView dataView = null;

    /**
     *
     */
    public void init() {
        if (chart3d != null) { // The chart already exists.  Do nothing.
            return;
        }

        chart3d = JCChart3d.createJava2dChart();
        dataView = chart3d.getDataView(0);

        chart3d.setBatched(true);
        chart3d.setAllowUserChanges(false);
        chart3d.getActionTable().addAllDefaultActions();
        chart3d.setBorder(new EtchedBorder());
        chart3d.setBackground(DEFAULT_BACKGROUND);

        chart3d.getChart3dArea().getView3d().setXRotation(45.);
        chart3d.getChart3dArea().getView3d().setYRotation(0.);
        chart3d.getChart3dArea().getView3d().setZRotation(45.);
        chart3d.getChart3dArea().setOpaque(false);

        chart3d.setBatched(false);

        initChart(chart3d);
        initGUI();
    }

    /**
     *
     * @param headline
     * @param fg
     * @param fontSize
     * @return
     */
    public static JLabel createHeadline(String headline, Color fg, float fontSize) {
        JLabel label = new JLabel("<html>" + headline + "</html>");
        label.setHorizontalAlignment(SwingConstants.CENTER);
        label.setForeground(fg);
        label.setOpaque(false);
        label.setBorder(null);
        label.setFont(label.getFont().deriveFont(fontSize));
        return (label);
    }

    /**
     *
     * @return
     */
    public JComponent getChart() {
        return chart3d;
    }

    /**
     *
     * @return
     */
    public JComponent getTable() {
        return surfaceTablePanel;
    }

    private void setODText(StringTokenizer st, JLabel jl) {
        if(st.hasMoreTokens()) {
            jl.setText(st.nextToken().trim());
        }
    }
    
    private void displayTotals() {

        if (od == null) {
            return;
        }     // standalone testing

        String rtnStr = HT_totals.get("t");
        if (rtnStr != null) {
            StringTokenizer st = new StringTokenizer(rtnStr);
            setODText(st, od.JL_totalSoilLoss);
            setODText(st, od.JL_saltCreepLoss);
            setODText(st, od.JL_suspensionLoss);
            setODText(st, od.JL_pm10Loss);
            setODText(st, od.JL_pm2_5Loss);
//            od.JL_totalSoilLoss.setText(st.nextToken().trim());
//            od.JL_saltCreepLoss.setText(st.nextToken().trim());
//            od.JL_suspensionLoss.setText(st.nextToken().trim());
//            od.JL_pm10Loss.setText(st.nextToken().trim());
//            od.JL_pm2_5Loss.setText(st.nextToken().trim());
        }

        od.g_nTotal.setText(Double.toString(Double.NaN));
        od.g_nSalt.setText(Double.toString(Double.NaN));
        od.g_nSusp.setText(Double.toString(Double.NaN));
        od.g_nPM10.setText(Double.toString(Double.NaN));
        od.g_nPM2_5.setText(Double.toString(Double.NaN));
        
        od.g_sTotal.setText(Double.toString(Double.NaN));
        od.g_sSalt.setText(Double.toString(Double.NaN));
        od.g_sSusp.setText(Double.toString(Double.NaN));
        od.g_sPM10.setText(Double.toString(Double.NaN));
        od.g_sPM2_5.setText(Double.toString(Double.NaN));
        
        od.g_eTotal.setText(Double.toString(Double.NaN));
        od.g_eSalt.setText(Double.toString(Double.NaN));
        od.g_eSusp.setText(Double.toString(Double.NaN));
        od.g_ePM10.setText(Double.toString(Double.NaN));
        od.g_ePM2_5.setText(Double.toString(Double.NaN));
        
        od.g_wTotal.setText(Double.toString(Double.NaN));
        od.g_wSalt.setText(Double.toString(Double.NaN));
        od.g_wSusp.setText(Double.toString(Double.NaN));
        od.g_wPM10.setText(Double.toString(Double.NaN));
        od.g_wPM2_5.setText(Double.toString(Double.NaN));
        
        for (String s : new String[]{"n", "s", "e", "w"}) {
            String line = HT_totals.get(s);
            if (line != null && line.length() > 1) {
                String[] values = line.split(" +");
                if (values.length < 7) {
                    continue;
                }
                if ("n".equals(s)) {
                    od.g_nTotal.setText(values[2]);
                    od.g_nSalt.setText(values[3]);
                    od.g_nSusp.setText(values[4]);
                    od.g_nPM10.setText(values[5]);
                    od.g_nPM2_5.setText(values[6]);
                } else if ("s".equals(s)) {
                    od.g_sTotal.setText(values[2]);
                    od.g_sSalt.setText(values[3]);
                    od.g_sSusp.setText(values[4]);
                    od.g_sPM10.setText(values[5]);
                    od.g_sPM2_5.setText(values[6]);
                } else if ("e".equals(s)) {
                    od.g_eTotal.setText(values[2]);
                    od.g_eSalt.setText(values[3]);
                    od.g_eSusp.setText(values[4]);
                    od.g_ePM10.setText(values[5]);
                    od.g_ePM2_5.setText(values[6]);
                } else if ("w".equals(s)) {
                    od.g_wTotal.setText(values[2]);
                    od.g_wSalt.setText(values[3]);
                    od.g_wSusp.setText(values[4]);
                    od.g_wPM10.setText(values[5]);
                    od.g_wPM2_5.setText(values[6]);
                }
            }
        }
    }

    /**
     *
     */
    public void changeData() {
        init();
        initChart(chart3d);
        displayTotals();
        chart3d.invalidate();
        chart3d.repaint();
    }
    Vector<double[]> dataVec = null;
    String header = null;

    /**
     *
     * @param chart3d
     */
    protected void initChart(JCChart3d chart3d) {

        try {
            header = JCB_type.getSelectedItem().toString();
        } catch (NullPointerException npe) {
            header = "no header";
        }
        try {
            int idx = V_labels.indexOf(JCB_period.getSelectedItem().toString().trim() + JCB_type.getSelectedItem().toString().trim());
            try {
                dataVec = V_data.get(idx);
            } catch (java.lang.ArrayIndexOutOfBoundsException e) {
                dataVec = null;
            }
        } catch (NullPointerException npe) {
            // chart has no data; this error has already been noted for the user so we'll just skip out of here
            return;
        }

        chart3d.setBatched(true);

        // creating default chart and setting it's properties
        chart3d.getChart3dArea().getPlotCube().setYScale(1.0);
        chart3d.getChart3dArea().getPlotCube().setXScale(1.0);
        chart3d.getChart3dArea().getView3d().setZRotation(0.0);
        chart3d.setAllowUserChanges(true);
        chart3d.setBackground(new Color(255, 250, 205));

        chart3d.getLegend().setOrientation(JCLegend.VERTICAL);
        chart3d.getLegend().setAnchor(JCLegend.WEST);
        chart3d.getLegend().setVisible(true);

        dataTableModel = new MyTableModel(dataVec);
        dataModel = new JCEditable3dGridDataSource("Editable Grid Data Model",
                dataTableModel.getXGrid(),
                dataTableModel.getYGrid(),
                dataTableModel.getZValues());
        dataModel.addChart3dDataListener(dataTableModel);

        dataView.setElevationDataSource(dataModel);
        dataView.getElevation().setMeshed(true);
        dataView.getElevation().setShaded(true);
        dataView.getContour().setZoned(true);
        dataView.getContour().setContoured(false);

        JCAxis xAxis = chart3d.getChart3dArea().getAxis(JCAxis.AXIS_X);
        xAxis.setTitle("X Length");
        JCAxis yAxis = chart3d.getChart3dArea().getAxis(JCAxis.AXIS_Y);
        yAxis.setTitle("Y Length");
        JCAxis zAxis = chart3d.getChart3dArea().getAxis(JCAxis.AXIS_Z);

        xAxis.getGridLines().setPlaneMask(JCGridLines.ALL_PLANES);
        yAxis.getGridLines().setPlaneMask(JCGridLines.ALL_PLANES);
        zAxis.getGridLines().setPlaneMask(JCGridLines.ALL_PLANES);

        Color color = new Color(100, 148, 235);
        xAxis.getGridLines().getLineStyle().setColor(color);
        yAxis.getGridLines().getLineStyle().setColor(color);
        zAxis.getGridLines().getLineStyle().setColor(color);

        // Setup the header.
        String error = "";
        if (dataVec == null) {
            error = "<p><p><p><center><b><font color=red>No Data Available for Time Period</font></b></center>";
        }
        chart3d.setHeader(createHeadline(
                this.header + error,
                Color.black,
                16.0f));

        chart3d.setBatched(false);

        zAxis = chart3d.getChart3dArea().getAxis(JCAxis.AXIS_Z);

        try {
            MinMax mm = HT_minMax.get(JCB_type.getSelectedItem().toString().trim());
            if (mm.maxValue != mm.minValue) {
                double delta = mm.maxValue - mm.minValue;
                zAxis.setMin(-(mm.maxValue + 0.1 * delta));
                zAxis.setMax(-(mm.minValue - 0.1 * delta));
            } else {
                zAxis.setMin(-(mm.maxValue + 1));
                zAxis.setMax(-(mm.maxValue - 1));
            }
        } catch (NullPointerException npe) {
            zAxis.setMin(-10);
            zAxis.setMax(10);
        }
        try {
            if (surfaceTable == null) {
//                surfaceTable = new JTable(dataTableModel);
                surfaceTable = new JCTable();
            }
            surfaceTable.setDataSource(new InvertingTabelModel(dataTableModel));
            JCCellRange jccr = new JCCellRange(0, 0, JCTableEnum.ALLCELLS, JCTableEnum.ALLCELLS);
            com.klg.jclass.table.CellStyleModel jccsm = surfaceTable.getCellStyle(0, 0);
            jccsm.setHorizontalAlignment(JCTableEnum.RIGHT);
            surfaceTable.setCellStyle(jccr, jccsm);
            surfaceTable.setMaxWidth(JCTableEnum.ALL, 75);
            surfaceTable.setMaxWidth(JCTableEnum.LABEL, 30);
//            surfaceTable.set
//            for (int cdx = 0; cdx < surfaceTable.getNumColumns(); cdx++) {
//                surfaceTable.setPixelWidth(cdx, 70);    // column width in pixels
//            }
//            surfaceTable.setModel(new InvertingTabelModel(dataTableModel));
//           surfaceTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        } catch (NullPointerException e) {
            //System.out.println(e + " surfaceTable.setModel");
        }
    }

    /**
     *
     */
    public void rotateRight() {
        double ang = chart3d.getChart3dArea().getView3d().getZRotation();
        ang += 45;
        if (ang > 360) {
            ang -= 360;
        }
        chart3d.getChart3dArea().getView3d().setZRotation(ang);
    }

    /**
     *
     */
    public void rotateLeft() {
        double ang = chart3d.getChart3dArea().getView3d().getZRotation();
        ang -= 45;
        if (ang < 0) {
            ang += 360;
        }
        chart3d.getChart3dArea().getView3d().setZRotation(ang);
    }

    /**
     *
     */
    protected void initGUI() {

        surfaceTablePanel = new javax.swing.JScrollPane(surfaceTable,
                JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

//        surfaceTable.setPreferredScrollableViewportSize(new java.awt.Dimension(500, 150));
    }

//    protected class InvertingTabelModel implements TableModel {
    /**
     *
     */
    protected static class InvertingTabelModel implements TableDataModel {

        /**
         *
         */
        protected TableModel c_model;

        /**
         *
         * @param model
         */
        public InvertingTabelModel(TableModel model) {
            c_model = model;
        }

        /**
         *
         * @param l
         */
        public void addTableModelListener(TableModelListener l) {
            c_model.addTableModelListener(l);
        }

        /**
         *
         * @param columnIndex
         * @return
         */
        public Class<?> getColumnClass(int columnIndex) {
            return c_model.getColumnClass(columnIndex);
        }

        /**
         *
         * @return
         */
        public int getColumnCount() {
            return c_model.getRowCount();
        }

        /**
         *
         * @param columnIndex
         * @return
         */
        public String getColumnName(int columnIndex) {
            return String.valueOf(columnIndex);
        }

        /**
         *
         * @return
         */
        public int getRowCount() {
            return c_model.getColumnCount();
        }

        /**
         *
         * @param rowIndex
         * @param columnIndex
         * @return
         */
        public Object getValueAt(int rowIndex, int columnIndex) {
            rowIndex = translateRow(rowIndex);
            return c_model.getValueAt(columnIndex, rowIndex);
        }

//        @Override
        /**
         *
         * @param rowIndex
         * @param columnIndex
         * @return
         */
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            //rowIndex = translateRow(rowIndex);
            //return c_model.isCellEditable(columnIndex, rowIndex);
            return false;
        }

        /**
         *
         * @param l
         */
        public void removeTableModelListener(TableModelListener l) {
            c_model.removeTableModelListener(l);
        }

        /**
         *
         * @param value
         * @param rowIndex
         * @param columnIndex
         */
        public void setValueAt(Object value, int rowIndex, int columnIndex) {
            rowIndex = translateRow(rowIndex);
            c_model.setValueAt(value, columnIndex, rowIndex);
        }

        /**
         *
         * @param rowIndex
         * @return
         */
        protected int translateRow(int rowIndex) {
            //int total = getRowCount() - 1;
            //return total - rowIndex;
            return rowIndex;
        }

        @Override
        public void addTableDataListener(JCTableDataListener l) {
        }

        @Override
        public void removeTableDataListener(JCTableDataListener l) {
        }

        @Override
        public int getNumColumns() {
            return c_model.getRowCount();
        }

        @Override
        public int getNumRows() {
            return c_model.getColumnCount();
        }

        @Override
        public Object getTableColumnLabel(int columnIndex) {
            return String.valueOf(columnIndex);
        }

        @Override
        public Object getTableDataItem(int rowIndex, int columnIndex) {
            rowIndex = translateRow(rowIndex);
            return c_model.getValueAt(columnIndex, rowIndex);
        }

        @Override
        public Object getTableRowLabel(int row) {
            return String.valueOf(row);
        }
    }

    /**
     * A class which extends TableModel.  This class generates a default
     * data set using a somewhat arbitrary mathematical function.
     */
    protected class MyTableModel extends AbstractTableModel
            implements Chart3dGridDataModel,
            Chart3dDataListener {

        private static final long serialVersionUID = 1L;

        private double zValues[][];
        private String names[];

        /*	public MyTableModel()
         {
         createZValues();
         }*/
        /**
         *
         * @param dataVec
         */
        public MyTableModel(Vector<double[]> dataVec) {
            createZValues(dataVec);
        }

        private void createZValues(Vector<double[]> dataVec) {

            if (dataVec == null) {
                Y_SIZE = 0;
                X_SIZE = 0;
                return;
            }

            Y_SIZE = dataVec.size();
            double[] xarr = dataVec.get(0);
            X_SIZE = xarr.length;
            zValues = new double[X_SIZE][];
            for (int xdx = 0; xdx < X_SIZE; xdx++) {
                zValues[xdx] = new double[Y_SIZE];
            }

            for (int ydx = 0; ydx < Y_SIZE; ydx++) {
                xarr = dataVec.get(ydx);
                for (int xdx = 0; xdx < X_SIZE; xdx++) {
                    zValues[xdx][Y_SIZE - 1 - ydx] = -xarr[xdx];
                }
            }

        }

        @Override
        public double[][] getZValues() {
            return zValues.clone();
        }

        @Override
        public double[] getXGrid() {
            double[] xGrid = new double[X_SIZE];
            double x = X_ORIGIN;
            for (int i = 0; i < X_SIZE; i++) {
                xGrid[i] = x;
                x += X_DELTA;
            }
            return (xGrid);
        }

        @Override
        public double[] getYGrid() {
            double[] yGrid = new double[Y_SIZE];
            double y = Y_ORIGIN;
            for (int i = 0; i < Y_SIZE; i++) {
                yGrid[i] = y;
                y += Y_DELTA;
            }
            return (yGrid);
        }

        @Override
        public void chart3dDataChange(Chart3dDataEvent e) {
            if (e.getType() == Chart3dGridDataEvent.RELOAD_ZVALUE) {
                JCData3dGridIndex gridIndex = (JCData3dGridIndex) e.getIndex();
                int xIndex = gridIndex.getX();
                int yIndex = gridIndex.getY();
                if (xIndex >= 0
                        && xIndex < zValues.length
                        && yIndex >= 0
                        && yIndex < zValues[xIndex].length) {
                    zValues[xIndex][yIndex] = dataModel.getZValue(xIndex, yIndex);
                    fireTableCellUpdated(xIndex, yIndex);
                }
            }
        }

        /**
         *
         * @param column
         * @return
         */
        @Override
        public String getColumnName(int column) {
            column = translateColumn(column);
            return (String.valueOf(column));
        }

        /**
         *
         * @return
         */
        @Override
        public int getRowCount() {
            return (X_SIZE);
        }

        /**
         *
         * @return
         */
        @Override
        public int getColumnCount() {
            return (Y_SIZE);
        }

        /**
         *
         * @param row
         * @param column
         * @return
         */
        @Override
        public Object getValueAt(int row, int column) {
            int tColumn = translateColumn(column);
            // A data value has been requested.
            try {
                return zValues[row][tColumn];
            } catch (ArrayIndexOutOfBoundsException aioobe) {
                return Double.NaN;
            }
        }

        /**
         *
         * @param row
         * @param column
         * @return
         */
        @Override
        public boolean isCellEditable(int row, int column) {
            // no cells are editable.
            return (false);
        }

        /**
         *
         * @param aValue
         * @param row
         * @param column
         */
        @Override
        public void setValueAt(Object aValue, int row, int column) {
            column = translateColumn(column);
            if (aValue instanceof Double) {
                zValues[row][column] = ((Double) aValue).doubleValue();
            } else {
                try {
                    zValues[row][column] = Double.parseDouble(aValue.toString());
                } catch (NumberFormatException e) {
                    // Just return, there was some error!
                    return;
                }
            }
            fireTableCellUpdated(row, column);
            dataModel.setZValue(row, column, zValues[row][column]);
        }

        /**
         *
         * @param column
         * @return
         */
        @Override
        public Class<?> getColumnClass(int column) {
            return (Double.class);
        }

        /**
         *
         * @param columnIndex
         * @return
         */
        protected int translateColumn(int columnIndex) {
            return getColumnCount() - 1 - columnIndex;
        }
    }

    /**
     *
     * @param args
     * @throws FileNotFoundException
     */
    public static void main(String args[]) throws FileNotFoundException {
        Surface surf = new Surface();
        surf.JCB_period = new javax.swing.JComboBox<>(new String[]{""});
        surf.JCB_period.setSelectedIndex(0);
        surf.JCB_type = new javax.swing.JComboBox<>(GRIDS_DO_NOT_NEGATE);
        surf.JCB_type.setSelectedIndex(0);

        surf.init();

        JFrame demoFrame;
        demoFrame = new JFrame("Surface Demo");
//	demoFrame.addWindowListener(createExitOnCloseListener());
//        demoFrame.getContentPane().add(surf.getChart());
        demoFrame.getContentPane().add(surf.chart3d);
        surf.loadData(new TFile("WE_data/input_files", "test.sgrd"));
//        surf.JMI_openAP();
        demoFrame.pack();
        demoFrame.setVisible(true);
        demoFrame.setSize(800, 600);
        //JP_display.add(surf);
        demoFrame.invalidate();
        demoFrame.repaint();
        demoFrame.setVisible(true);
    }
    private double[] fieldDim = null;
    //Vector totVec;
    //Vector saltVec;
    //Vector suspVec;
    //Vector pm10Vec;
    JComboBox<String> JCB_period = null;
    JComboBox<String> JCB_type = null;
    OutputDisplay od = null;

    public boolean loadData(OutputDisplay od, TFile inpf) throws FileNotFoundException {
        JCB_period = od.JCB_period;
        JCB_type = od.JCB_type;
        this.od = od;
        this.od.setVisible(false);
        boolean result = loadData(inpf);
        //Only show if we have time periods     --Joseph
        JCB_period.setVisible(JCB_period.getItemCount() > 1);
        return result;
    }
    double xLen = 0;
    double yLen = 0;
    double windAng = 0;

    /**
     *
     * @param inpFile
     * @throws FileNotFoundException
     */
    public boolean loadData(TFile inpFile) throws FileNotFoundException {
        od.setVisible(false);
        BufferedReader inpf = null;
        try {
            inpf = new BufferedReader(new TFileReader(inpFile));
        } catch (FileNotFoundException f) {
            System.err.println("File not found " + inpFile.getAbsolutePath());
            return false;
        }

        // load the input data for the run into a display buffer
        while (true) {
            String lineBuf = readLine(inpf);
            if (lineBuf == null) {
                break;
            }
            // Fortran Compiler option can cause each line to contain a blank space
            if (lineBuf.trim().startsWith("wind dir (deg)")) {
                double[] winds = readDoubles(inpf, 0);
                windAng = winds[0];
            }
            if (lineBuf.trim().startsWith("wind direction =")) {
                String[] elems = lineBuf.trim().split("\\s+");
                windAng = Double.parseDouble(elems[elems.length - 2].trim());
            }
            if (lineBuf.trim().startsWith("<field dimensions>")) {
                fieldDim = readDoubles(inpf, 0);
                xLen = fieldDim[3] - fieldDim[1];
                yLen = fieldDim[4] - fieldDim[2];
                break;
            }
        }

        readTables(inpf);
        try
        {
            inpf.close();
        }
        catch(IOException ioe)
        {
            
        }

        try {
            Vector<double[]> tvec = V_data.get(0);
            Y_DELTA = yLen / tvec.size();
            double[] tmp = tvec.get(0);
            X_DELTA = xLen / tmp.length;
            //changeData();
            //invalidate();
            //repaint();
        } catch (ArrayIndexOutOfBoundsException aioobe) {
            od.close();
            //JOptionPane.showMessageDialog(this, "Data vector is empty (no data to graph)", "Graph error", JOptionPane.ERROR_MESSAGE);
            JOptionPane.showMessageDialog(this, "The supplied inputs did not generate any erosion.\n"
                    + "A graph will not be generated.", "No Erosion Calculated", JOptionPane.ERROR_MESSAGE);
            //throw new FileNotFoundException();
            return false;
        }
        changeData();
        invalidate();
        repaint();
        od.setVisible(true);
        return true;
    }
    Vector<String> V_labels = new Vector<>();
    Vector<Vector<double[]>> V_data = new Vector<>();

    private static class MinMax {

//        String type;
        double maxValue = -Double.MAX_VALUE;
        double minValue = Double.MAX_VALUE;
    }
    Hashtable<String, MinMax> HT_minMax = new Hashtable<>();
    Hashtable<String, String> HT_units = new Hashtable<String, String>();
    Vector<String> V_period = new Vector<>();
    Vector<String> V_type = new Vector<>();
    private static final String[] GRIDS_DO_NOT_NEGATE = {"Total Soil Loss", "Saltation/Creep Soil Loss", "Suspension Soil Loss", "PM10 Soil Loss", "PM2_5 Soil Loss"};

    private static boolean negateGrid(String type) {
        if (type == null) {
            return false;
        }
        for (String test : GRIDS_DO_NOT_NEGATE) {
            if (type.contains(test)) {
                return false;
            }
        }
        return true;
    }
    private final Hashtable<String, String> HT_totals = new Hashtable<String, String>();

    private void readTables(BufferedReader inpf) {
        while (true) {
            String buf = readLine(inpf);
            if (buf == null) {
                break;
            } // done with the file
            if (buf.trim().startsWith("**")) { // read total lines
                try {
                    for (int sdx = 0; sdx < 3; sdx++) {
                        inpf.readLine();
                    } // skip 3 lines
                    buf = inpf.readLine();
                    if (buf == null) {
                        break;
                    }
                    buf = buf.replaceAll("-", " ");
                    HT_totals.put("t", buf);
                    for (int sdx = 0; sdx < 4; sdx++) {
                        inpf.readLine();
                    } // skip 4 lines
                    buf = inpf.readLine();
                    HT_totals.put("n", buf);
                    buf = inpf.readLine();
                    HT_totals.put("s", buf);
                    buf = inpf.readLine();
                    HT_totals.put("e", buf);
                    buf = inpf.readLine();
                    HT_totals.put("w", buf);
                } catch (IOException f) {
                    //System.err.println("OD_rT: failed reading total lines");
                    break;
                }
            }
            try {
                if (!buf.trim().startsWith("<grid data>")) {
                    continue;
                }
            } catch (NullPointerException npe) {
                return; //FIXME
            }
            StringTokenizer st = new StringTokenizer(buf, "|");
            st.nextToken();		//throw away tag
            String period = st.nextToken().trim(); // save the period
            String type = st.nextToken().trim(); // save the type
            st.nextToken(); //Not used, Z-axis
            String units = st.nextToken().trim();
            type += " " + units;
            MinMax mm = HT_minMax.get(type);
            if (mm == null) {
                mm = new MinMax();
//                mm.type = type;
            }
            Vector<double[]> rtnVec = new Vector<>();
            while (true) {
                double[] linArry = readDoubles(inpf, 0);
                if (linArry == null) {
                    break;
                }
                for (int jdx = 0; jdx < linArry.length; jdx++) {

                    //Negate the data for the tables not showing soil loss.  --Joseph
                    if (negateGrid(type) && linArry[jdx] != 0) {
                        linArry[jdx] = linArry[jdx] * -1;
                    }

                    if (linArry[jdx] < mm.minValue) {
                        mm.minValue = linArry[jdx];
                    }
                    if (linArry[jdx] > mm.maxValue) {
                        mm.maxValue = linArry[jdx];
                    }
                }
                rtnVec.add(linArry);
            }

            HT_minMax.put(type, mm);
            HT_units.put(type, units);
            V_labels.add(period + type);
            V_data.add(rtnVec);
            if (!V_period.contains(period)) {
                V_period.add(period);
                JCB_period.addItem(period);
            } else {
            }
            if (!V_type.contains(type)) {
                V_type.add(type);
                JCB_type.addItem(type);
            } else {
            }
        }
    }

    private double[] readDoubles(BufferedReader inpf, int numlay) {
        String line = readLine(inpf);
        if (line.trim().startsWith("</grid data>")) {
            return null;
        }
        java.util.StringTokenizer st = new java.util.StringTokenizer(line, " ");
        if (numlay == 0) {
            numlay = st.countTokens();
        }
        double rtnval[] = new double[numlay];
        for (int ldx = 0; ldx < numlay; ldx++) {
            String dblstr = st.nextToken();
            //rtnval[ldx] =  - Double.valueOf(dblstr.trim()).doubleValue();
            rtnval[ldx] = Double.valueOf(dblstr.trim()).doubleValue();

        }
        return rtnval;
    }

    private String readLine(BufferedReader inpf) {
        String line;
        try {
            line = inpf.readLine();
        } catch (IOException e) {
            return null;
        }
        return line;
    }

    /**
     *
     */
    public void makeDrawField() {
        DrawField df = new DrawField();
        this.addPropertyChangeListener(df);
        df.setBackground(java.awt.Color.white);
        changes.firePropertyChange(DataStore.SimRegLenX, "", "" + xLen);
        changes.firePropertyChange(DataStore.SimRegLenY, "", "" + yLen);
        changes.firePropertyChange(DataStore.WeaWindDir, "", "" + windAng);
        df.setSize(100, 100);
        df.setVisible(true);
        df.setLocation(0, 0);
        od.JP_drawField.add(df);

    }
    /****** Property change support methods *****/
    /**
     * RunFileData throws a property change each time setData is called.
     * Wrapper classes listen to determine if the the event is relevant to
     * them.
     */
    private final PropertyChangeSupport changes = new PropertyChangeSupport(this);

    /**
     * Allows the container to add or register some other components to recognize the changes that occur
     * on this component.
     * @param l The listener that listens and reacts towards the the changes to be reflected.
     */
    @Override
    public void addPropertyChangeListener(PropertyChangeListener l) {
        changes.addPropertyChangeListener(l);
    }

    /**
     * Allows the container to remove or de-register some other components and stop recognizing the
     * changes that occur on this component.
     * @param l The listener that does not listen and react towards the the changes to be reflected.
     */
    @Override
    public void removePropertyChangeListener(PropertyChangeListener l) {
        changes.removePropertyChangeListener(l);
    }
}
