package usda.weru.weps.reports.query;

import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.TFileReader;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Types;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.ArrayList;
import java.util.Arrays;
import org.apache.log4j.Logger;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.regex.Pattern;
import javax.swing.JOptionPane;
import org.openide.util.Exceptions;
import usda.weru.util.Util;
import static usda.weru.util.diff.DifferFrame.LOGGER;

/**
 *
 * @author joelevin
 */
public class HarvestsResultSet extends WepsResultSet {

    private static final Logger LOGGER = Logger.getLogger(HarvestsResultSet.class);

    private boolean flag = true;

    private boolean checkFlag() {	// check if rain and irrig data are split (true if so)
        return this.flag;
    }

    private void setFlag(boolean flag) {
        this.flag = flag;
    }

    private boolean cropData;

    private boolean check_cropdata() {
        return this.cropData;
    }

    private void set_cropdata(boolean cd) {
        this.cropData = cd;
    }

    private int harvestFlag = 0;

    protected String currentDate = null;

    protected Date startDateX = null;
    protected Date endDateX = null;

    public static final String NAME = "harvests";
    public static final String COLUMN_RUNID = "runid";
    public static final String COLUMN_CYCLENUMBER = "cyclenumber";
    public static final String COLUMN_HARVESTDATE = "harvestdate";
    public static final String COLUMN_PREVIOUSDATA = "previousdate";
    public static final String COLUMN_DURATION = "duration";
    public static final String COLUMN_CROPINDEX = "cropindex";
    public static final String COLUMN_CROP = "crop";
    public static final String COLUMN_DRYYIELD = "dryyield";
    public static final String COLUMN_RESIDUE = "residue";
    public static final String COLUMN_INDEX = "index";
    public static final String COLUMN_YIELD = "yield";
    public static final String COLUMN_YIELDUNITS = "yieldunits";
    public static final String COLUMN_MOISTURE = "moisture";
    public static final String COLUMN_RAIN = "rain";
    public static final String COLUMN_IRRIG = "irrig";
    public static final String COLUMN_INITSWC = "initswc";
    public static final String COLUMN_FINALSWC = "finalswc";
    public static final String COLUMN_DELTASWC = "deltaswc";
    public static final String COLUMN_TRANSP = "transp";
    public static final String COLUMN_UNITS = "units";
    //
    //defines data structures used to build and determine crop intervals
    private final Set_CI_DataStruct CI_DS;
    //define data object for crop (harvest) details
    private final CropIntervalDataStruct cid;
    private int rot_nums;
    private final WepsConnection c_con;
    private boolean c_filled;
    private String runName;

    /**
     *
     * @param con
     * @throws SQLException
     */
    public HarvestsResultSet(WepsConnection con, String name) throws SQLException {

        runName = name;
        c_con = con;
        CI_DS = new Set_CI_DataStruct();
        rot_nums = 0;
        cid = new CropIntervalDataStruct();
        cropData = true;
        rotationCheck = true;
        addColumn(COLUMN_RUNID, Types.INTEGER, 10, 0);
        addColumn(COLUMN_CYCLENUMBER, Types.INTEGER, 10, 0);
        addColumn(COLUMN_HARVESTDATE, Types.DATE, 10, 0);
        //addition of plant data
        addColumn(COLUMN_PREVIOUSDATA, Types.DATE, 10, 0);
        //addition of plant-plant duration
        addColumn(COLUMN_DURATION, Types.INTEGER, 10, 0);
        addColumn(COLUMN_CROPINDEX, Types.INTEGER, 10, 0);
        addColumn(COLUMN_CROP, Types.VARCHAR, 255, 0);
        addColumn(COLUMN_DRYYIELD, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_RESIDUE, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_INDEX, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_YIELD, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_YIELDUNITS, Types.VARCHAR, 255, 0);
        addColumn(COLUMN_MOISTURE, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_RAIN, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_IRRIG, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_INITSWC, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_FINALSWC, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_DELTASWC, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_TRANSP, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_UNITS, Types.BOOLEAN, 10, 3);
    }

    /**
     *
     * @return
     */
    @Override
    public String getName() {
        return NAME;
    }

    /**
     *
     * @return
     */
    protected boolean isUSUnits() {
        return Util.USUnits.equals(c_con.getUnits());
    }

    private boolean rotationCheck;

    private boolean check_rotation() {
        return this.rotationCheck;
    }

    private void set_rotation(boolean rot) {
        this.rotationCheck = rot;
    }

    private final DateFormat harvestDateFormat = new SimpleDateFormat("dd/MM/yyyy");

    //function used to determine/read rotation year
    //function reads from mandate.out
    private void getRotationYears(TFile runDir) throws FileNotFoundException {
        //managefile
        //get rot year, only read once;
        BufferedReader man_f = null;

        String manFileName = "mandate.out";
        TFile manfile = new TFile(runDir, manFileName);
        man_f = new BufferedReader(new TFileReader(manfile));
        String man_line1 = null;
        try {
            man_line1 = getLine(man_f);
        } catch (IOException ex) {
            LOGGER.error("Error reading getLine(man_f).");
        }
        if (man_line1 != null) {
            String[] line1 = man_line1.split(" ", -1);
            rot_nums = Integer.parseInt(line1[0]);
        }
    }

    static boolean lockMessage = true;

    void setLockMessage(boolean bool) {
        lockMessage = bool;
    }

    boolean getLockMessage() {
        return lockMessage;
    }

    static boolean lockMessage2 = true;

    void setLockMessage2(boolean bool) {
        lockMessage2 = bool;
    }

    boolean getLockMessage2() {
        return lockMessage2;
    }

    static boolean lockMessage3 = true;

    void setLockMessage3(boolean bool) {
        lockMessage3 = bool;
    }

    boolean getLockMessage3() {
        return lockMessage3;
    }

    static ArrayList<String> currentRanFiles = new ArrayList<>();
    static ArrayList<String> currentRanFiles2 = new ArrayList<>();
    static ArrayList<String> currentRanFiles3 = new ArrayList<>();

    /**
     *
     * @throws SQLException
     */
    @Override
    public synchronized void fill() throws SQLException {

        if (currentRanFiles.contains(runName)) {
        } else {
            currentRanFiles.add(runName);
            setLockMessage(true);
        }

        if (currentRanFiles2.contains(runName)) {
        } else {
            currentRanFiles2.add(runName);
            setLockMessage2(true);
        }

        if (c_filled) {
            return;
        }

        TFile[] files = c_con.getRunFiles();
        for (int runIndex = 0; runIndex < files.length; runIndex++) {
            run_files(files, runIndex);
        }
        c_filled = true;
    }

    private void run_files(TFile[] files, int runIndex) throws SQLException {
        TFile runDir = files[runIndex];
        //open harvest file
        String harvestFileName = isUSUnits() ? "harvest_en.out" : "harvest_si.out";
        TFile harvestFile = new TFile(runDir, harvestFileName);
        //open hydrobal
        String hydrobalFileName = "hydrobal.out";
        TFile hydrobalFile = new TFile(runDir, hydrobalFileName);
        //open season file
        String seasonFileName = "season.out";
        TFile seasonFile = new TFile(runDir, seasonFileName);

        //Read and get rotation years to calculate date information, only read once;
        if (check_rotation()) {
            try {
                getRotationYears(runDir);
                set_rotation(false);
            } catch (FileNotFoundException ex) {
                Exceptions.printStackTrace(ex);
            }
        }

        if (harvestFile.exists() && hydrobalFile.exists()) {
            BufferedReader harvestFile_br = null;
            BufferedReader hydrobalFile_br = null;
            BufferedReader seasonFile_br = null;
            try {

                harvestFile_br = new BufferedReader(new TFileReader(harvestFile));
                hydrobalFile_br = new BufferedReader(new TFileReader(hydrobalFile));
                seasonFile_br = new BufferedReader(new TFileReader(seasonFile));
                int cycleNumber = 0;
                String harvest_f;
                String hydrobal_f;
                String season_f;
                while (((harvest_f = getLine(harvestFile_br)) != null) && ((hydrobal_f = getLine(hydrobalFile_br)) != null) && ((season_f = getLine(seasonFile_br)) != null)) {
                    process_files(runIndex, cycleNumber, harvest_f, hydrobal_f, season_f);
                }

            } catch (IOException ioe) {
                LOGGER.error("Error reading harvest file or hydrobal.out file: " + harvestFile.getAbsolutePath()
                        + hydrobalFile.getAbsolutePath(), ioe);
            } finally {
                if (harvestFile_br != null) {
                    try {
                        harvestFile_br.close();
                    } catch (IOException e) {
                        LOGGER.error("Error closing harvest file: " + harvestFile.getAbsolutePath(), e);
                    }
                }
                if (hydrobalFile_br != null) {
                    try {
                        hydrobalFile_br.close();
                    } catch (IOException e) {
                        LOGGER.error("Error closing hydrobal.out file: " + hydrobalFile.getAbsolutePath(), e);
                    }
                }
                if (seasonFile_br != null) {
                    try {
                        seasonFile_br.close();
                    } catch (IOException e) {
                        LOGGER.error("Error closing season.out file: " + seasonFile.getAbsolutePath(), e);
                    }
                }
            }
        }
    }

    public void process_files(int runIndex, int cycleNumber, String harvest_f, String hydrobal_f, String season_f) throws SQLException {
        //reset currentDate in order to exectute next iteration
        currentDate = null;
        //System.out.println("reset current: " + currentDate);
        //each line is a new cycle
        cycleNumber++;
        //harvest_xx.out file
        String[] harvest_parts = harvest_f.split("\\|", -1);
        // hydrobal.out file
        String[] hydrobal_parts = hydrobal_f.split("\\|", -1);
        // season.out file
        String[] season_parts = season_f.split("\\|", -1);
        //if irrigation not contained in hydrobal older output; exit and inform user.
        String[] rainIrrigSplit = hydrobal_parts[10].split("\\,", -1);
        if (!rainIrrigSplit[1].equals("irrigation")) {
            setOlderRunCheck(true);
            if (getLockMessage()) {
                if (checkFlag()) {
                    setFlag(false);
                    JOptionPane.showMessageDialog(null, "Older output data incompatible with new Crop Reports.\n"
                            + "Older Crop Reports will be Generated.\nIrrigation is included in Rain column for total precipitation + irrigation."
                            + "\nDuration Not Avaiable, Always Reported as 0 days.", "Crop Report", JOptionPane.WARNING_MESSAGE);
                    setLockMessage(false);
                    return;
                }
                setLockMessage(false);
            }
            //return CoverCropResultSet();
            return;
        }
        //determine crop data for Crop Reports (Determine index once)
        //...no need to re calculate same data N-times
        if (check_cropdata()) {
            //System.out.println("Setting crop data...");
            try {
                //see function implementation for detail...
                getHarvests(cid, harvest_f, hydrobal_parts, season_parts, rot_nums);
                setCropData(hydrobal_parts, harvest_parts);

            } catch (Exception e) {
                if (getLockMessage2()) {
                    JOptionPane.showMessageDialog(null, "Error Generating Crop Data for Crop Report(s)", "Crop Report(s)", JOptionPane.WARNING_MESSAGE);
                    setLockMessage2(false);
                }
                LOGGER.error(e.getMessage(), e);
                return;
            }
            set_cropdata(false);
        }

        for (int cropIndex = 0; cropIndex < cid.hydroHarvestsIndSize(); cropIndex++) {
            setHarvests(runIndex, cycleNumber, cropIndex, harvest_parts, hydrobal_parts);
        }
    }

    public void setHarvests(int runIndex, int cycleNumber, int cropIndex, String[] harvest_parts, String[] hydrobal_parts) throws SQLException {
        Object[] row = createNewRow(true);
        setRowValue(row, COLUMN_RUNID, runIndex);
        setRowValue(row, COLUMN_CYCLENUMBER, cycleNumber);
        setRowValue(row, COLUMN_CROPINDEX, cropIndex);
        setRowValue(row, COLUMN_UNITS, this.isUSUnits());
        String dateString = null;
        //set row data for harvest informtion for crop report
        setHarvestSpecificData(row, harvest_parts, cropIndex, dateString);
        //set row data for hydrobal information for crop report
        setHydrobalSpecificData(row, hydrobal_parts, cropIndex);
    }

    public void getHarvests(CropIntervalDataStruct ci, String harvest_l, String[] hydrobal_line_parts, String[] season_line_parts, int rot_nums) {
        //determine harvest dates for crop intervals cover crops
        boolean flagAdded = false;
        String[] harvest_line_parts = harvest_l.split("\\|", -1);
        for (int i = 0; i < harvest_line_parts.length; i++) {
            if (isValidDate(harvest_line_parts[i])) {
                if (harvest_line_parts[i + 1].trim().equals("1")) {
                    harvestFlag = 1;
                    flagAdded = false;
                }
                if (harvest_line_parts[i + 1].trim().equals("0")) {
                    harvestFlag = 1;
                    flagAdded = true;
                }
                if (!flagAdded) {
                    ci.addHarvestDate(harvest_line_parts[i].trim());
                    ci.addHarvestDateInd(i);
                }
            }
        }
        //break down each line in hydrobal
        //each index in the arraylist "data" contains an array
        //each array contains a "pipe" delimited set
        //each index in an array contains the value between 2 pipes broken down in individual pieces
        String[] first_delim = null;
        ArrayList<String[]> data = new ArrayList<>();
        //each piece helps find where an operation is
        for (String hydrobal_line_part : hydrobal_line_parts) {
            first_delim = hydrobal_line_part.split("\\W+", -1);
            data.add(first_delim);
        }
    }

    private void setCropData(String[] hydrobal_parts, String[] harvest_parts) {
        String[] first_delim = null;
        ArrayList<String[]> data = new ArrayList<>();
        //each piece helps find where an operation is
        for (String hydrobal_line_part : hydrobal_parts) {
            first_delim = hydrobal_line_part.split("\\W+", -1);
            data.add(first_delim);
        }
        ArrayList<String> plantOps = new ArrayList<>();
        plantOps.add("Drill");
        plantOps.add("Plant");
        //build vector of indices of harvest corresponding to location in hydrobal 
        //this will allow us to determine the harvest interval and data
        //vector contains index(s) of harvest corresponding to hydrobal
        for (int i = 0; i < hydrobal_parts.length; i++) {
            if (CI_DS.isValidDate(hydrobal_parts[i])) {
                if (cid.harvest_contains(hydrobal_parts[i].trim()) && !plantOps.contains(data.get(i + 1)[0])) {
                    cid.addHydroHarvestDateInd(i);
                    cid.addHydroHarvestDate(hydrobal_parts[i].trim());
                }
            }
        }

        if (cid.hydroHarvestsIndSize() != cid.harvestsIndSize()) {
            //find duplicate date in harvestsInd
            for (int h = 0; h < cid.harvestsIndSize() - 1; h++) {
                //if true, duplicate date found
                //if true, add match for duplicate date in hydrobal
                //System.out.println("checking...");
                if (cid.getHarvestDate(h).trim().equals(cid.getHarvestDate(h + 1).trim())) {
                    String ddate = cid.getHarvestDate(h).trim();
                    //System.out.println("double harvest operation... inserting " + ddate);
                    for (int n = 0; n < cid.hydroHarvestsIndSize(); n++) {
                        if (cid.getHydroHarvestDate(n).trim().equals(ddate)) {
                            //System.out.println("date " + cid.getHydroHarvestDate(n) + " matched with " + ddate);
                            String dupDate = cid.getHydroHarvestDate(n).trim();
                            int dupInd = cid.getHydroHarvestDateInd(n);
                            //insert into n+1...
                            cid.insert_hydroHarvest(n + 1, dupDate);
                            cid.insert_hydroHarvestInd(n + 1, dupInd);
                            break;
                        }
                    }
                }
            }
        }
        //when setting pre harvest data, if 2 harvest dates are equal, 
        //set harvest 1 as the plant for harvest 2 
        setPreHarvests(hydrobal_parts);
    }

    public void setPreHarvests(String[] hydrobal_parts) {
        //determine per harvest data
        //this is a list of the dates corresponding to the harvests
        preHarvest(hydrobal_parts, 0);
        for (int i = 1; i < cid.hydroHarvestsIndSize(); i++) {
            if (cid.getHydroHarvestDateInd(i - 1) == cid.getHydroHarvestDateInd(i)) {
                cid.addpre_harvest(cid.getHydroHarvestDate(i).trim());
                cid.addpre_harvestInd(cid.getHydroHarvestDateInd(i));
            } else {
                preHarvest(hydrobal_parts, i);
            }
        }
    }

    private void preHarvest(String[] hydrobal_parts, int i) {
        if (cid.getHydroHarvestDateInd(i) == 0) {
            for (int k = hydrobal_parts.length - 1; k > 0; k--) {
                if (CI_DS.isValidDate(hydrobal_parts[k].trim())) {
                    cid.addpre_harvest(hydrobal_parts[k].trim());
                    cid.addpre_harvestInd(k);
                    break;
                }
            }
        } else {
            for (int j = cid.getHydroHarvestDateInd(i) - 1; j >= 0; j--) {
                if (CI_DS.isValidDate(hydrobal_parts[j].trim())) {
                    cid.addpre_harvest(hydrobal_parts[j].trim());
                    cid.addpre_harvestInd(j);
                    break;
                }
            }
        }
    }

    //Skip comments and blank lines
    private String getLine(BufferedReader in) throws IOException {
        String temp;
        while ((temp = in.readLine()) != null) {
            temp = temp.trim();
            if (temp.length() == 0) {
                continue;
            }
            if (temp.charAt(0) != '#') {
                return temp;
            }
        }
        return null;
    }

    /**
     *
     * @param date
     * @return
     */
    public static int convertToJulian365(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int year = calendar.get(Calendar.YEAR);

        double julian = Math.floor(year * 365) + get365(date);
        return (int) julian;
    }

    public static int get365(Date currDate) {

        GregorianCalendar start = new GregorianCalendar();
        Calendar cal = Calendar.getInstance();
        cal.setTime(currDate);

        int day = cal.get(Calendar.DAY_OF_MONTH);
        start.set(GregorianCalendar.DAY_OF_MONTH, day);
        int month = cal.get(Calendar.MONTH);
        start.set(GregorianCalendar.MONTH, month);
        start.set(GregorianCalendar.YEAR, 1901);
        int numDays = start.get(GregorianCalendar.DAY_OF_YEAR);
        //System.out.println("***" + numDays);
        return numDays;
    }

    public static int getYear(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int year = calendar.get(Calendar.YEAR);
        return year;
    }

    public static boolean isValidDate(String inDate) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");

        dateFormat.setLenient(false);
        try {
            dateFormat.parse(inDate.trim());
        } catch (ParseException pe) {
            return false;
        }
        return true;
    }

    private void setHarvestSpecificData(Object[] row, String[] harvest_parts, int cropIndex, String dateString) throws SQLException {
        dateString = cid.getHydroHarvestDate(cropIndex);
        Date harvestDate = null;
        try {
            harvestDate = harvestDateFormat.parse(dateString);
        } catch (ParseException ex) {
            Exceptions.printStackTrace(ex);
        }
        endDateX = harvestDate;
        setRowValue(row, COLUMN_HARVESTDATE, new java.sql.Date(harvestDate.getTime()));
        currentDate = cid.getpre_harvest(cropIndex);
        Date harvestDate2 = null;
        try {
            harvestDate2 = harvestDateFormat.parse(currentDate);
        } catch (ParseException ex) {
            Exceptions.printStackTrace(ex);
        }
        startDateX = harvestDate2;
        setRowValue(row, COLUMN_PREVIOUSDATA, new java.sql.Date(harvestDate2.getTime()));
        try {
            int sday = convertToJulian365(startDateX);
            int eday = convertToJulian365(endDateX);
            // When the startDate is greater than the endDate, we use rotation number for the calibtation
            if (sday > eday) {
                int num_rotation_years = getYear(startDateX);
                int dur = ((num_rotation_years * 365) - sday + eday);
                setRowValue(row, COLUMN_DURATION, dur);
            } else {
                int dur = (eday - sday);
                setRowValue(row, COLUMN_DURATION, dur);
            }
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing harvest date.", nfe);
        }
        try {
            String cropName = harvest_parts[cid.getHarvestDateInd(cropIndex) + 1 + harvestFlag].trim();
            setRowValue(row, COLUMN_CROP, cropName);
        } catch (SQLException e) {
            LOGGER.error("Error reading crop name.", e);
        }
        //dry yield
        try {
            Double dryYield = parse(harvest_parts[cid.getHarvestDateInd(cropIndex) + 2 + harvestFlag].trim());
            setRowValue(row, COLUMN_DRYYIELD, dryYield);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing dry yield.", nfe);
        }
        //residue
        try {
            Double residue = parse(harvest_parts[cid.getHarvestDateInd(cropIndex) + 4 + harvestFlag].trim());
            setRowValue(row, COLUMN_RESIDUE, residue);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing residue.", nfe);
        }
        //harvest index
        try {
            Double harvestIndex = parse(harvest_parts[cid.getHarvestDateInd(cropIndex) + 6 + harvestFlag].trim());
            setRowValue(row, COLUMN_INDEX, harvestIndex);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing harvest index.", nfe);
        }
        //yield
        try {
            Double yield = parse(harvest_parts[cid.getHarvestDateInd(cropIndex) + 8 + harvestFlag].trim());
            setRowValue(row, COLUMN_YIELD, yield);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing harvest yield.", nfe);
        }
        //yield units
        try {
            String yieldUnits = harvest_parts[cid.getHarvestDateInd(cropIndex) + 9 + harvestFlag].trim();
            setRowValue(row, COLUMN_YIELDUNITS, yieldUnits);
        } catch (SQLException e) {
            LOGGER.error("Error reading yield units.", e);
        }
        //% water
        try {
            Double moisture = parse(harvest_parts[cid.getHarvestDateInd(cropIndex) + 10 + harvestFlag].trim());
            setRowValue(row, COLUMN_MOISTURE, moisture);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing % moisture.", nfe);
        }

    }

    private void setHydrobalSpecificData(Object[] row, String[] hydrobal_parts, int cropIndex) throws SQLException {
        // Rain (and Irrig)
        try {
            Double rain = parse(hydrobal_parts[cid.getHydroHarvestDateInd(cropIndex) + 11]);
            rain = isUSUnits() ? rain * 0.03937 : rain;
            setRowValue(row, COLUMN_RAIN, rain);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing rain.", nfe);
        }
        try {
            Double irrig = parse(hydrobal_parts[cid.getHydroHarvestDateInd(cropIndex) + 12]);
            irrig = isUSUnits() ? irrig * 0.03937 : irrig;
            setRowValue(row, COLUMN_IRRIG, irrig);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing irrig.", nfe);
        }

        // Initial SWC
        try {
            Double initswc = parse(hydrobal_parts[cid.getHydroHarvestDateInd(cropIndex) + 4]);
            initswc = isUSUnits() ? initswc * 0.03937 : initswc;
            setRowValue(row, COLUMN_INITSWC, initswc);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing Initial SWC.", nfe);
        }
        // Final SWC
        try {
            Double finalswc = parse(hydrobal_parts[cid.getHydroHarvestDateInd(cropIndex) + 8]);
            finalswc = isUSUnits() ? finalswc * 0.03937 : finalswc;
            setRowValue(row, COLUMN_FINALSWC, finalswc);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing Final SWC.", nfe);
        }
        try {
            Double deltaswc = parse(hydrobal_parts[cid.getHydroHarvestDateInd(cropIndex) + 8])
                    - parse(hydrobal_parts[cid.getHydroHarvestDateInd(cropIndex) + 4]);
            deltaswc = isUSUnits() ? deltaswc * 0.03937 : deltaswc;
            setRowValue(row, COLUMN_DELTASWC, deltaswc);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing Delta SWC.", nfe);
        }
        // Transpiration
        try {
            Double transp;
            if (checkFlag()) {
                transp = parse(hydrobal_parts[cid.getHydroHarvestDateInd(cropIndex) + 15]);
            } else {
                transp = parse(hydrobal_parts[cid.getHydroHarvestDateInd(cropIndex) + 14]);
            }
            transp = isUSUnits() ? transp * 0.03937 : transp;
            //System.out.println("transp: " +transp); // Alex's debug line
            setRowValue(row, COLUMN_TRANSP, transp);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing Transpiration.", nfe);
        }
    }

    private double parse(String s) {

        if (currentRanFiles3.contains(runName)) {
        } else {
            currentRanFiles3.add(runName);
            setLockMessage3(true);
        }

        try {
            return Double.valueOf(s);
        } catch (NumberFormatException nfe) {
            if (getLockMessage3()) {
                JOptionPane.showMessageDialog(null, "Error Processing Data Values for Crop Rerpot(s)." + "\nBad Value Found.", "Crop Report(s)", JOptionPane.WARNING_MESSAGE);
                setLockMessage3(false);
            }
            LOGGER.error("Error parsing value: " + s, nfe);
            return Double.NaN;
        }
    }
}
