//
//Title:        WEPS Skelton File Importer
//Version:
//Copyright:
//Author:       Jim Frankenberger
//Company:      USDA-ARS
//Date:         June 23, 2003
//Description:  Handles unresolved operations or crops in a skelton file by allowing the user
//              to choose an altenate from a WEPS dialog.
//              The modified data is saved back to mcrew arrays which only includes valid weps operations and
//              crops. This code was written with JBuider so the screen layout will probably not be compatibile with
//              with the WebGain environement.
//Modified Decemenber 2004
//
package usda.weru.mcrew;

import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.TFileReader;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.swing.BorderFactory;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTable;
import javax.swing.JTextPane;
import javax.swing.ListSelectionModel;

import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.DefaultTableCellRenderer;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.traversal.DocumentTraversal;
import org.w3c.dom.traversal.NodeFilter;
import org.w3c.dom.traversal.TreeWalker;
import usda.weru.util.Caster;
import usda.weru.util.Util;
import usda.weru.util.WepsFileTypes;

import usda.weru.util.WepsMessage;
import usda.weru.util.WepsMessageDialog;
import usda.weru.util.WepsMessageLog;
//
// class UnknownTable
//
// The only reason for this class is to be able to capture the events for changing rows within the table. The 'linkedBut' variable
// controls whether the find/replace dialog can be used.
//

class UnknownTable extends JTable {

    private static final long serialVersionUID = 1L;

    int focusRow;
    int focusCol;
    JButton linkedBut = null;
    JButton linkedButLoc = null;

    UnknownTable(AbstractTableModel mod, JButton but, JButton butLoc) {
        super(mod);
        linkedBut = but;
        linkedButLoc = butLoc;
    }

    public void setLinkedButton(JButton but) {
        linkedBut = but;
    }

    @Override
    public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {
        focusRow = rowIndex;
        focusCol = columnIndex;
        if (linkedBut != null) {
            if (focusCol == 2 || focusCol == 3) {
                linkedBut.setEnabled(true);
                linkedButLoc.setEnabled(true);
            } else {
                linkedBut.setEnabled(false);
                linkedButLoc.setEnabled(false);
            }
        }
    }

    int getFocusRow() {
        return focusRow;
    }

    int getFocusCol() {
        return focusCol;
    }
};

//
// TablePopup()
//
// Handles the menu diaplayed when a right-click happens to delete a line.
//
class TablePopup extends JPopupMenu {

    private static final long serialVersionUID = 1L;

    ImTableModel model;
    JTable tab;
    int selectRow;

    public TablePopup(JTable table, ImTableModel mod) {
        tab = table;
        model = mod;
        selectRow = 1;
        JMenuItem itemDelete = new JMenuItem("Delete row");
        itemDelete.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                model.deleteSelectedRow(selectRow);
                tab.revalidate();
                tab.repaint();
            }
        });

        add(itemDelete);
        add(new JSeparator());
    }

    public void setRow(int row) {
        selectRow = row;
    }
}

//
// colRender()
//
// Renderer for JTable to highlight the row that the operation or crop is undefined. This keeps track of hotspots that need to
// be changed, these are initially displayed in red when the user corrects the entries they are shown in green. The hotspots array
// keeps track of the row[0], column[1] and enabled[2] properties of each bad line.
//
class colRenderer extends DefaultTableCellRenderer {

    private static final long serialVersionUID = 1L;

    int[][] hotspots = new int[500][3];
    int count = 0;
    int selectRow = -1;
    JButton targetBut;
    JButton locBut;

    public colRenderer(JButton but, JButton but2) {
        targetBut = but;
        locBut = but2;
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
            boolean isFocused, int row, int col) {

        Component component = super.getTableCellRendererComponent(table, value, isSelected, isFocused, row, col);
        Color c = Color.white;
        boolean isHotspot = false;
        for (int i = 0; i < count; i++) {
            if ((row == hotspots[i][0]) && (col == hotspots[i][1]) && (hotspots[i][2] == 1)) {
                c = Color.pink;
                isHotspot = true;
                break;
            } else {
                if ((row == hotspots[i][0]) && (col == hotspots[i][1]) && (hotspots[i][2] == 0)) {
                    c = new Color(223, 247, 215);
                    isHotspot = true;
                    break;
                }
            }
        }
        if (col == 0) {
            c = Color.gray;
        }
        if ((!isSelected) || (col == 2) || (col == 3)) {
            component.setBackground(c);
        } else {
            selectRow = row;
        }

        if (isFocused) {
            ////System.out.println("isFocused=true");
            // code to turn on/off the replace button, only enable it when we are in the operations column
            // and over a hotspot (unknown) operation.
            if ((col == 2) && (isHotspot)) {
                ////System.out.println("   Enabled replace");
                targetBut.setEnabled(true);
                locBut.setEnabled(true);
            } else {
                targetBut.setEnabled(false);
                locBut.setEnabled(false);
                ////System.out.println("   Disabled replace");
            }

            if ((col == 0) || (col == 1)) {
                selectRow = row;
            }
        }

        return component;
    }

    public void setHotspot(int row, int col) {
        for (int i = 0; i < count; i++) {
            if ((hotspots[i][0] == row) && (hotspots[i][1] == col)) {
                hotspots[i][2] = 1;
                return; // already set
            }
        }
        // add to the end of the list
        hotspots[count][0] = row;
        hotspots[count][1] = col;
        hotspots[count][2] = 1;
        count++;
    }

    // removing a hotspot just means setting its enabled property to true
    public void remHotspot(int row, int col) {
        int index = -1;
        for (int i = 0; i < count; i++) {
            if ((hotspots[i][0] == row) && (hotspots[i][1] == col)) {
                hotspots[i][2] = 0;
                break;
            }
        }
    }

    public int getSelectedRow() {
        return selectRow;
    }

    // when a row is deleted the locations of the hotspots will change, this fixes all the entries. If the deleted row was a
    // hotspot then its row is set to -1 it prevent it from being used again
    public void adjustHotspots(int row) {
        for (int i = 0; i < count; i++) {
            if (hotspots[i][0] == row) {
                hotspots[i][0] = -1;
            } else if (hotspots[i][0] > row) {
                hotspots[i][0] = hotspots[i][0] - 1;
            }
        }
    }

    // check if any hotspots reamin. This is called before closing to check if we have a "good" weps file
    public boolean noHotspots() {
        for (int i = 0; i < count; i++) {
            if (hotspots[i][0] != -1) {
                if (hotspots[i][2] == 1) {
                    return false;
                }
            }
        }

        return true;
    }

    public boolean isHotspot(int row, int col) {
        for (int i = 0; i < count; i++) {
            if ((hotspots[i][0] == row) && (hotspots[i][1] == col)) {
                return true;
            }
        }
        return false;
    }

    public void removeAllHotspots() {
        for (int i = 0; i < 500; i++) {
            hotspots[i][0] = -1;
            hotspots[i][2] = 0;
        }
    }
};

//
// ImTableModel()
//
// Table model component for JTable to supply data for the grid.
//
class ImTableModel extends AbstractTableModel {

    private static final long serialVersionUID = 1L;

    String[] columnNames = {"Num", "Date", "Operation", "Crop"};
    String[] dates;
    String[] ops;
    String[] crops;
    colRenderer crend;
    int lines = 0;
    int lastReplaceRow = -1;
    int lastReplaceCol = -1;
    String previousOp;
    String newOp;
    String previousCrop;
    String newCrop;
    SkelImportPanel skelparent;

    @Override
    public int getColumnCount() {
        return columnNames.length;
    }

    @Override
    public int getRowCount() {
        return lines;
    }

    @Override
    public String getColumnName(int col) {
        return columnNames[col];
    }

    @Override
    public Object getValueAt(int row, int col) {

        switch (col) {
            case 0:
                return row;
            case 1:
                return dates[row];
            case 2:
                return ops[row];
            case 3:
                return crops[row];
        }

        return "?";
    }

    @Override
    public Class<?> getColumnClass(int c) {
        return getValueAt(0, c).getClass();
    }

    // only allow editing of op and crop column with entry.
    @Override
    public boolean isCellEditable(int row, int col) {
        if (col == 2) {
            if (crend.isHotspot(row, col)) {
                return true;
            } else {
                return false;
            }
        }

        if (col == 3) {
            // check operation column to see if it requires a crop 8-19-2005
            if (skelparent.requiresCrop(ops[row])) {
                return true;
            }
            if (crops[row].equals("")) {
                return false;
            } else {
                if (crend.isHotspot(row, col)) {
                    return true;
                } else {
                    return false;
                }
            }
        }
        return false;
    }

    // this will be called when the user changes an entry in the op or crop column. The entries beginning with ***
    // are bogus and indicate bad weps operations. If an entry is fixed this will also update the any hotspots in the
    // renderer.
    @Override
    public void setValueAt(Object value, int row, int col) {

        if (col == 2) {
            previousOp = ops[row];
            newOp = (String) value;
            ops[row] = String.class.cast(value);
        } else if (col == 3) {
            previousCrop = crops[row];
            newCrop = (String) value;
            crops[row] = String.class.cast(value);
        }
        if (crend != null) {
            String str1 = (String) value;
            if (str1.startsWith("***") == false) {
                crend.remHotspot(row, col);
            } else {
                crend.setHotspot(row, col);
            }
            lastReplaceRow = row;
            lastReplaceCol = col;
        }
//        fireTableCellUpdated(row, col);
        fireTableDataChanged();
    }

    public int getLastRow() {
        return lastReplaceRow;
    }

    public int getLastCol() {
        return lastReplaceCol;
    }

    public String getPrevOp() {
        return previousOp;
    }

    public String getPrevCrop() {
        return previousCrop;
    }

    public String getNewOp() {
        return newOp;
    }

    public String getNewCrop() {
        return newCrop;
    }

    // called during initialization to get the real data into the table model.
    // insures that the dates are ordered
    void setContent(Vector<String> dateV, Vector<String> opsV, Vector<String> cropsV, int rotationYears) {
        dates = new String[dateV.size()];
        ops = new String[dateV.size()];
        crops = new String[dateV.size()];

        lines = dateV.size();

        int[] days = new int[lines];

        String month;
        String day;
        String year;
        String str;
        int yearI;
        int monthI;
        int dayI;
        int duration;
        int firstMonth;
        int firstDay;
        int firstYear;
        int decYear;

        boolean adjustYear0 = false;

        yearI = monthI = dayI = firstMonth = firstDay = firstYear = decYear = 0;

        int minJul = Integer.MAX_VALUE;
        int maxJul = Integer.MIN_VALUE;

        for (int i = 0; i < dateV.size(); i++) {
            str = dateV.elementAt(i);

            StringTokenizer thedate = new StringTokenizer(str, "/");
            month = thedate.nextToken();
            day = thedate.nextToken();
            year = thedate.nextToken();

            // if the year field is 0 set a flag to increment all year fields by 1
            yearI = Integer.parseInt(year);
            monthI = Integer.parseInt(month);
            dayI = Integer.parseInt(day);
            if (yearI == 0) {
                adjustYear0 = true;
            }
            if (adjustYear0) {
                yearI++;
            }
            // save the first entry, may need to be checked later
            if (i == 0) {
                firstMonth = monthI;
                firstDay = dayI;
                firstYear = yearI;

                if (firstYear > 1) {
                    decYear = firstYear - 1;
                    firstYear -= decYear;
                }
            }

            yearI = yearI - decYear;

            int jul = tojulian(monthI, dayI, yearI);
            days[i] = jul;

            //keep track of the man and max dates
            minJul = Math.min(minJul, jul);
            maxJul = Math.max(maxJul, jul);

            //dates[i] = dateV.elementAt(i);
            dates[i] = month + "/" + day + "/" + Integer.toString(yearI);
            ops[i] = opsV.elementAt(i);
            if (cropsV.elementAt(i).equals("0")) {
                crops[i] = "";
            } else {
                crops[i] = cropsV.elementAt(i);
            }
        }

        SkelManConverter.ConversionTask<TFile> task = SkelManConverter.ConversionContext.getTask();

        if ((rotationYears == (yearI - 1)) && (tojulian(firstMonth, firstDay, 1) > tojulian(monthI, dayI, 1))) {

            //don't wrap the rot files            
            if (task == null || !WepsFileTypes.Rotation.accept(task.getTo())) {
                //man files need to wrap!
                changeLastYears(firstYear, yearI, days, dates);

            }

        }

        //only make rots for rotations when days <= rotationyears * 365 
        //and only push onto the stack if the current target is a man
        if (task != null && WepsFileTypes.Management.accept(task.getTo())) {
            if (maxJul - minJul <= rotationYears * 365) {
                //queue up a rot file!
                Stack<SkelManConverter.ConversionTask<TFile>> stack = SkelManConverter.ConversionContext.getStack();

                String rotName = Util.purgeExtensions(task.getTo().getName(), WepsFileTypes.Management.getExtension());
                rotName = Util.addExtensions(rotName, WepsFileTypes.Rotation.getExtension());

                TFile rotFile = new TFile(task.getTo().getParentFile(), rotName);

                stack.push(new SkelManConverter.ConversionTask<TFile>(task.getFrom(), rotFile));
            }
        }

        // insure that the entries are ordered by date, use a slow bubblesort because almost all the time the
        // entries should be ordered.
        boolean swapped;
        String tempo;
        int temp;
        do {
            swapped = false;

            for (int i = 0; i < lines - 1; i++) {
                if (days[i] > days[i + 1]) {
                    temp = days[i + 1];
                    days[i + 1] = days[i];
                    days[i] = temp;

                    tempo = dates[i + 1];
                    dates[i + 1] = dates[i];
                    dates[i] = tempo;

                    tempo = ops[i + 1];
                    ops[i + 1] = ops[i];
                    ops[i] = tempo;

                    tempo = crops[i + 1];
                    crops[i + 1] = crops[i];
                    crops[i] = tempo;

                    swapped = true;
                }
            }
        } while (swapped);
        fireTableDataChanged();
    }

    // changes all entries from last year into first year.
    // added 7-6-2005 jrf
    void changeLastYears(int first, int last, int[] days, Object[] dates) {
        int yearI;
        int monthI;
        int dayI;
        String month;
        String day;
        String year;
        String str;
        for (int i = 0; i < dates.length; i++) {
            str = (String) dates[i];

            StringTokenizer thedate = new StringTokenizer(str, "/");
            month = thedate.nextToken();
            day = thedate.nextToken();
            year = thedate.nextToken();

            yearI = Integer.parseInt(year);

            if (yearI == last) {
                // needs to change
                monthI = Integer.parseInt(month);
                dayI = Integer.parseInt(day);
                yearI = first;
                days[i] = tojulian(monthI, dayI, yearI);
                dates[i] = month + "/" + day + "/" + Integer.toString(yearI);
            }
        }
    }

    // converts a month,day,year into a julian date so it is easier to compare
    int tojulian(int month, int day, int year) {
        int[] dmon = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
        return dmon[month - 1] + day + (year * 365);
    }

    // called when the dialog will be closed, copies the current model data back to the main mcrew arrays
    // so they can be converted to weps files
    void getContent(Vector<String> dateV, Vector<String> opsV, Vector<String> cropsV) {
        for (int i = 0; i < lines; i++) {
            dateV.setElementAt(dates[i], i);
            opsV.setElementAt(ops[i], i);
            if ((crops[i].equals("")) || (crops[i].equals(" "))) {
                cropsV.setElementAt("0", i);
            } else {
                cropsV.setElementAt(crops[i], i);
            }
        }
    }

    void copyContent(Vector<String> dateV, Vector<String> opsV, Vector<String> cropsV) {
        for (int i = 0; i < lines; i++) {
            dateV.addElement(dates[i]);
            opsV.addElement(ops[i]);
            if ((crops[i].equals("")) || (crops[i].equals(" "))) {
                cropsV.addElement("0");
            } else {
                cropsV.addElement(crops[i]);
            }
        }
    }

    public void setMyRender(colRenderer c) {
        crend = c;
    }

    public void setOp(String str, int line) {
        ops[line] = str;
    }

    public void setCrop(String str, int line) {
        crops[line] = str;
    }

    // called from the popup to delete a row in the table
    public void deleteSelectedRow(int row) {
        if (crend == null) {
            return;
        }
        if ((row < 0) || (row >= ops.length)) {
            return;
        }
        String msg = "Delete " + ops[row] + " operation on " + dates[row] + " ?";
        if (JOptionPane.showConfirmDialog(null, msg, "Delete row", JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) {
            return;
        }
        if (row != -1) {
            //System.out.println("**Delete row " + row);
            for (int i = row + 1; i < lines; i++) {
                dates[i - 1] = dates[i];
                ops[i - 1] = ops[i];
                crops[i - 1] = crops[i];
            }
        }
        lines--;
        crend.adjustHotspots(row);
    }

    // called on OK to check if any unresolved entries are left
    public boolean verify() {
        boolean rc = crend.noHotspots();

        if (rc == false) {
            return false;
        }
        rc = verifyRequiredFields();
        if (rc == false) {
            return false;
        }
        return true;
    }

    public boolean verifyRequiredFields() {

        String tbuf;
        String verifyBuf = "";
        for (int i = 0; i < lines; i++) {
            if (skelparent.requiresCrop(ops[i])) {
                if ((crops[i].equals("")) || (crops[i].equals("---Delete Crop---")) || (crops[i]).startsWith("***")) {
                    crops[i] = skelparent.getDefaultCrop(ops[i]);
                    Node node = SkelImportPanel.loadXMLCropFile(MCREWConfig.getDirectoryName(XMLConstants.scrop)
                            + "/" + crops[i] + ".crop");
                    if (node == null) {
                        if (!skelparent.dbCropNames.contains("***" + crops[i])) {
                            skelparent.dbCropNames.add("***" + crops[i]);
                        }
                        tbuf = "Line " + (i) + ": Operation requires a plant to be selected. Default plant used.\n";
                        crend.setHotspot(i, 3);
                        verifyBuf += tbuf;
                    }
                    if (crops[i].equals("no crop")) {
                        tbuf = "Line " + (i) + ": Operation requires a plant to be selected. Default plant used.\n";
                        crend.setHotspot(i, 3);
                        verifyBuf += tbuf;
                    }
                }
            } else {
                // operation does not require a crop, make sure done is listed
                if ((!crops[i].equals("")) && (!(crops[i].equals("---Delete Crop---")))
                        && (!ops[i].equals("---Delete Operation---"))) {
                    tbuf = "Line " + (i) + ": Operation does not require a plant.\n";
                    crend.setHotspot(i, 3);
                    verifyBuf += tbuf;
                }
            }
        }

        if (!verifyBuf.equals("")) {
            JOptionPane.showMessageDialog(null, verifyBuf, "Errors in verifying correct WEPS management",
                    JOptionPane.INFORMATION_MESSAGE);
            return false;
        }
        return true;
    }

    void doReplace(String oldstr, String newstr, boolean doall, int colNum) {
        //System.out.println("In doReplace()-colNum:"+colNum+" lines"+lines);
        for (int i = 0; i < lines; i++) {
            //System.out.println("value of i:"+i);
            String str = "";
            if (colNum == 2) {
                str = ops[i];
            } else {
                str = crops[i];
            }
            if (str.compareTo(oldstr) == 0) {
                //String nstr  = new String(newstr);
                if (colNum == 2) {
                    ops[i] = newstr;
                } else {
                    crops[i] = newstr;
                }
                if (crend != null) {
                    if (newstr.startsWith("***") == false) {
                        crend.remHotspot(i, colNum);
                    } else {
                        crend.setHotspot(i, colNum);
                    }
                }
//                fireTableCellUpdated(i, colNum);
                fireTableDataChanged();
                if (doall == false) {
                    break;
                }
            }
        }
    }

    public void cleanup() {
        dates = null;
        crops = null;
        ops = null;
    }

    public void setSkelParent(SkelImportPanel p) {
        skelparent = p;
    }
};

/**
 * Main dialog for import function, displays some choices and then a grid showing
 * the skeleton file.
 */
public class SkelImportPanel extends JDialog {

    private static final long serialVersionUID = 1L;

    private static final Logger LOGGER = Logger.getLogger(SkelImportPanel.class);
    JPanel tablePanel = new JPanel();
    BorderLayout borderLayout1 = new BorderLayout();
    BorderLayout borderLayout2 = new BorderLayout();
    Vector<String> dbOpNames = new Vector<String>();
    Vector<String> dbCropNames = new Vector<String>();
    Vector<String> untranslatedCrops = new Vector<String>();
    Vector<String> untranslatedOperations = new Vector<String>();
    //Vector dbOpPathNames = new Vector();
    //Vector dbCropPathNames = new Vector();
    JTextPane jtextPane = new JTextPane();
    JProgressBar progressBar = new JProgressBar();
    JPanel buttonPanel = new JPanel();
    JPanel headerPanel = new JPanel();
    JButton okButton = new JButton();
    JButton skipButton = new JButton();
    JButton replBut = new JButton();
    JButton replLocBut = new JButton();
//    JButton skipRemainingButton=new JButton();
    JButton cancelAllButton = new JButton();
    JScrollPane jScroll = new JScrollPane();
    ImTableModel tmodel = new ImTableModel();
    String filename;
    Frame owner;
    WindowAdapter windAdap;
    ComponentAdapter compAdap;
    FocusListener tabFocus;
    MouseAdapter tabMouse;
    // New addtions
    Vector<String> opnames;
    Vector<String> opdates = new Vector<>();
    Vector<String> vegnames = new Vector<>();
    String destManFile;
    HashMap<String, String> globalMapOp = new HashMap<>();
    HashMap<String, String> globalMapDelete = new HashMap<>();
    HashMap<String, String> globalMapDeleteCrop = new HashMap<>();
    HashMap<String, String> cachedCropChecker = new HashMap<>();
    int rotationYears = 1;
    String skelNotes = "";
    String skelVersion = "Unknown";
    String skelProgram = "Unknown";
    UnknownTable jTable1 = new UnknownTable(tmodel, replBut, replLocBut);
    TablePopup tablePopup = new TablePopup(jTable1, tmodel);
    int badRow = 0;
    colRenderer r1 = new colRenderer(replBut, replLocBut);
    boolean findOp = true;
    boolean translationLoaded = false;
    String unknownOp;
    boolean altPicked = true;
    //This flag is made true once the cancel All button is pressed
    boolean skipRemainingFlag = false;
    boolean cancelAllFlag = false;

    //Handle cancelAll button     
    void processReplace(boolean isGlobal) {
        int lastRow = tmodel.getLastRow();
        String oldString;
        String newString;

        if (tmodel.getLastCol() == 2) {
            oldString = tmodel.getPrevOp();
            newString = tmodel.getNewOp();
        } else {
            oldString = tmodel.getPrevCrop();
            newString = tmodel.getNewCrop();
        }

        if (isGlobal) {
            if (oldString.startsWith("***")) {
                addToGlobals(globalMapOp, oldString.substring(3), newString);
            } else {
                addToGlobals(globalMapOp, oldString, newString);
            }
        }

        //
        // Need to go through all the lines in the table and do the change, then redisplay the window
        if (oldString.compareTo(newString) != 0) {
            // Need to tell the current combobox to go away to insure that the selection gets
            // applied to the current cell, without this the combobox selection overrules anything we set
            // in the table model.
            TableCellEditor ed = jTable1.getCellEditor(lastRow, tmodel.getLastCol());
            if (ed != null) {
                ed.cancelCellEditing();
            }
            tmodel.doReplace(oldString, newString, true, tmodel.getLastCol());

            jTable1.invalidate();
            jTable1.repaint();
        }
    }

    /**
     *  Three argument constructor that knows which frame component it resides in, the
     * title for this fram and whether its modal or not.
     * @param frame The address of the Frame object where this will exist.
     * @param title The title string that will be used to set the frame title in which
     * the it appears.
     * @param modal True if thedialog is modal i:e other objects cannot be accessed
     * @param mode enum {NORMAL, UPDATE, FROM_NRCS} to determine what comments if any to add to the man file.
     * when this is open else false.
     */
    public SkelImportPanel(Frame frame, String title, boolean modal, ManageData.WriteFileMode mode) {
        super(frame, title, modal);
        try {
            owner = frame;
            jbInit();
            progressBar.setStringPainted(true);
            progressBar.setString("0 / 0");
            pack();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        c_mode = mode;
        tmodel.setSkelParent(this);
    }

    /**
     * Default constructor that sets the frame title to an empty string.
     */
    public SkelImportPanel() {
        this(null, "", false, ManageData.WriteFileMode.NORMAL);
    }

    /**
     *
     * @param value
     * @param total
     */
    public void setProgress(int value, int total) {
        progressBar.setMaximum(total);
        progressBar.setValue(value);
        progressBar.setString(Integer.toString(value) + " / " + Integer.toString(total));
    }

    //
    // loadRUSLE2TRanslationTable()
    //
    // This loads the text file that contains mappings between RUSLE2 names and WEPS names.
    // This information is stored in the global search/replace map so that when an unknown entry
    // is encountered the WEPS replacement can be inserted automatically.
    //
    private void loadRUSLE2TranslationTable() throws FileNotFoundException {

        String transFile = MCREWConfig.getSkelTranslationPath();
        if (transFile == null || transFile.trim().length() == 0) {
            //the file is optional
            return;
        }
        BufferedReader input = null;

        try {
            TFile trans = new TFile(transFile);

            input = new BufferedReader(new TFileReader(trans));
            String line = null;
            String startStr = null;
            String rusle2Name = null;
            String wepsName = null;
            String action = null;
            String errorMsg = "";
            int lineCount = 1;
            WepsMessageLog log = new WepsMessageLog();
            while ((line = input.readLine()) != null) {
                if (line.trim().length() > 0) {
                    // check for a # character at the start, this is a comment character.
                    if (line.startsWith("#") == false) {
                        // line is real data, read the RUSLE2 name which is upto the first | character
                        int delim = line.indexOf("|");
                        if (delim > 0) {
                            rusle2Name = line.substring(0, delim);

                            int delim2 = line.lastIndexOf("|");
                            if ((delim2 > delim) && ((delim2 + 1) < line.length())) {
                                action = line.substring(delim + 1, delim2);
                                action = action.trim();
                                if (action.equalsIgnoreCase("rename")) {
                                    wepsName = line.substring(delim2 + 1);
                                    String foundWepsName = findWepsName(wepsName);
                                    if (foundWepsName != null || wepsName.equalsIgnoreCase("none")) {
                                        // add this entry to the list of global substitutions that are done automatically
                                        if (wepsName.equalsIgnoreCase("none")) {
                                            wepsName = " ";
                                        }
                                        if (addToGlobals(globalMapOp, rusle2Name, foundWepsName) == false) {
                                            log.logMessage(WepsMessage.warningMessage("Line " + lineCount
                                                    + ": RUSLE2 name maps to multiple WEPS names: " + rusle2Name));
                                        }
                                    } else {
                                        // error weps name not found in operations or crops list
                                        log.logMessage(WepsMessage.warningMessage("Line " + lineCount
                                                + ": WEPS name not found in crops or operations list: " + wepsName));
                                    }
                                } else if (action.equalsIgnoreCase("delete")) {
                                    addToDeleteList(globalMapDelete, rusle2Name);
                                } else if (action.equalsIgnoreCase("renameAndDeleteCrop")) {
                                    wepsName = line.substring(delim2 + 1);
                                    String foundWepsName = findWepsName(wepsName);
                                    if (foundWepsName != null) {
                                        // add this entry to the list of global substitutions that are done automatically
                                        if (addToGlobals(globalMapOp, rusle2Name, foundWepsName) == false) {
                                            log.logMessage(WepsMessage.warningMessage("Line " + lineCount
                                                    + ": RUSLE2 name maps to multiple WEPS names: " + rusle2Name));
                                        } else {
                                            addToDeleteCropList(globalMapDeleteCrop, rusle2Name);
                                        }
                                    } else {
                                        // error weps name not found in operations or crops list
                                        log.logMessage(WepsMessage.warningMessage("Line " + lineCount
                                                + ": WEPS name not found in crops or operations list: " + wepsName));
                                    }
                                } else {
                                    // error, unknown action
                                    log.logMessage(WepsMessage.warningMessage("Line " + lineCount
                                            + ": Unknown action, valid entries are RENAME or DELETE or RENAMEANDDELETECROP\n"
                                            + action));
                                }
                            } else {
                                // error, missing second | symbol
                                log.logMessage(WepsMessage.warningMessage("Line " + lineCount + ": Missing | character."));
                            }
                        } else {
                            // error no | delimter
                            log.logMessage(WepsMessage.warningMessage("Line " + lineCount + ": Missing | character."));
                        }
                    }
                }
                lineCount++;
            } // end while
            String logFile = new TFile(MCREWConfig.getConfigDir(), "RUSLE2_Translation.log").getAbsolutePath();
            if (log.hasMessage()) {
                int result = WepsMessageDialog.showMessageList(this, "WEPS Translation File",
                        "Translation File: " + transFile, logFile, log.getMessages());
                switch (result) {
                    case WepsMessageDialog.SAVE_OPTION:
                        LOGGER.debug("User saved weps messages.");
                        break;
                    case WepsMessageDialog.CANCEL_OPTION:
                        LOGGER.debug("User canceled weps message dialog.");
                        break;
                    case WepsMessageDialog.OK_OPTION:
                        LOGGER.debug("User closed weps message dialog.");
                        break;
                }
            }
            if (input != null) {
                input.close();
            }
        } catch (FileNotFoundException ex) {
            throw ex;
        } catch (IOException ex) {
            // this is an error we should handle
            ex.printStackTrace();
        }
    }

    //
    // findWepsName()
    //
    // This searches the weps operation and crop database vectors for a name that matches.
    // This is used to verify that the rusle2 to weps translation entries for weps are valid.
    //
    private String findWepsName(String nameToFind) {

        String opName = findWepsOpName(nameToFind);
        if (opName != null) {
            return opName;
        }

        String cropName = findWepsCropName(nameToFind);
        if (cropName != null) {
            return cropName;
        }

        return null;

    }

    private String findWepsOpName(String nameToFind) {

        //trim and lowercase the name
        nameToFind = nameToFind != null ? nameToFind.trim().toLowerCase() : null;

        //search the ops        
        for (String name : dbOpNames) {
            //trim and lowercase
//            String name2 = name != null ? name.trim().toLowerCase() : "";
//            if(name2.endsWith(nameToFind)){
//                return name;
//            }
            if (name == null || name.trim().length() == 0) {
                continue;
            }
            TFile test = new TFile(name);
            if (test.getName().equalsIgnoreCase(nameToFind)) {
                return name;
            }
        }

        //fallback to nothing
        return null;
    }

    private String findWepsCropName(String nameToFind) {

        //trim and lowercase the name
        nameToFind = nameToFind != null ? nameToFind.trim().toLowerCase() : null;

        //search the crops
        for (String name : dbCropNames) {
            //trim and lowercase
//            String name2 = name != null ? name.trim().toLowerCase() : "";
            if (name == null || name.trim().length() == 0) {
                continue;
            }
            TFile test = new TFile(name);
            if (test.getName().equalsIgnoreCase(nameToFind)) {
                return name;
            }

//            if(name2.endsWith(nameToFind)){
//                return name;
//            }
        }

        //fallback to nothing
        return null;
    }

    //
    // addToGlobals()
    //
    // Keep a list of substitutions that should be applied across all files. Returns true if added, false if entry already present
    //
    private boolean addToGlobals(HashMap<String, String> map, String oldStr, String newStr) {
        boolean rc = true;
        if (map.containsKey(oldStr) == false) {
            map.put(oldStr, newStr);
            //System.out.println("Added to Map: " +oldStr+ " & "+newStr);
        } else {
            //System.out.println("Global Subst map already contains: "+oldStr+"\n");
            rc = false;
        }
        return rc;
    }

    //
    // addToDeleteList()
    //
    // Adds a rusle2 operation or crop name to the list that will cause it to be deleted from
    // any weps generated data.
    private void addToDeleteList(HashMap<String, String> map, String rusle2name) {
        boolean rc = true;
        if (map.containsKey(rusle2name) == false) {
            map.put(rusle2name, "");
            //System.out.println("Added to delete list: " +rusle2name);
        }
    }

    //
    // addToDeleteCropList()
    //
    // Adds a rusle2 operation to the list that will cause any crop entry to be deleted that is with this operation.
    //
    private void addToDeleteCropList(HashMap<String, String> map, String rusle2name) {
        boolean rc = true;
        if (map.containsKey(rusle2name) == false) {
            map.put(rusle2name, "");
            //System.out.println("Added to delete crop list: " +rusle2name);
        }
    }

    //
    // showUnknownOps()
    //
    // Called by the main mcrew before it will translate a skeleton file into a weps file. The
    // argument Vectors will be filled in with the updated data.
    //
    boolean showUnknownOps() {
        // First check if we already have an alternate for this operation
        String str1;
        boolean found;
        boolean hashotspot = false;

        Vector<String> dates = new Vector<>();
        Vector<String> ops = new Vector<>();
        Vector<String> crops = new Vector<>();
        Vector<String> mcDates = new Vector<>();
        Vector<String> mcOps = new Vector<>();
        Vector<String> mcCrops = new Vector<>();

        // retrieve the data from the table model
        tmodel.copyContent(dates, ops, crops);

        for (int i = 0; i < tmodel.getRowCount(); i++) {
            // first check an operation to see if it is ok
            str1 = ops.elementAt(i);
            if (str1 != null) {
                str1 = str1.trim(); // added 12-29-2004
            }
            found = false;

            for (int j = 0; j < dbOpNames.size(); j++) {
                String dop = dbOpNames.elementAt(j);
                int endpath = dop.lastIndexOf("/");
                String opBase = dop;
                if ((endpath > 0) && (endpath < dop.length())) {
                    opBase = dop.substring(endpath + 1);
                }
                if (!opBase.startsWith("***")) {
                    if (opBase.equalsIgnoreCase(str1)) {
                        found = true;
                        ops.set(i, dop);
                        tmodel.setOp(dop, i);
                        break;
                    }
                }
            }

            // if the operation was not found check the global subst list for a match
            if ((found == false) && (globalMapOp.containsKey(str1))) {
                found = true;
                ops.set(i, globalMapOp.get(str1));
                tmodel.setOp(globalMapOp.get(str1), i);
            }

            // if this operation was not found then add a bogus opertaion to the list of
            // weps operations and set a hotspot which will highlight this item
            if (found == false) {
                hashotspot = true;
                r1.setHotspot(i, 2);
                str1 = "***" + ops.elementAt(i);

                tmodel.setOp(str1, i);
                boolean alreadyListed = false;
                for (int j = 0; j < dbOpNames.size(); j++) {
                    if (str1.equalsIgnoreCase(dbOpNames.elementAt(j))) {
                        found = true;
                        alreadyListed = true;
                        break;
                    } //else
                    //if (((String)dbOpNames.elementAt(j)).startsWith("***") == false)
                    //   break;
                }
                if (alreadyListed == false) {
                    //Operation not translated
                    dbOpNames.addElement(str1);
                    untranslatedOperations.addElement(ops.elementAt(i));
                    JComboBox<String> cBox = new JComboBox<>(dbOpNames);
                    DefaultCellEditor ed = new DefaultCellEditor(cBox);
                    TableColumnModel tc = jTable1.getColumnModel();
                    TableColumn opcol = tc.getColumn(2);
                    opcol.setCellEditor(ed);
                    ed = null;
                    cBox = null;
                }
            }

            // go through the same procedure for the crops database
            found = false;
            str1 = crops.elementAt(i);

            OperationObject object = new OperationObject();
            Node node = loadXMLOperationsFile(MCREWConfig.getDirectoryName(XMLConstants.soperation) + "/" + ops.get(i) + ".oprn");
            if (node != null) {
                object.initialize(node);
            }
            if (object.hasCrop() && ((str1 == null) || "".equals(str1))) {
                str1 = object.getCropName();
            }

            if ((str1 != null) && (!str1.equals("")) && (!str1.equals("0"))) {
                str1 = str1.trim(); // aded 12-29-2004
                for (int j = 0; j < dbCropNames.size(); j++) {
                    String dcrop = dbCropNames.elementAt(j);
                    int endpath = dcrop.lastIndexOf("/");
                    String vegBase = dcrop;
                    if ((endpath > 0) && (endpath < dcrop.length())) {
                        vegBase = dcrop.substring(endpath + 1);
                    }
                    if (!vegBase.startsWith("***")) {
                        if (vegBase.equalsIgnoreCase(str1)) {
                            found = true;
                            crops.set(i, dcrop);
                            tmodel.setCrop(dcrop, i);
                            break;
                        }
                    }
                }

                // if the crop was not found check the global subst list for a match
                if ((found == false) && globalMapOp.containsKey(str1)) {
                    found = true;
                    crops.set(i, globalMapOp.get(str1));
                    tmodel.setCrop(globalMapOp.get(str1), i);
                }

                if (found == false) {
                    hashotspot = true;
                    r1.setHotspot(i, 3);
                    str1 = "***" + crops.elementAt(i);

                    tmodel.setCrop(str1, i);
                    boolean alreadyListed = false;
                    for (int j = 0; j < dbCropNames.size(); j++) {
                        if (str1.equalsIgnoreCase(dbCropNames.elementAt(j))) {
                            found = true;
                            alreadyListed = true;
                            break;
                        } //else
                        //if (((String)dbCropNames.elementAt(j)).startsWith("***") == false)
                        //  break;
                    }
                    if (alreadyListed == false) {
                        //Crop not translated
                        dbCropNames.addElement(str1);
                        untranslatedCrops.addElement(crops.elementAt(i));
                        JComboBox<String> cBox = new JComboBox<>(dbCropNames);
                        DefaultCellEditor ed = new DefaultCellEditor(cBox);
                        TableColumnModel tc = jTable1.getColumnModel();
                        TableColumn cropcol = tc.getColumn(3);
                        cropcol.setCellEditor(ed);
                        ed = null;
                        cBox = null;
                    }
                }
            }
        }

        // only show the dialog if there are unrecognized ops or crops
        int reason = 0;
        if ((hashotspot) || (tmodel.verify() == false)) {
            altPicked = false;
            setVisible(true);
        }

        if (altPicked) {
            // copy the model table into the vectors passed to this function
            tmodel.copyContent(mcDates, mcOps, mcCrops);
            altPicked = writeWEPSManagementFile(destManFile, mcDates, mcOps, mcCrops);
        }

        return altPicked;
    }

    // setDatabases()
    //
    // Initially called to fill the choice list for a crop or operation.
    //
    boolean setDatabases(String opdb, String vegdb) {

        dbCropNames.removeAllElements();
        dbOpNames.removeAllElements();
        buildDatabaseList(vegdb, "crop", dbCropNames);
        buildDatabaseList(opdb, "oprn", dbOpNames);
        dbCropNames.add("---Delete Crop---");
        dbOpNames.add("---Delete Operation---");

        findOp = true;

        if (!translationLoaded) {
            try {
                loadRUSLE2TranslationTable();
            } catch (FileNotFoundException fnfe) {
                JOptionPane.showMessageDialog(this, "The specifed translation file was not found.\n"
                        + MCREWConfig.getSkelTranslationPath(), "Missing File", JOptionPane.ERROR_MESSAGE);
                return false;
            }

            translationLoaded = true;
        }

        return true;
    }

    // sortVector()
    //
    // Keeps list of crops and operations sorted so they are easier to find. Also removes the extension before adding to the list.
    //
    private void sortVector(Vector<String> db, String str, String ext) {
        int end = str.lastIndexOf(ext);
        String newstr = str.substring(0, end);
        newstr = newstr.replace('\\', '/');

        boolean added = false;
        for (int i = 0; i < db.size(); i++) {
            if (newstr.compareToIgnoreCase(db.elementAt(i)) < 0) {
                db.insertElementAt(newstr, i);
                added = true;
                break;
            }
        }
        if (added == false) {
            db.addElement(newstr);
        }
    }

    //
    // removeEmptyDirs()
    //
    // Removes empty directories from the list (there should not be any). Also, removes the extension from
    @Deprecated
    @SuppressWarnings({"rawtypes", "unchecked"})
    private void removeEmptyDirs(Vector db, String ext) {
        String s;
        for (int i = 0; i < db.size(); i++) {
            s = (String) db.elementAt(i);
            if (s.endsWith(ext) == false) {
                db.removeElementAt(i);
            } else {
                // remove the ext - it is not dispalyed
                int end = s.lastIndexOf(ext);
                String newstr = s.substring(0, end);
                db.setElementAt(newstr, i);
            }
        }
    }

    //
    // writeWEPSManagementFile()
    //
    // Creates a full WEPS .man file from the skeleton data.
    //
    private boolean writeWEPSManagementFile(String destManFile, Vector<String> mcDates,
            Vector<String> mcOps, Vector<String> mcCrops) {
        ManageData xmlManFile = new ManageData();
        //JOptionPane.showMessageDialog(null,"writeWEPS","Error",JOptionPane.INFORMATION_MESSAGE);
        xmlManFile.kRotationYears = rotationYears;
        xmlManFile.setWepsManFileNotes(skelNotes);
        boolean saveit = true;
        int curRow = 0;
        for (int i = 0; i < mcDates.size(); i++) {
            if (!(mcOps.elementAt(i)).equals("---Delete Operation---")) {
                //JOptionPane.showMessageDialog(null,(String)mcOps.elementAt(i),"Error",JOptionPane.INFORMATION_MESSAGE);
                RowInfo row = new RowInfo();
                String fullOpName = MCREWConfig.getDirectoryName(XMLConstants.soperation) + "/" + mcOps.elementAt(i) + ".oprn";
                Node opNode = loadXMLOperationsFile(fullOpName);
                if (opNode != null) {
                    row.initialize(opNode);
                    OperationObject operation = (OperationObject) row.getDataObject("operation");
                    // Need to switch the month and day for WEPS internal
                    String wdate = mcDates.elementAt(i);
                    StringTokenizer thedate = new StringTokenizer(wdate, "/");
                    String month = thedate.nextToken();
                    String day = thedate.nextToken();
                    String year = thedate.nextToken();
                    try {
                        row.setDate(day + "/" + month + "/" + year);
                    } catch (IllegalStateException ex) {
                        JOptionPane.showMessageDialog(rootPane, "Invalid dates found in file.  Skipping file \n" + destManFile);
                        return false;
                    }
                    // If the operation has a crop then we need to plug in the one the user wants
                    if (operation.hasCrop()) {
                        if (!(mcCrops.elementAt(i)).equals("---Delete Crop---")) {
                            String fullCropName = MCREWConfig.getDirectoryName(XMLConstants.scrop)
                                    + "/" + mcCrops.elementAt(i) + ".crop";
                            Node cropNode = loadXMLCropFile(fullCropName);
                            CropObject crop;
                            if (cropNode == null) {
                                crop = operation.getCrop();
                            } else {
                                crop = new CropObject();
                                crop.initialize(cropNode);
                            }
                            operation.changeCrop(crop);
                        }
                    }
                    xmlManFile.addRow(curRow++, row);
                } else {
                    //System.out.println("Error loading: "+fullOpName);
                    saveit = false;
                }
            }
        }

        // If file is ok then save it to a .man file
        if (saveit) {
            if (xmlManFile.writeDataFile(destManFile, c_mode) == ManageData.kSuccess) {
                return true;
            } else {
                return false;
            }
        }
        return false;
    }

    //
    // loadXMLCropFile()
    //
    // This loads the contents of a crop and returns the XML node.
    //
    /**
     *
     * @param op
     * @return
     */
    public static Node loadXMLCropFile(String op) {

        Document doc;
        NodeList opList;
        Node root;

        // check if the file exists
        TFile f = new TFile(op);
        if (f.exists() == false) {
            JOptionPane.showMessageDialog(null, "Crop not found: " + op, "Error", JOptionPane.INFORMATION_MESSAGE);
            return null;
        }

        doc = XMLDoc.getDocument(op);
        if (doc == null) {
            //System.out.println("XMLDoc: Could not load -> "+op+"\n");
            return null;
        }
        doc.normalize();

        root = doc.getDocumentElement();

        if (root.getOwnerDocument() == null) {
            return null;
        }
        return root;
    }

    //
    // loadXMLOperationsFile()
    //
    // This loads the contents of an operation and returns the XML node.
    //
    /**
     *
     * @param op
     * @return
     */
    public static Node loadXMLOperationsFile(String op) {

        Document doc;
        NodeList opList;
        Node root;

        // check if the file exists
        TFile f = new TFile(op);
        if (f.exists() == false) {
            JOptionPane.showMessageDialog(null, "Operation not found: " + op, "Error", JOptionPane.INFORMATION_MESSAGE);
            return null;
        }

        doc = XMLDoc.getDocument(op);
        if (doc == null) {
            //System.out.println("XMLDoc: Could not load -> "+op+"\n");
            return null;
        }
        doc.normalize();

        return doc;
    }

    //
    // buildDatabaseList()
    //
    // This traverses a directory tree and returns a list of the all the files that match a particular extension. This is used
    // to build a list of all the .crop and .oprn files.
    //
    private void buildDatabaseList(String startDir, String ext, Vector<String> db) {
        String basedir = "";
        int basedirlen = 0;

        OprnCropFilter filter = new OprnCropFilter(ext);

        TFile currentDir = new TFile(startDir);

        if (currentDir.isDirectory() == false) {
            JOptionPane.showMessageDialog(null, "Directory is not valid: " + startDir, "Error", JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        try {
            basedir = currentDir.getCanonicalPath();
            basedirlen = basedir.length() + 1;
        } catch (IOException e) {
        }

        Stack<TFile> alldirs = new Stack<TFile>();
        alldirs.push(currentDir);

        while (!alldirs.empty()) {
            currentDir = alldirs.pop();
            TFile[] fnames = currentDir.listFiles(filter);

            if (fnames != null) {
                for (TFile fname : fnames) {
                    try {
                        String dname = fname.getCanonicalPath().substring(basedirlen);
                        if (!fname.isDirectory()) {
                            sortVector(db, dname, "." + ext);
                        } else {
                            String dname2 = fname.getCanonicalPath();
                            TFile dirname = new TFile(dname2);
                            alldirs.push(dirname);
                        }
                    } catch (Exception e) {
                        //System.out.println("Error: " + e);
                    }
                }
            }
        }

        // This will remove any empty directories and also strip the extension
        //removeEmptyDirs(db,"."+ext);
    }

    //
    // setContent()
    //
    // Initialize the table model with the dates, operations and crops
    //
    void setContent(Vector<String> dates, Vector<String> ops, Vector<String> crops) {

        tmodel.setContent(dates, ops, crops, rotationYears);
    }

    //
    // jbInit()
    //
    // All the initialization to layout the dialog.
    //
    void jbInit() throws Exception {
        tablePanel.setLayout(borderLayout1);
        jtextPane.setText("This management file contains operations or crops which were not found in the databases. "
                + "The cells highlighted in red should be changed. Click on the higlighted cells to select a new entry. "
                + "To delete a row click in one of the first two columns and then right-click and select the delete menu item.");
        tablePanel.setBorder(BorderFactory.createEtchedBorder());
        Dimension mindem = new Dimension(400, 300);
        tablePanel.setMinimumSize(mindem);
        tablePanel.setPreferredSize(new Dimension(785, 400));

        jScroll.getViewport().add(jTable1);
        jScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

        jTable1.setMinimumSize(mindem);
        jScroll.setMinimumSize(mindem);
        tablePanel.setMinimumSize(mindem);

        okButton.setText("OK");
        skipButton.setText("Skip");
//        skipRemainingButton.setText("Skip Remaining");
        cancelAllButton.setText("Cancel");
        replBut.setText("Apply Last Change(All Files)");
        replLocBut.setText("Apply Last Change(All in this file)");

        jtextPane.setBackground(Color.lightGray);
        jtextPane.setEditable(false);
        headerPanel.setLayout(borderLayout2);
        headerPanel.add(progressBar, BorderLayout.NORTH);
        headerPanel.add(jtextPane, BorderLayout.SOUTH);
        tablePanel.add(headerPanel, BorderLayout.NORTH);
        tablePanel.add(jScroll, BorderLayout.CENTER);
        tablePanel.add(buttonPanel, BorderLayout.SOUTH);
        buttonPanel.add(okButton, null);
        buttonPanel.add(skipButton, null);
//        buttonPanel.add(skipRemainingButton, null);
        buttonPanel.add(cancelAllButton, null);
        buttonPanel.add(replBut, null);
        buttonPanel.add(replLocBut, null);
        replBut.setEnabled(false);
        replLocBut.setEnabled(false);

        skipButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent evt) {
                if (JOptionPane.showConfirmDialog(null, "Skip import of this managment file?", "Skip",
                        JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
                    altPicked = false;
                    skipRemainingFlag = false;
                    setVisible(false);
                }
            }
        });
        okButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent evt) {
                // First check if we are to delete or supply a new name
                int verifyFailReason = 0;
                if (tmodel.verify() == true) {
                    altPicked = true;
                    setVisible(false);
                } else {
                    if (verifyFailReason == 1) {
                        JOptionPane.showMessageDialog(null,
                                "There are unknown operations or crops in this management.\n"
                                + "Change the highlighted operations or crops so they can be recognized by WEPS.",
                                "Can't save for WEPS use", JOptionPane.INFORMATION_MESSAGE);
                    }
                }
            }
        });
        replBut.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent evt) {
                String msg;

                if (tmodel.getLastCol() == 2) {
                    msg = "Replace all occurances of operation: \n" + tmodel.getPrevOp()
                            + "\n...with...\n" + tmodel.getNewOp();
                } else {
                    if (tmodel.getPrevCrop() == null || tmodel.getPrevCrop().length() == 0) {
                        //The previous crop is blank.  We don't allow global replace from a blank crop.
                        JOptionPane.showMessageDialog(null,
                                "Global substitutions for a 'blank' crop are not allowed.", "Conversion",
                                JOptionPane.WARNING_MESSAGE);
                        return;
                    }
                    msg = "Replace all occurances of crop: \n" + tmodel.getPrevCrop() + "\n...with...\n" + tmodel.getNewCrop();
                }
                if (JOptionPane.showConfirmDialog(null, msg, "Replace all in every file?",
                        JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
                    // do the real work
                    processReplace(true);
                }
            }
        });
        replLocBut.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent evt) {

                String msg;

                if (tmodel.getLastCol() == 2) {
                    msg = "Replace all occurances of operation: \n" + tmodel.getPrevOp() + "\n...with...\n" + tmodel.getNewOp();
                } else {
                    if (tmodel.getPrevCrop() == null || tmodel.getPrevCrop().length() == 0) {
                        //The previous crop is blank.  We don't allow global replace from a blank crop.
                        JOptionPane.showMessageDialog(null,
                                "Global substitutions for a 'blank' crop are not allowed.", "Conversion",
                                JOptionPane.WARNING_MESSAGE);
                        return;
                    }
                    msg = "Replace all occurances of crop: \n" + tmodel.getPrevCrop() + "\n...with...\n" + tmodel.getNewCrop();
                }
                if (JOptionPane.showConfirmDialog(null, msg, "Replace all in current file?", JOptionPane.YES_NO_OPTION)
                        == JOptionPane.YES_OPTION) {
                    // do the real work
                    processReplace(false);
                }
            }
        });
        cancelAllButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent evt) {
                int result = JOptionPane.showConfirmDialog(null, "Would you like to save the work you have already done?",
                        "Cancel", JOptionPane.YES_NO_CANCEL_OPTION);
                if (result == JOptionPane.YES_OPTION) {
                    altPicked = false;
                    skipRemainingFlag = true;
                    setVisible(false);
                } else if (result == JOptionPane.NO_OPTION) {
                    altPicked = false;
                    cancelAllFlag = true;
                    setVisible(false);
                }
            }
        });

        jTable1.getTableHeader().setBackground(Color.blue);
        jTable1.getTableHeader().setForeground(Color.white);
        jTable1.getTableHeader().setReorderingAllowed(false); // don't allow column dragging
        TableColumnModel tc = jTable1.getColumnModel();
        TableColumn delcol = tc.getColumn(0);
        TableColumn datecol = tc.getColumn(1);
        TableColumn opcol = tc.getColumn(2);
        TableColumn cropcol = tc.getColumn(3);

        delcol.setMinWidth(25);
        delcol.setMaxWidth(55);
        delcol.setPreferredWidth(25);
        datecol.setMinWidth(50);
        datecol.setMaxWidth(100);
        datecol.setPreferredWidth(70);

        opcol.setMinWidth(100);
        opcol.setMaxWidth(1000);
        opcol.setPreferredWidth(350);

        JComboBox<String> cBox = new JComboBox<>(dbOpNames);

        DefaultCellEditor ed = new DefaultCellEditor(cBox);
        ed.setClickCountToStart(1);
        opcol.setCellEditor(ed);

        cropcol.setMinWidth(70);
        cropcol.setMaxWidth(1000);
        cropcol.setWidth(150);
        JComboBox<String> cBoxC = new JComboBox<>(dbCropNames);

        DefaultCellEditor edC = new DefaultCellEditor(cBoxC);
        cropcol.setCellEditor(edC);

        delcol.setCellRenderer(r1);
        datecol.setCellRenderer(r1);
        opcol.setCellRenderer(r1);
        cropcol.setCellRenderer(r1);

        tmodel.setMyRender(r1);

        jTable1.setRowSelectionAllowed(true);
        jTable1.setColumnSelectionAllowed(false);
        jTable1.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        jTable1.setSelectionBackground(Color.yellow);
        jTable1.setCellSelectionEnabled(true);

        buttonPanel.setMinimumSize(new Dimension(400, 400));
        jTable1.setMinimumSize(new Dimension(400, 400));

        getContentPane().add(tablePanel);

        // handle the right-click but only show the menu if the user was in col 1 or 2
        tabMouse = new MouseAdapter() {

            @Override
            public void mouseReleased(MouseEvent e) {
                if (e.isPopupTrigger()) {
                    int col = jTable1.columnAtPoint(e.getPoint());
                    int row = jTable1.rowAtPoint(e.getPoint());
                    if ((col == 0) || (col == 1)) {
                        tablePopup.setRow(row);
                        tablePopup.show(e.getComponent(), e.getX(), e.getY());
                    }
                }
            }
        };

        jTable1.addMouseListener(tabMouse);

        // handle click to close window, telling user that file was not converted
        windAdap = new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                JOptionPane.showMessageDialog(null, "Management file not converted",
                        "Cancel Import", JOptionPane.INFORMATION_MESSAGE);
                altPicked = false;
            }
        };

        addWindowListener(windAdap);

        // This forces a scrollbar at the bottom of the table if the width falls below a minimum. If the window
        // is larger than the minumum then columns resize to take up the window width.
        compAdap = new ComponentAdapter() {

            @Override
            public void componentResized(ComponentEvent e) {
                Dimension width = getSize();
                if (width.width < 400) {
                    jTable1.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
                } else {
                    jTable1.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
                }
                jTable1.invalidate();
                jTable1.repaint();
            }
        };

        addComponentListener(compAdap);
    }

    // loadSkelFile()
    //
    // Loads an NRCS skeleton file and populates the vectors for dates, operations and vegetations
    //
    private boolean loadSkelFile(String filename) {
        // parse the XML file
        Document doc;
        NodeList opList;
        Node root;
        DocumentTraversal traversable;
        TreeWalker walker;

        doc = XMLDoc.getDocument(filename);
        if (doc == null) {
            JOptionPane.showMessageDialog(null, "Could not load skeleton file: \n\n" + filename,
                    "Error", JOptionPane.INFORMATION_MESSAGE);
            return false;
        }
        doc.normalize();

        root = doc.getDocumentElement();
        if (root.getOwnerDocument() == null) {
            traversable = (DocumentTraversal) root;
        } else {
            traversable = (DocumentTraversal) root.getOwnerDocument();
        }

        walker = traversable.createTreeWalker(root, NodeFilter.SHOW_ALL, null, false);
        Node rootNode = walker.getRoot();
        skelProgram = "Unknown";
        skelVersion = "Unknown";
        String tempProgram = ((Element) rootNode).getAttribute("prog");
        String tempVersion = ((Element) rootNode).getAttribute("ver");
        if (tempProgram != null && tempProgram.length() > 0) {
            skelProgram = tempProgram;
        }
        if (tempVersion != null && tempVersion.length() > 0) {
            skelVersion = tempVersion;
        }
        skelNotes = "";
        NodeList notesNodes = doc.getElementsByTagName(XMLConstants.snoteskel);
        if (notesNodes.getLength() == 1) {
            Element node = (Element) notesNodes.item(0);
            skelNotes = XMLDoc.getTextData(node);
            //System.out.println("notes=" + skelNotes + "\n");
        }
        if (c_mode == ManageData.WriteFileMode.FROM_NRCS) {
            skelNotes = skelNotes + "\nOriginating Program: " + skelProgram;
            skelNotes = skelNotes + "\nSkel Format Version: " + skelVersion;
        }

        NodeList rotYearList = doc.getElementsByTagName(XMLConstants.srotationyears);
        if (rotYearList.getLength() != 1) {
            rotYearList = doc.getElementsByTagName("duration");
        }
        if (rotYearList.getLength() == 1) {

            Element node = (Element) rotYearList.item(0);
            rotationYears = (new Integer(XMLDoc.getTextData(node)));
            //System.out.println("rot years="+rotationYears+"\n");
        }

        opList = doc.getElementsByTagName(XMLConstants.sopskel);
        String opname;
        String opdate;
        String vegname;
        opnames = new Vector<>();
        opdates = new Vector<>();
        vegnames = new Vector<>();
        int hasveg = 0;

        for (int i = 0; i < opList.getLength(); i++) {
            //for each op
            // parse the op in xml
            hasveg = 0;
            Element opElement = (Element) opList.item(i);
            NodeList childNodeList = opElement.getChildNodes();
            for (int j = 0; j < childNodeList.getLength(); j++) {
                // for all nodes under the op
                if (childNodeList.item(j) instanceof Element) {
                    Element aChildNode = (Element) childNodeList.item(j);
                    if (aChildNode.getNodeName().equals(XMLConstants.sname)) {
                        // op date node
                        opname = aChildNode.getFirstChild().getNodeValue(); ////////lsb+++trim().....
                        opnames.addElement(opname.trim());
                    } else {
                        // this is a date or veg
                        if (aChildNode.getNodeName().equals(XMLConstants.svegskel)) {
                            // veg
                            //System.out.println("Veg children: "+aChildNode.getChildNodes().getLength());
                            for (int k = 0; k < aChildNode.getChildNodes().getLength(); k++) {

                                Node veg = aChildNode.getChildNodes().item(k);

                                if (veg != null) {

                                    if (veg.getNodeName().equals(XMLConstants.sname)) {
                                        vegname = veg.getFirstChild().getNodeValue();
                                        vegnames.addElement(vegname.trim());
                                        hasveg = 1;
                                    }
                                }
                            }
                        } else {
                            //date
                            opdate = aChildNode.getFirstChild().getNodeValue();
                            opdates.addElement(opdate);
                        }
                    }
                } //for node under the op
            } //for a op
            if (hasveg == 0) {
                vegnames.addElement("0");
            }
        } //for each op
        //opnames.addElement("---Delete Operation---");
        //vegnames.addElement("---Delete Crop---");
        return true;
    }

    //
    // skel2man()
    //
    //
    //
    /**
     * This method is called to start the conversion of the skeleton file to a .MAN
     * i;e management file.
     * @param filename The skeleton file name that needs to be converted.
     * @param manfile The path for the .MAN file where the data will be saved after
     * conversion from skeleton file.
     * @return False if the skeleton file was not loaded successfully into the array
     * else return true.
     */
    public boolean skel2man(String filename, String manfile) {
        altPicked = true;
        r1.removeAllHotspots();

        // step 1: load the xml .skel file and get stuff into arrays
        if (!loadSkelFile(filename)) {

            return false;
        }

        // step 2: remove any entries indicated by rusle2 translation file
        int origSize = opdates.size();
        int j = 0;
        for (int i = 0; i < origSize; i++) {
            if (opdates.get(j) != null) {
                if (globalMapDelete.containsKey(opnames.get(j))) {
                    // needs to be deleted
                    opdates.remove(j);
                    opnames.remove(j);
                    vegnames.remove(j);
                } else if (globalMapDeleteCrop.containsKey(opnames.get(j))) {
                    vegnames.set(j, "0");
                    j++;
                } else if (globalMapDelete.containsKey(vegnames.get(j))) {
                    vegnames.set(j, "0");
                    j++;
                } else {
                    j++;
                }
            }
        }

        // step 3: get the data into the table model
        setContent(opdates, opnames, vegnames);

        // save destination file
        destManFile = manfile;

        setTitle("Operation or crop not found: " + filename);

        return showUnknownOps();
    }

// OprnCropFilter()
    //
    // Generic filter that uses the extension passed to the constructor. This is used to get the .oprn and .crop database files.
    //
    private static class OprnCropFilter implements FilenameFilter {

        String theFilter;

        OprnCropFilter(String filter) {
            theFilter = filter;
        }

        //Accept all directories and all MAN files.
        @Override
        public boolean accept(java.io.File dir, String name) {
            String fullname = "";
            try {
                fullname = dir.getCanonicalPath() + "/" + name;
            } catch (IOException e) {
            }

            TFile f = new TFile(fullname);
            if (f.isDirectory()) {

                return true;
            }

            String ext = null;
            String s = name;
            int i = s.lastIndexOf('.');

            if (i > 0 && i < s.length() - 1) {
                ext = s.substring(i + 1).toLowerCase();
            }
            String extension = ext;
            if (extension != null) {
                return extension.equals(theFilter);
            }

            return false;
        }
    }

    /**
     *
     * @param opStr
     * @return
     */
    public boolean requiresCrop(String opStr) {
        boolean val = false;
        if (opStr.equals("---Delete Operation---")) {
            return false;
        }
        RowInfo row = new RowInfo();
        String fullOpName = MCREWConfig.getDirectoryName(XMLConstants.soperation) + "/" + opStr + ".oprn";
        if (cachedCropCheck(opStr)) {
            return getCachedCropCheck(opStr);
        }
        Node opNode = loadXMLOperationsFile(fullOpName);
        if (opNode != null) {
            row.initialize(opNode);
            OperationObject operation = (OperationObject) row.getDataObject("operation");

            if (operation != null) {
                if (operation.hasCrop()) {
                    return setCachedCropCheck(opStr, true);
                } else {
                    return setCachedCropCheck(opStr, false);
                }
            }
        }

        return false;
    }

    /**
     *
     * @param opStr
     * @return
     */
    public String getDefaultCrop(String opStr) {
        String fullOpName = MCREWConfig.getDirectoryName(XMLConstants.soperation) + "/" + opStr + ".oprn";
        RowInfo row = new RowInfo();
        Node opNode = loadXMLOperationsFile(fullOpName);
        if (opNode != null) {
            row.initialize(opNode);
            OperationObject operation = (OperationObject) row.getDataObject("operation");

            if (operation != null) {
                if (operation.hasCrop()) {
                    if (globalMapOp.containsKey(operation.getCropName())) {
                        return globalMapOp.get(operation.getCropName());
                    } else {
                        String foundName = findWepsCropName(operation.getCropName());
                        return foundName != null ? foundName : operation.getCropName();
                    }
                }
            }
        }
        return "";
    }

    boolean cachedCropCheck(String opStr) {
        if (cachedCropChecker.containsKey(opStr) == false) {
            return false;
        } else {
            return true;
        }
    }

    boolean getCachedCropCheck(String opStr) {
        boolean val;
        String val2 = cachedCropChecker.get(opStr);

        if (val2.equals("T")) {
            val = true;
        } else {
            val = false;

            //JOptionPane.showMessageDialog(null,"Used Cache",opStr,JOptionPane.INFORMATION_MESSAGE);
        }
        return val;
    }

    boolean setCachedCropCheck(String opStr, boolean val) {

        if (cachedCropChecker.containsKey(opStr) == false) {
            if (val == true) {
                cachedCropChecker.put(opStr, "T");
            } else {
                cachedCropChecker.put(opStr, "F");
            }
        }
        return val;
    }

    /**
     *
     * @return
     */
    public boolean hadUnknownCrop() {
        return untranslatedCrops.size() > 0;
    }

    /**
     *
     * @return
     */
    public boolean hadUnknownOperation() {
        return untranslatedOperations.size() > 0;
    }

    /**
     *
     * @return
     */
    public Vector<String> getUnknownCrops() {
        return untranslatedCrops;
    }

    /**
     *
     * @return
     */
    public Vector<String> getUnknownOperations() {
        return untranslatedOperations;
    }
    private ManageData.WriteFileMode c_mode;
};

//--------------------------------------------------------------------------------
// FindReplacePanel()
//
// This dialog handles the replace button click in the main screen.
//
//--------------------------------------------------------------------------------
class FindReplacePanel extends JDialog {

    private static final long serialVersionUID = 1L;

    JPanel tablePanel = new JPanel();
    BorderLayout borderLayout1 = new BorderLayout();
    JPanel jPanelb = new JPanel();
    JButton okButton = new JButton();
    JButton skipButton = new JButton();
    JPanel buttonPanel = new JPanel();
    JLabel unop = new JLabel();
    JComboBox<String> jComboBox2;
    //JComboBox oldName;
    JLabel jLabel1 = new JLabel();
    JLabel jLabel2 = new JLabel();
    JCheckBox subsAll = new JCheckBox("Apply change to all files");
    Vector<String> dbOpNames;
    Vector<String> allNames;
    String unknownOp;
    boolean okhit = false;

    public FindReplacePanel(Frame frame, String title, boolean modal, int focusCol,
            Vector<String> dbOps, Vector<String> dbCrops, String op) {
        super(frame, title, modal);
        try {
            if (focusCol == 2) {
                dbOpNames = Caster.<Vector<String>>cast(dbOps.clone());
                allNames = Caster.<Vector<String>>cast(dbOps.clone());
            } else if (focusCol == 3) {
                dbOpNames = Caster.<Vector<String>>cast(dbCrops.clone());
                allNames = Caster.<Vector<String>>cast(dbCrops.clone());
            }
            unknownOp = op;
            jbInit(focusCol);
            pack();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public FindReplacePanel() {
        this(null, "", false, 0, null, null, "nothing");
    }

    void jbInit(int col) throws Exception {
        tablePanel.setLayout(borderLayout1);
        okButton.setText("OK");
        skipButton.setText("Cancel");
        buttonPanel.setLayout(null);

        subsAll.setSelected(true);

        filterOps(dbOpNames);

        jComboBox2 = new JComboBox<>(dbOpNames);

        // There is a bug in jdk versions before 1.3 that prevents the combobox from dropping down past
        // the edge of the JDialog, the height of the dialog is increased to get around this.
        jComboBox2.setBounds(new Rectangle(0, 116, 450, 26));
        jComboBox2.setPreferredSize(new Dimension(450, 26));
        unop.setBounds(new Rectangle(2, 45, 450, 26));
        if (col == 2) {
            jLabel1.setText("Operation to Find:");
        } else {
            jLabel1.setText("Crop to Find:");
        }
        unop.setText(unknownOp);
        jLabel1.setBounds(new Rectangle(4, 25, 238, 16));
        if (col == 2) {
            jLabel2.setText("Replace with this operation:");
        } else {
            jLabel2.setText("Replace with this crop:");
        }
        jLabel2.setBounds(new Rectangle(3, 95, 174, 17));

        tablePanel.setMinimumSize(new Dimension(350, 270));
        tablePanel.setPreferredSize(new Dimension(475, 320));

        getContentPane().add(tablePanel);

        jPanelb.add(okButton, null);
        jPanelb.add(skipButton, null);

        tablePanel.add(jPanelb, BorderLayout.SOUTH);

        buttonPanel.add(jComboBox2);
        buttonPanel.add(jLabel1, null);
        buttonPanel.add(jLabel2, null);
        buttonPanel.add(unop);

        tablePanel.add(buttonPanel, BorderLayout.CENTER);

        tablePanel.add(subsAll, BorderLayout.NORTH);

        okButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent evt) {
                okhit = true;
                setVisible(false);
            }
        });
        skipButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent evt) {
                setVisible(false);
            }
        });

        unop.setForeground(Color.red);

        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setLocation((screenSize.width - getSize().width) / 2, (screenSize.height - getSize().height) / 2);
        setResizable(false);
    }

    // remove any names that begn with *** so the user can't choose an unknown op
    void filterOps(Vector<String> dbOpNames) {
        Enumeration<String> e;
        String str;
        Vector<String> remNames = new Vector<>();
        for (e = dbOpNames.elements(); e.hasMoreElements();) {
            str = e.nextElement();
            if (str.startsWith("***")) {
                remNames.addElement(str);
            }
        }
        for (e = remNames.elements(); e.hasMoreElements();) {
            str = e.nextElement();
            dbOpNames.removeElement(str);
        }
    }

    public boolean OKHit() {
        return okhit;
    }

    public String getOldString() {
        return unknownOp;
        //return (String)oldNames.getSelectedItem();
    }

    public String getNewString() {
        return (String) jComboBox2.getSelectedItem();
    }

    public boolean isGlobal() {
        return subsAll.isSelected();
    }

    public boolean getReplace() {
        return true;
    }

    @Override
    public final void dispose() {
        long now = System.currentTimeMillis();
        MouseEvent event = new MouseEvent(this, MouseEvent.MOUSE_EXITED, now, 0, 0, 0, 0, false);
        dispatchEvent(event);
        this.setRootPane(null);
        super.dispose();
    }
};
