/*
 * calFactorDialog.java
 *
 * Created on May 24, 2004, 1:56 PM
 */
package usda.weru.weps;

import com.klg.jclass.cell.JCCellEditor;
import com.klg.jclass.cell.JCCellEditorListener;
import com.klg.jclass.cell.JCCellEditorSupport;
import com.klg.jclass.cell.JCCellInfo;
import com.klg.jclass.cell.JCComponentCellRenderer;
import com.klg.jclass.cell.JCKeyModifier;
import com.klg.jclass.table.CellStyleModel;
import com.klg.jclass.table.JCCellRange;
import com.klg.jclass.table.JCTable;
import com.klg.jclass.table.JCTableEnum;
import com.klg.jclass.table.data.JCEditableVectorDataSource;
import com.klg.jclass.table.data.JCVectorDataSource;
import de.schlichtherle.truezip.file.TFile;
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.text.Format;
import java.text.SimpleDateFormat;
import usda.weru.util.*;

import java.util.*;
import usda.weru.mcrew.*;
import javax.swing.*;
import org.openide.util.Exceptions;

/**
 * This GUI helps the user in telling what the calibration factors are like and
 * their details regarding their project name, current run, selected management file,
 * the planting date for various crops and their biomass adjustment fact, etc.
 * @author manmohan
 */
public class CalFactorDialog extends usda.weru.weps.gui.CalFactorDialog_n implements PropertyChangeListener {

    private static final long serialVersionUID = 1L;

    private TFile c_managementFile;
    private final Vector<CalibrationAdjustment> c_adjustments = new Vector<CalibrationAdjustment>();
    private final PropertyChangeSupport changes;
    private TFile currentFile;
    private TFile projectDirectory;
    private JCVectorDataSource ds;
    private JCTable table;
    private final RunFileData dataStore;

    /**  Default constructor that creates the new GUI form as CalibrationFactors
     * @param runprogram
     * @param rfd
     * @param cd */
    public CalFactorDialog(RunProgram runprogram, usda.weru.util.ConfigData cd, RunFileData rfd) {

        //Create table
        createTable();

        changes = new PropertyChangeSupport(this);
        changes.addPropertyChangeListener(runprogram);
        cd.addPropertyChangeListener(this);
        rfd.addPropertyChangeListener(this);
        cd.fireAll(this);
        rfd.fireAll(this);
        dataStore = rfd;
    }

    /**
     *
     * @param b
     */
    @Override
    public void setVisible(boolean b) {
        setVisible(b, true);
    }

    /**
     *
     * @param b
     * @param invokeOnSwing
     */
    public void setVisible(final boolean b, boolean invokeOnSwing) {
        if (invokeOnSwing) {
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    setVisible(b, false);
                }
            });
        } else {
            super.setVisible(b);
        }
    }

    private void createTable() {
        //Table
        table = new JCTable();
        JP_table.add(table, BorderLayout.CENTER);
        table.setTrackCursor(false);

        //Data
        ds = new JCEditableVectorDataSource();
        table.setRowLabelDisplay(false);
        table.setDataSource(ds);

        //Scrolling
        table.setVertSBPosition(JCTableEnum.POSITION_AT_SIDE);
        table.setHorizSBPosition(JCTableEnum.POSITION_AT_SIDE);
        table.setVertSBDisplay(JCTableEnum.SCROLLBAR_AS_NEEDED);
        table.setHorizSBDisplay(JCTableEnum.SCROLLBAR_AS_NEEDED);
        table.setAutoScroll(JCTableEnum.AUTO_SCROLL_BOTH);

        //Resizing
        table.setAllowCellResize(JCTableEnum.RESIZE_COLUMN);
        table.setAllowResizeBy(JCTableEnum.RESIZE_BY_LABELS);

        //Columns
        String labels[] = {"Planting\nDate", "Crop", "Biomass\nAdj.\nFactor", ""};
        ds.setNumColumns(labels.length);
        ds.setColumnLabels(labels);

        table.setPixelWidth(0, 85);
        table.setPixelWidth(1, 200);
        table.setPixelWidth(2, 60);
        table.setPixelWidth(3, 100);

        table.setPixelHeight(-1, 50);

        //Header style
        CellStyleModel labelStyle = table.getDefaultLabelStyle();
        labelStyle.setBackground(new Color(246, 200, 84));
        labelStyle.setCellRenderer(new MyMultilineCellRenderer());

        table.getDefaultCellStyle().setEditable(false);
        CellStyleModel dateStyle = (CellStyleModel) table.getDefaultCellStyle().clone();
        dateStyle.setCellRenderer(new MyDateCellRenderer());
        table.setCellStyle(new JCCellRange(JCTableEnum.ALLCELLS, 0, JCTableEnum.ALLCELLS, 0), dateStyle);

        CellStyleModel saveAsStyle = (CellStyleModel) table.getDefaultCellStyle().clone();
        SaveAsCellRendererEditor button = new SaveAsCellRendererEditor();
        saveAsStyle.setCellRenderer(button);
        saveAsStyle.setCellEditor(button);
        saveAsStyle.setEditable(true);
        table.setCellStyle(new JCCellRange(JCTableEnum.ALLCELLS, 3, JCTableEnum.ALLCELLS, 3), saveAsStyle);

        //table.revalidate();
        //Rows
        ds.setNumRows(0);

    }

    /**
     *
     */
    protected String c_cropDirPath;

    /**
     *
     * @param e
     */
    @Override
    public void propertyChange(PropertyChangeEvent e) {
        switch (e.getPropertyName()) {
            case usda.weru.util.ConfigData.CurrentProj:
                projectDirectory = new TFile(Util.parse((String) e.getNewValue()));
                break;
            case RunFileData.ManageFile:
                currentFile = new TFile((String) e.getNewValue());
                break;
            case usda.weru.util.ConfigData.CropDB:
                c_cropDirPath = e.getNewValue().toString();
                break;
        }
    }

    /**
     * This button is used to close the calibration factor dialog once the
     * user is done accessing the information needed.
     * @param evt The action event that is generated when this "Close" button is hit.
     */
    @Override
    public void JBClose_actionPerformed(java.awt.event.ActionEvent evt) {
        setVisible(false);
        dispose();
    }

    /**
     * This button is used to apply the calibration factor to the management file in the
     * run directory.
     * @param evt The action event that is generated when this "Apply" button is hit.
     */
    @Override
    protected void JB_apply_actionPerformed(java.awt.event.ActionEvent evt) {

        //Is MCREW open?
        if (Mcrew.isOpen()) {
            JOptionPane.showMessageDialog(this, "Unable to set current management file.\n"
                    + "Please close MCREW first.", "Calibration", JOptionPane.WARNING_MESSAGE);
            return;
        }

// We shouldn't be checking the soil UI when we are only attempting a management file save - LEW
        //Is soil open?
//        if (usda.weru.soil.Soil.isOpen()){
//            JOptionPane.showMessageDialog(this, "Unable to set current management file.\nPlease close the soil editor.",
        //"Calibration", JOptionPane.WARNING_MESSAGE);
//            return;
//        }
        //We only want to work with files in the project, not template files.
        TFile fileInProject = new TFile(projectDirectory, currentFile.getName());
//        TFile preBackupInProject = new TFile(fileInProject.getAbsolutePath());
//        TFile postBackupInProject = null;
//
//        //Do we Need to backup the current file?
//        if (preBackupInProject.exists()) {
//            postBackupInProject = Util.incrementFileName(fileInProject, "_backup", ".man");
//            try {
//                preBackupInProject.mv(postBackupInProject);
//
//            } catch (IOException e) {
//                JOptionPane.showMessageDialog(this, "Unable to save backup of the current management file.\n"
//                        + fileInProject.getPath(), "Calibration", JOptionPane.WARNING_MESSAGE);
//                return;
//            }
//        }

        fileInProject = Util.incrementFileName(fileInProject, "_calib", ".man");
        if (Util.copyFile(c_managementFile, fileInProject)) {
            dataStore.setData(RunFileData.ManageFile, fileInProject.getAbsolutePath());
            JOptionPane.showMessageDialog(this, "The current project is now using the calibrated management file.",
                    "Calibration", JOptionPane.INFORMATION_MESSAGE);
            dispose();
        } else {
            JOptionPane.showMessageDialog(this, "Unable to copy the calibrated management file to the project.\n"
                    + c_managementFile.getPath(), "Calibration", JOptionPane.WARNING_MESSAGE);
        }

    }

    /**
     *
     */
    protected Map<CalibrationAdjustment, CropObject> c_cropObjects;

    /**
     *
     */
    public void updateManagementFile() {
        if (c_hasBadValues) {
            return;
        }
        ManageData manData = new ManageData();
        JB_apply.setEnabled(false);
        boolean appliedAdjustments = false;

        /**
         * We can now apply changes directly to our working copy, as it is now the
         * _calib file, with a _orig already existing as a backup.
         */
        Format dateFormat = new SimpleDateFormat("MMM dd, yy");
        String notesHeader = "Crop Calibration Adjusment From [" + JTF_runName.getText() + "] on "
                + dateFormat.format(new Date()) + "\n";
        List<String[]> manTable = new LinkedList<String[]>();
        manTable.add(new String[]{"Date", "Crop", "Target", "Units", "Old", "New"});

        manData.readDataFile(c_managementFile.getPath());
        //Do Magic, loop over each adjustment.  Find the crop record and change the value.

        for (CalibrationAdjustment adjustment : c_adjustments) {
            for (Object o : manData.getRows()) {
                RowInfo row = (RowInfo) o;
                OperationObject operation = (OperationObject) row.getAllDataObjects().get("operation");
                if (operation.hasCrop()) {
                    if (row.getDate().toString().equals(adjustment.date.toString())) {
                        CropObject crop = operation.getCrop();
                        if (crop.getCropName().trim().equals(adjustment.crop)) {
                            //We found the operation
                            adjustment.oldValue = crop.getValue("cbafact", 0);
                            crop.setValue("cbafact", adjustment.value, 0);
                            String targetYield = crop.getValue("tgtyield");
                            String targetYieldUnits = crop.getValue("hyldunits");
                            appliedAdjustments = true;
                            String cropNotes = crop.getParameter("crop_notes").getValue().toString();

                            String[][] cropTable = new String[2][];
                            cropTable[0] = new String[]{"Target", "Units", "Old", "New"};
                            cropTable[1] = new String[]{targetYield, targetYieldUnits, adjustment.oldValue,
                                adjustment.value};

                            cropNotes += "\n" + notesHeader;
                            cropNotes += Util.makePrettyTable(cropTable) + "\n";

                            crop.getParameter("crop_notes").setValue(cropNotes);

                            manTable.add(new String[]{dateFormat.format(adjustment.date.getTime()),
                                adjustment.crop, targetYield, targetYieldUnits, adjustment.oldValue,
                                adjustment.value});

                            if (c_cropObjects == null) {
                                c_cropObjects = new Hashtable<CalibrationAdjustment, CropObject>();
                            }
                            c_cropObjects.put(adjustment, crop);
                        }

                    }
                }
            }
        }

        if (manTable.size() > 1) {
            String notes = notesHeader;
            notes += Util.makePrettyTable(manTable.toArray(new String[manTable.size()][]));
            manData.mwepsmanfilenotes = manData.mwepsmanfilenotes + "\n" + notes + "\n";
        }

        if (appliedAdjustments) {
            manData.writeDataFile(c_managementFile.getPath());
            this.JB_apply.setEnabled(true);

        } else {
            JOptionPane.showMessageDialog(this, "Could not find the crop records in the management file",
                    "Calibration", JOptionPane.WARNING_MESSAGE);
        }

    }
    private boolean c_hasBadValues;

    /**
     *
     * @param date
     * @param crop
     * @param biomassAdjFactor
     */
    public void addCalibrationAdjustment(String date, String crop, String biomassAdjFactor) {
        // now takes two string arguments so we can get a temporary workaround "calibration" issue - LEW

        JulianCalendar d = new JulianCalendar(date.trim(), date.trim());
        CalibrationAdjustment ca = new CalibrationAdjustment(d, crop, biomassAdjFactor);
        if (!ca.valid) {
            c_hasBadValues = true;
            biomassAdjFactor = "ERR";
        }
        c_adjustments.add(ca);

        Vector<Object> values = new Vector<Object>();
        values.add(d);
        values.add(crop);
        values.add(biomassAdjFactor);
        values.add(ca);

        ds.addRow(JCTableEnum.MAXINT, null, values);
        //setTableLooks();
        updateApplyButton();
    }

    /**
     *
     * @param file
     */
    public void setManagementFile(TFile file) {
        c_managementFile = file;
        updateApplyButton();

    }

    private void updateApplyButton() {
        if (!c_hasBadValues && c_managementFile != null && c_adjustments.size() > 0) {
            JB_apply.setEnabled(true);
        } else {
            JB_apply.setEnabled(false);
        }
    }

    static class CalibrationAdjustment {

        public JulianCalendar date;
        public String crop;
        public String value;
        public String oldValue;
        public boolean valid;

        public CalibrationAdjustment(JulianCalendar d, String c, String v) {
            date = d;
            crop = c.trim();
            value = v;

            if (v.startsWith("*****")) {
                value = "ERR";
                valid = false;
            } else {
                valid = true;
            }
        }
    }

    class SaveAsCellRendererEditor extends JButton implements JCComponentCellRenderer, JCCellEditor, ActionListener {

        private static final long serialVersionUID = 1L;

        protected JCCellEditorSupport c_support = new JCCellEditorSupport();
        protected CalibrationAdjustment ca;

        public SaveAsCellRendererEditor() {
            super("Save As");
            addActionListener(this);
        }

        @Override
        public Component getRendererComponent(JCCellInfo cellInfo, Object o, boolean selected) {
            if (o instanceof CalibrationAdjustment) {
                CalibrationAdjustment ca = (CalibrationAdjustment) o;
                setEnabled(ca.valid);
            }

            return this;
        }

        @Override
        public void initialize(AWTEvent ev, JCCellInfo info, Object o) {
            setEnabled(info.isEditable());
            if (isEnabled()) {
                if (o instanceof CalibrationAdjustment) {
                    ca = (CalibrationAdjustment) o;
                    setEnabled(ca.valid);
                }
                doClick(100);
            }
        }

        @Override
        public Component getComponent() {
            return this;
        }

        @Override
        public Object getCellEditorValue() {
            return null;
        }

        @Override
        public boolean stopCellEditing() {
            return true;
        }

        @Override
        public boolean isModified() {
            return false;
        }

        @Override
        public void cancelCellEditing() {
        }

        @Override
        public JCKeyModifier[] getReservedKeys() {
            return null;
        }

        @Override
        public void addCellEditorListener(JCCellEditorListener l) {
            c_support.addCellEditorListener(l);
        }

        @Override
        public void removeCellEditorListener(JCCellEditorListener l) {
            c_support.removeCellEditorListener(l);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            WepsFileChooser wfc = new WepsFileChooser(WepsFileChooser.Filetype.CROP, c_cropDirPath, WepsFileChooser.SAVE);
            wfc.setAccessory(JP_cropNotes);
            TFile cropDir = new TFile(c_cropDirPath);
            TFile newCrop = new TFile(cropDir, ca.crop + "_CALIB");
            newCrop = Util.incrementFileName(newCrop, null, ".crop");
            CropObject crop = c_cropObjects.get(ca);
            JTA_cropNotes.setText(crop.getParameter("crop_notes").getValue().toString());
            wfc.setPersitSelectedFile(false);
            TFile currentDir = new TFile(newCrop.getParentFile().getAbsolutePath() + TFile.separator + "NRCS");
            if(newCrop.getParentFile() != null)
            {
                if(currentDir.exists()) 
                {
                    currentDir = new TFile(currentDir.getAbsolutePath() + TFile.separator + "local");
                    if(!currentDir.exists()) 
                    {
                        currentDir = new TFile(newCrop.getParentFile().getAbsolutePath() + TFile.separator + "local");
                        if(!currentDir.exists()) currentDir = newCrop.getParentFile();
                    }
                }
                else
                {
                    currentDir = new TFile(newCrop.getParentFile().getAbsolutePath() + TFile.separator + "local");
                    if(!currentDir.exists()) currentDir = newCrop.getParentFile();
                }
            }
            wfc.setCurrentDirectory(currentDir);
            wfc.setSelectedFile(newCrop);
            wfc.setPersitSelectedFile(true);
            int result = wfc.showDialog(CalFactorDialog.this);
            if (result == WepsFileChooser.APPROVE_OPTION) {
                crop.getParameter("crop_notes").setValue(JTA_cropNotes.getText());
                newCrop = new TFile(wfc.getSelectedFile());
                crop.writeXMLFile(newCrop.getAbsolutePath());
            }
        }
    }
}
