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.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import javax.swing.JOptionPane;
import org.apache.log4j.Logger;
import org.openide.util.Exceptions;
import usda.weru.util.Util;

/**
 *
 * @author alexander.blum
 */
public class CropIntervalResultSet extends WepsResultSet {

    private static final Logger LOGGER = Logger.getLogger(HarvestsResultSet.class);

    private boolean rotationCheck;

    private boolean check_rotation() {
        return this.rotationCheck;
    }

    private void set_rotation(boolean rot) {
        this.rotationCheck = rot;
    }

    private boolean cropIntervalDataCheck;

    private boolean check_cropIntervalData() {
        return this.cropIntervalDataCheck;
    }

    private void set_CropIntervalData(boolean ciData) {
        this.cropIntervalDataCheck = ciData;
    }

    private boolean emptyCropInterval;

    private boolean check_emptyCI() {
        return this.emptyCropInterval;
    }

    private void set_emptyCI(boolean ciData) {
        this.emptyCropInterval = ciData;
    }

    protected String currentDate = null;
    protected Date startDateX = null;
    protected Date endDateX = null;
    //hydrobal data
    public static final String NAME = "cropinterval";
    public static final String COLUMN_CROP_CI = "crop";
    public static final String COLUMN_RUNID_CI = "runid";
    public static final String COLUMN_CYCLENUMBER_CI = "cyclenumber";
    public static final String COLUMN_TERMINATEDATE_CI = "terminatedate";
    public static final String COLUMN_PREVIOUSDATE_CI = "previousdate";
    public static final String COLUMN_DURATION_CI = "duration";
    public static final String COLUMN_CROPINDEX_CI = "cropindex";
    public static final String COLUMN_MOISTURE_CI = "moisture";
    public static final String COLUMN_RAIN_CI = "rain";
    public static final String COLUMN_IRRIG_CI = "irrig";
    public static final String COLUMN_INITSWC_CI = "initswc";
    public static final String COLUMN_FINALSWC_CI = "finalswc";
    public static final String COLUMN_DELTASWC_CI = "deltaswc";
    public static final String COLUMN_TRANSP_CI = "transp";
    public static final String COLUMN_EVAP_CI = "evap";
    public static final String COLUMN_RUNOFF_CI = "runoff";
    public static final String COLUMN_DRAIN_CI = "drain";
    public static final String COLUMN_EVAP_LOSS_CI = "evaploss";
    public static final String COLUMN_RUNOFF_LOSS_CI = "runoffloss";
    public static final String COLUMN_DRAIN_LOSS_CI = "drainloss";
    public static final String COLUMN_WUE_CI = "wue";
    public static final String COLUMN_FALLOWEFF_CI = "fallow";
    public static final String COLUMN_WATERSTORAGE_CI = "water_storage";
    public static final String COLUMN_CROP_PERIOD_CI = "period";
    public static final String COLUMN_BIOMASS_CI = "biomass";
    //Season.out data field
    public static final String COLUMN_STANDING_STEM_CI = "standing_stem";
    public static final String COLUMN_STANDING_LEAF_CI = "standing_leaf";
    public static final String COLUMN_AVG_HEIGHT_CI = "avg_height";
    public static final String COLUMN_STEM_COUNT_CI = "stem_count";
    //condition data
    public static final String COLUMN_INT_CI = "crop_int";
    public static final String COLUMN_UNITS_CI = "units";
    public static final String COLUMN_KEY_CI = "key";
    public static final String LINE_NUMBER_CI = "line_number";
    private final WepsConnection c_con;
    private boolean c_filled;
    //defines data structures used to build and determine crop intervals
    private final Set_CI_DataStruct CI_DS;
    //defines data members used to calculate and store values for crop intervals
    private final Set_CI_DataMembers CI_DM;
    //defines and determines where in season.out needed information is located
    private final DateSetandCalc DSC;
    private CropIntervalDataStruct ci;
    //used to determine rotaiton years (mandate.out)
    private int rot_nums;
    private int current_line;
    private boolean ran = true;
    private final DateFormat terminateDateFormat = new SimpleDateFormat("dd/MM/yyyy");

    public boolean line_check() {
        return current_line == 0;
    }

    private final String runName;

    public CropIntervalResultSet(WepsConnection con, String name) throws SQLException {
        runName = name;
        //Calculation classes 
        CI_DS = new Set_CI_DataStruct();
        CI_DM = new Set_CI_DataMembers(name);
        DSC = new DateSetandCalc();
        ci = new CropIntervalDataStruct();
        c_con = con;
        rot_nums = 0;
        current_line = 0;
        rotationCheck = true;
        cropIntervalDataCheck = true;
        emptyCropInterval = true;
        //hydrobal.out data
        addColumn(COLUMN_TERMINATEDATE_CI, Types.DATE, 10, 0);
        addColumn(COLUMN_PREVIOUSDATE_CI, Types.DATE, 10, 0);
        addColumn(COLUMN_DURATION_CI, Types.INTEGER, 10, 0);

        addColumn(COLUMN_CROPINDEX_CI, Types.INTEGER, 10, 0);
        addColumn(COLUMN_RUNID_CI, Types.INTEGER, 10, 0);
        addColumn(COLUMN_CYCLENUMBER_CI, Types.INTEGER, 10, 0);

        addColumn(COLUMN_RAIN_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_IRRIG_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_INITSWC_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_FINALSWC_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_DELTASWC_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_TRANSP_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_EVAP_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_RUNOFF_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_DRAIN_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_EVAP_LOSS_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_RUNOFF_LOSS_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_DRAIN_LOSS_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_WUE_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_FALLOWEFF_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_WATERSTORAGE_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_CROP_PERIOD_CI, Types.VARCHAR, 255, 0);
        addColumn(COLUMN_BIOMASS_CI, Types.DOUBLE, 10, 3);

        //season.out
        addColumn(COLUMN_CROP_CI, Types.VARCHAR, 255, 0);
        addColumn(COLUMN_STANDING_STEM_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_STANDING_LEAF_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_AVG_HEIGHT_CI, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_STEM_COUNT_CI, Types.DOUBLE, 10, 3);

        //booleans
        addColumn(COLUMN_INT_CI, Types.BOOLEAN, 10, 3);
        addColumn(COLUMN_UNITS_CI, Types.BOOLEAN, 10, 3);
        addColumn(LINE_NUMBER_CI, Types.BOOLEAN, 10, 3);
        //interval indication
        addColumn(COLUMN_KEY_CI, Types.VARCHAR, 255, 0);
    }

    @Override
    public String getName() {
        return NAME;
    }

    protected boolean isUSUnits() {
        return Util.USUnits.equals(c_con.getUnits());
    }

    //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 ArrayList<String> currentRanFiles = new ArrayList<>();
    static ArrayList<String> currentRanFiles2 = new ArrayList<>();

    @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++) {
            try {
                TFile runDir = files[runIndex];
                //Season File
                String seasonFileName = "season.out";
                TFile seasonFile = new TFile(runDir, seasonFileName);
                //Hydobal File
                String hydrobalFileName = "hydrobal.out";
                TFile hydrobalFile = new TFile(runDir, hydrobalFileName);
                //Harvest file
                String harvestFileName = isUSUnits() ? "harvest_en.out" : "harvest_si.out";
                TFile harvestFile = new TFile(runDir, harvestFileName);
                //Read and get rotation years to calculate date information, only read once;
                if (check_rotation()) {
                    getRotationYears(runDir);
                    set_rotation(false);
                }

                if (seasonFile.exists() && hydrobalFile.exists() && harvestFile.exists()) {
                    //Reads text from a character-input stream, 
                    //buffering characters so as to provide for the efficient reading of characters, arrays, and lines. 
                    BufferedReader season_f = null;
                    BufferedReader hydobal_f = null;
                    BufferedReader harvest_f = null;

                    try {
                        season_f = new BufferedReader(new TFileReader(seasonFile));
                        hydobal_f = new BufferedReader(new TFileReader(hydrobalFile));
                        harvest_f = new BufferedReader(new TFileReader(harvestFile));
                        int cycleNumber = 0;
                        String season_l;
                        String hydro_l;
                        String harvest_l;
                        //read each line of files hydrobal, season, harvest into corresponding BufferedReader for processing
                        while (((season_l = getLine(season_f)) != null) && ((hydro_l = getLine(hydobal_f)) != null) && ((harvest_l = getLine(harvest_f)) != null)) {
                            currentDate = null;
                            //each line is a new cycle
                            cycleNumber++;
                            // season.out file
                            String[] season_line_parts = season_l.split("\\|", -1);
                            // hydrobal.out file
                            String[] hydrobal_line_parts = hydro_l.split("\\|", -1);
                            //if rain irrig not split old file, exit
                            String[] rainIrrigSplit = hydrobal_line_parts[10].split("\\,", -1);
                            if (!rainIrrigSplit[1].equals("irrigation")) {
                                if (getLockMessage()) {
                                    if (ran) {
                                        ran = false;
                                        JOptionPane.showMessageDialog(null, "Older output data incompatible with Crop Interval Reports.\n"
                                                + "Unable to load Crop Interval Data.", "Crop Interval Report", JOptionPane.WARNING_MESSAGE);
                                        setLockMessage(false);
                                        return;
                                    }
                                    setLockMessage(false);
                                }
                                return;
                            }

                            if (check_cropIntervalData()) {
                                try {
                                    //see function implementation for detail...
                                    CI_DS.getCropIntervals(ci, harvest_l, hydrobal_line_parts, season_line_parts, rot_nums);
                                } catch (Exception e) {
                                    if (getLockMessage2()) {
                                        JOptionPane.showMessageDialog(null, "Error Generating Crop Interval Report", "Crop Interval Report", JOptionPane.WARNING_MESSAGE);
                                        LOGGER.error(e);
                                        setLockMessage2(false);
                                        return;
                                    }
                                }
                                this.set_CropIntervalData(false);
                            }
                            int row_id = 0;
                            //build reports based on plant operations count 
                            for (int cc_size = 0; cc_size < ci.operationCount(); cc_size++) {
                                //if planting date is equal to harvest date do no report...
                                if (!ci.getPlantOpDates(cc_size).trim().equals(ci.getPostOpDate(cc_size).trim())) {
                                    try {
                                        //function used to process data captured by call to getCropIntervals
                                        //this is the main driver function to build and process data for Crop Interval reports...
                                        row_id = buildIntervals(cc_size, cycleNumber, runIndex, row_id, season_line_parts, hydrobal_line_parts);
                                    } catch (ParseException ex) {
                                        Exceptions.printStackTrace(ex);
                                    }
                                }
                            }
                            current_line++;
                        }
                    } catch (IOException ioe) {
                        LOGGER.error("Error reading season.out file or hydrobal.out file: " + seasonFile.getAbsolutePath()
                                + hydrobalFile.getAbsolutePath());
                    } finally {
                        if (season_f != null) {
                            try {
                                season_f.close();
                            } catch (IOException e) {
                                LOGGER.error("Error closing season.out file: " + seasonFile.getAbsolutePath());
                            }
                        }
                        if (hydobal_f != null) {
                            try {
                                hydobal_f.close();
                            } catch (IOException e) {
                                LOGGER.error("Error closing hydrobal.out file: " + hydrobalFile.getAbsolutePath());
                            }
                        }
                        if (harvest_f != null) {
                            try {
                                harvest_f.close();
                            } catch (IOException e) {
                                LOGGER.error("Error closing hydrobal.out file: " + harvestFile.getAbsolutePath());
                            }
                        }
                    }
                }
            } catch (FileNotFoundException ex) {
                LOGGER.error("Error in method .Fill() of CropIntervalResultSet.java.", ex);
            }
        }
        c_filled = true;
    }

    private int buildIntervals(int cc_size, int cycleNumber, int runIndex, int row_id, String[] season_line_parts, String[] hydrobal_line_parts) throws SQLException, ParseException {

        Object[] row = createNewRow(true);
        setRowValue(row, LINE_NUMBER_CI, line_check());
        setRowValue(row, COLUMN_RUNID_CI, runIndex);
        setRowValue(row, COLUMN_CYCLENUMBER_CI, cycleNumber);
        setRowValue(row, COLUMN_CROPINDEX_CI, row_id);
        row_id++;
        setRowValue(row, COLUMN_INT_CI, true);
        setRowValue(row, COLUMN_UNITS_CI, this.isUSUnits());
        setRowValue(row, COLUMN_KEY_CI, "CP_N");
        int s_out = 0;

        String cropName = null;
        CropIntervalDataMembers ci_interval = new CropIntervalDataMembers();
        //get season data corresponding to multi harvest crop
        //check if current operation is multi harvest 
        if (ci.getPostPlantSets().get(cc_size).size() > 1) {

            boolean setDateCheck = true;
            boolean setNameCheck = true;
            //look thru current post plant set to determine/calculate values for biomass data on multi harvest
            for (int i = 0; i < ci.getPostPlantSets().get(cc_size).size(); i++) {
                //current "post plant" harvest date 
                String harvest_date = ci.getPostPlantSets().get(cc_size).get(i);
                //get index corresponding to match between "post plant" harvest informatin and date 
                //corresponding to season.out
                s_out = DSC.getDateRange(terminateDateFormat, season_line_parts, harvest_date);
                if (setDateCheck) {
                    //interval multi harvest start date 
                    String startDate = ci.getPreOpDates(cc_size);
                    //interval multi harvest end date
                    String endDate = ci.getPostPlantSets().get(cc_size).get(ci.getPostPlantSets().get(cc_size).size() - 1);
                    //setDate0(ci, row, cc_size, start, end);
                    setDate(row, startDate, endDate);
                    setDateCheck = false;
                }
                if (setNameCheck) {
                    cropName = season_line_parts[s_out + 1].trim();
                    setNameCheck = false;
                }
                //init data memebers for current full interval
                CI_DM.sumSeasonOutData(ci_interval, s_out, season_line_parts);
            }
        } //
        //get data from season file corresponding single harvest crop
        else {
            try {
                //set date range on single harvest crop interval
                String plant = ci.getPlantOpDates(cc_size);
                String preplant = ci.getPreOpDates(cc_size);
                String postplant = ci.getPostOpDate(cc_size);

                setDate0(ci, row, cc_size, preplant, plant);
            } catch (ParseException ex) {
                LOGGER.error("Error processing single harvest crop interval.");
            }
            String end_year = ci.getPostPlantSets().get(cc_size).get(0).trim();
            s_out = DSC.getDateRange(terminateDateFormat, season_line_parts, end_year);
            cropName = season_line_parts[s_out + 1].trim();
            CI_DM.setSeasonOutData(ci_interval, s_out, season_line_parts);
        }
        try {
            setRowValue(row, COLUMN_CROP_CI, cropName);
        } catch (SQLException e) {
            LOGGER.error("Error reading crop name.");
        }
        //set data from season.out file
        set_season_data(s_out, season_line_parts, row, cropName, ci_interval);

        boolean set_swc = true;
        //use multiharvest date information to calcuate and sum multiharvest data from hydrobal data
        if (ci.getMultiHarvestDates().get(cc_size).size() > 1) {
            for (int x = 0; x < ci.getMultiHarvestDateInd().get(cc_size).size(); x++) {
                //determine data to sum for multiharvest crops 
                int index = ci.getMultiHarvestDateInd().get(cc_size).get(x);
                int harX = ci.getMultiHarvestDateInd().get(cc_size).size() - 1;
                int harN = ci.getMultiHarvestDateInd().get(cc_size).get(harX);
                boolean pre_equal_plant = !ci.getPreOpDates(cc_size).trim().equals(ci.getPlantOpDates(cc_size).trim());

                CI_DM.sumHydrobalOutDataInterval(ci_interval, ci, cc_size, hydrobal_line_parts, index, harN, set_swc, pre_equal_plant);
                set_swc = false;
            }
        } else {
            //otherwise interval is single non multiharvest, determine and set data from hydrobal
            int plantIndex = ci.getPlantIndex(cc_size);
            int postIndex = ci.getPostIndex(cc_size);
            boolean pre_equals_plant = !ci.getPreOpDates(cc_size).trim().equals(ci.getPlantOpDates(cc_size).trim());
            CI_DM.setHydrobalOutDataInterval(ci_interval, hydrobal_line_parts, plantIndex, postIndex, pre_equals_plant);
        }
        set_hydro_data(row, ci_interval);
        //determine period data...
        row_id = period_data_driver(ci, row_id, cc_size, runIndex, cycleNumber, season_line_parts, hydrobal_line_parts);
        return row_id;
    }

    private void set_hydro_data(Object[] row, CropIntervalDataMembers ci_dm)
            throws SQLException {

        try {
            double rainX = isUSUnits() ? ci_dm.getRain() * 0.03937 : ci_dm.getRain();
            setRowValue(row, COLUMN_RAIN_CI, rainX);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing rain.");
            setRowValue(row, COLUMN_RAIN_CI, 0);
        }
        // Rain (and Irrig)
        try {
            double irrigX = isUSUnits() ? ci_dm.getIrrig() * 0.03937 : ci_dm.getIrrig();
            setRowValue(row, COLUMN_IRRIG_CI, irrigX);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing irrig.");
        }
        try {
            double transpX = isUSUnits() ? ci_dm.getTransp() * 0.03937 : ci_dm.getTransp();
            setRowValue(row, COLUMN_TRANSP_CI, transpX);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing Transpiration.");
        }
        try {
            double initswcX = isUSUnits() ? ci_dm.getInitswc() * 0.03937 : ci_dm.getInitswc();
            setRowValue(row, COLUMN_INITSWC_CI, initswcX);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing Initial SWC.");
        }
        try {
            double finalswcX = isUSUnits() ? ci_dm.getFinalswc() * 0.03937 : ci_dm.getFinalswc();
            setRowValue(row, COLUMN_FINALSWC_CI, finalswcX);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing Final SWC.");
        }
        try {
            double deltaswc = (ci_dm.getFinalswc() - ci_dm.getInitswc());
            deltaswc = isUSUnits() ? deltaswc * 0.03937 : deltaswc;
            if (deltaswc < 0.0 && deltaswc > -0.1) {
                deltaswc = 0;
            }
            setRowValue(row, COLUMN_DELTASWC_CI, deltaswc);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing Delta SWC.");
        }
        try {
            double evapX = isUSUnits() ? ci_dm.getEvap() * 0.03937 : ci_dm.getEvap();
            setRowValue(row, COLUMN_EVAP_CI, evapX);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing evap.");
        }
        try {
            double runoffX = isUSUnits() ? ci_dm.getRunoff() * 0.03937 : ci_dm.getRunoff();
            setRowValue(row, COLUMN_RUNOFF_CI, runoffX);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing runoff.");
        }
        try {
            double drain = isUSUnits() ? ci_dm.getDrain() * 0.03937 : ci_dm.getDrain();
            setRowValue(row, COLUMN_DRAIN_CI, drain);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing drain.");
        }
        try {

            double wue = ci_dm.getTransp()
                    / (ci_dm.getRain() + ci_dm.getIrrig()
                    + (ci_dm.getInitswc()) - ci_dm.getFinalswc()
                    + (ci_dm.getSni() - ci_dm.getSnf()));
            wue *= 100;
            setRowValue(row, COLUMN_WUE_CI, wue);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing water use eff.");
        }
        try {
            double evaploss = ci_dm.getEvap() / (ci_dm.getRain() + ci_dm.getIrrig()
                    + (ci_dm.getInitswc() - ci_dm.getFinalswc())
                    + (ci_dm.getSni() - ci_dm.getSnf()));
            evaploss *= 100;
            setRowValue(row, COLUMN_EVAP_LOSS_CI, evaploss);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing evap loss.");
        }
        try {
            double drainloss = ci_dm.getDrain()
                    / (ci_dm.getRain() + ci_dm.getIrrig()
                    + (ci_dm.getInitswc() - ci_dm.getFinalswc())
                    + (ci_dm.getSni() - ci_dm.getSnf()));
            drainloss *= 100;
            setRowValue(row, COLUMN_DRAIN_LOSS_CI, drainloss);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing drain loss.");
        }
        try {
            double runoffloss = (ci_dm.getRunoff()
                    / (ci_dm.getRain() + ci_dm.getIrrig()
                    + (ci_dm.getInitswc() - ci_dm.getFinalswc())
                    + (ci_dm.getSni() - ci_dm.getSnf())));
            runoffloss *= 100;
            setRowValue(row, COLUMN_RUNOFF_LOSS_CI, runoffloss);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing Delta SWC.");
        }
        try {
            setRowValue(row, COLUMN_FALLOWEFF_CI, ci_dm.getFallow());
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing fallow_eff.");
        }
        try {
            setRowValue(row, COLUMN_WATERSTORAGE_CI, ci_dm.getWater_storage());
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing water_storage.");
        }
    }

    private void set_season_data(int s_out, String[] season_line_parts, Object[] row, String cropName, CropIntervalDataMembers ci_dm)
            throws SQLException {

        if (s_out >= season_line_parts.length) {
        } else {
            /*try {
                double standing_stemX = isUSUnits() ? ci_dm.getStanding_stem() * 8921.79 : ci_dm.getStanding_stem();
                setRowValue(row, COLUMN_STANDING_STEM_CI, standing_stemX);
            } catch (NumberFormatException nfe) {
                LOGGER.error("Error parsing standing_stem.");
            }
            try {
                double standing_leafX = isUSUnits() ? ci_dm.getStanding_leaf() * 8921.79 : ci_dm.getStanding_leaf();
                setRowValue(row, COLUMN_STANDING_LEAF_CI, standing_leafX);
            } catch (NumberFormatException nfe) {
                LOGGER.error("Error parsing standing_leafX.");
            }
            try {
                double stem_countX = isUSUnits() ? ci_dm.getStem_count() / 1000 : ci_dm.getStem_count();
                setRowValue(row, COLUMN_STEM_COUNT_CI, stem_countX);
            } catch (NumberFormatException nfe) {
                LOGGER.error("Error parsing stem_count.");
            }
            try {
                double avg_height = isUSUnits() ? ci_dm.getAvg_height() * 39.3701 : (ci_dm.getAvg_height() / 100);
                setRowValue(row, COLUMN_AVG_HEIGHT_CI, avg_height);
            } catch (NumberFormatException nfe) {
                LOGGER.error("Error parsing avg_height.");
            }
             */
            try {
                double biomass = ci_dm.getStanding_stem() + ci_dm.getStanding_leaf();
                biomass = isUSUnits() ? biomass * 8921.79 : biomass;
                setRowValue(row, COLUMN_BIOMASS_CI, biomass);
            } catch (NumberFormatException nfe) {
                LOGGER.error("Error parsing biomass.");
            }
        }
    }

    private int period_data_driver(CropIntervalDataStruct ci, int row_id, int cc_size, int runIndex, int cycleNumber,
            String[] season_line_parts, String[] hydrobal_line_parts) throws SQLException {

        boolean ran_1stPeriod = false;
        //condition1
        if (!ci.getPreOpDates(cc_size).trim().equals(ci.getPlantOpDates(cc_size).trim())) {
            //System.out.println("condition1");
            try {
                row_id = period_data(ci, row_id, cc_size, runIndex, cycleNumber, season_line_parts, hydrobal_line_parts);
            } catch (ParseException ex) {
                LOGGER.error("Error determining period data for crop interval (condition1).");
            }
            ran_1stPeriod = true;
        }
        //condition2
        if (ci.getPostOpDate(cc_size).equals("H") && (ran_1stPeriod == false)) {
            //System.out.println("condition2");
            try {
                row_id = period_data(ci, row_id, cc_size, runIndex, cycleNumber, season_line_parts, hydrobal_line_parts);
            } catch (ParseException ex) {
                LOGGER.error("Error determining period data for crop interval (condition2).");
            }
        }
        return row_id;
    }

    private int period_data(CropIntervalDataStruct ci, int row_id, int cc_size, int runIndex, int cycleNumber,
            String[] season_line_parts, String[] hydrobal_line_parts) throws SQLException, ParseException {
        try {
            //Set start first period of interval IF the preplant is not equals to the plant
            //meaning we have a pre plant period
            if (!ci.getPreOpDates(cc_size).trim().equals(ci.getPlantOpDates(cc_size).trim())) {
                String label = "CP_0";
                row_id = set_start_data(ci, row_id, cc_size, runIndex, cycleNumber, season_line_parts, hydrobal_line_parts, label);
            }
            //season data
            String cropName = null;
            //Object ci_dm_pd is short for crop interval data members period data
            //defines instance data used to calculate required fields for crop interval
            CropIntervalDataMembers ci_dm_pd = new CropIntervalDataMembers();
            //if multi harvest crop is found, collect period data for each period
            if (ci.getPostOpDate(cc_size).equals("H")) {
                row_id = multiHarvestPeriodData(ci_dm_pd, cropName, hydrobal_line_parts, season_line_parts, row_id, cc_size, runIndex, cycleNumber);
            }//
            //Otherwise NONMULTI harvest crop, collect data on single period for crop interval
            else {
                row_id = singleHarvestPeriodData(ci_dm_pd, cropName, hydrobal_line_parts, season_line_parts, row_id, runIndex, cycleNumber, cc_size);
            }
        } catch (NumberFormatException nfe) {
            JOptionPane.showMessageDialog(null, "Bad Value Found.\n"
                    + "Unable to properly load Crop Interval Data.", "Crop Interval Report", JOptionPane.WARNING_MESSAGE);
            LOGGER.error("Error parsing Double in method .period_data() of CropIntervalResultSet.java.");
        }
        return row_id;
    }

    private int multiHarvestPeriodData(CropIntervalDataMembers ci_dm_pd, String cropName, String[] hydrobal_line_parts, String[] season_line_parts, int row_id, int cc_size, int runIndex, int cycleNumber) throws SQLException, ParseException {
        String label = "CP_1";
        //if multiharvest crop set 1st Crop Period before determining remaining
        row_id = set_start_data_h(ci, row_id, cc_size, runIndex, cycleNumber, season_line_parts, hydrobal_line_parts, label);
        //loop sets remaining data for multi harvest crop periods
        for (int i = 0; i < ci.getPostPlantSets().get(cc_size).size() - 1; i++) {

            int s_out = 0;
            Object[] row_periodN = createNewRow(true);
            setRowValue(row_periodN, COLUMN_RUNID_CI, runIndex);
            setRowValue(row_periodN, COLUMN_CYCLENUMBER_CI, cycleNumber);
            setRowValue(row_periodN, COLUMN_CROPINDEX_CI, row_id);
            row_id++;
            setRowValue(row_periodN, COLUMN_INT_CI, false);
            setRowValue(row_periodN, COLUMN_UNITS_CI, this.isUSUnits());
            String curr_date = ci.getPostPlantSets().get(cc_size).get(i + 1);
            s_out = DSC.getDateRange(terminateDateFormat, season_line_parts, curr_date);
            setDate(row_periodN, ci.getPostPlantSets().get(cc_size).get(i), ci.getPostPlantSets().get(cc_size).get(i + 1));
            //setDateSingleSeason(ci, terminateDateFormat, row_periodN, cc_size, plant, preplant, postplant);
            if (s_out < season_line_parts.length) {
                CI_DM.setSeasonOutData(ci_dm_pd, s_out, season_line_parts);

                try {
                    setRowValue(row_periodN, COLUMN_KEY_CI, "CP_" + Integer.toString(i + 2));
                } catch (SQLException e) {
                    LOGGER.error("Error parsing crop key.");
                }
            }
            //set data from season.out file
            set_season_data(s_out, season_line_parts, row_periodN, cropName, ci_dm_pd);

            cropName = hydrobal_line_parts[ci.getMultiHarvestDateInd().get(cc_size).get(i + 1) + 1].trim();
            try {
                setRowValue(row_periodN, COLUMN_CROP_CI, cropName);
            } catch (SQLException e) {
                LOGGER.error("Error reading crop name.");
            }
            //set hydodro data
            int harvest_index = ci.getMultiHarvestDateInd().get(cc_size).get(i + 1);
            CI_DM.setHydrobalPeriod(ci_dm_pd, hydrobal_line_parts, harvest_index, label);
            set_hydro_data(row_periodN, ci_dm_pd);
        }
        return row_id;
    }

    private int singleHarvestPeriodData(CropIntervalDataMembers ci_dm_pd, String cropName, String[] hydrobal_line_parts, String[] season_line_parts, int row_id, int runIndex, int cycleNumber, int cc_size) throws SQLException, ParseException {
        Object[] row_period2 = createNewRow(true);
        setRowValue(row_period2, COLUMN_RUNID_CI, runIndex);
        setRowValue(row_period2, COLUMN_CYCLENUMBER_CI, cycleNumber);
        setRowValue(row_period2, COLUMN_CROPINDEX_CI, row_id);
        row_id++;
        setRowValue(row_period2, COLUMN_INT_CI, false);
        setRowValue(row_period2, COLUMN_UNITS_CI, this.isUSUnits());

        cropName = hydrobal_line_parts[ci.getPostIndex(cc_size) + 1].trim();
        try {
            setRowValue(row_period2, COLUMN_CROP_CI, cropName);
        } catch (SQLException e) {
            LOGGER.error("Error reading crop name.", e);
        }
        try {
            setRowValue(row_period2, COLUMN_KEY_CI, "CP_1");
        } catch (SQLException e) {
            LOGGER.error("Error parsing crop key.", e);
        }
        //set hydobal_data
        int postIndex = ci.getPostIndex(cc_size);
        CI_DM.setHydrobalPeriod(ci_dm_pd, hydrobal_line_parts, postIndex, "CP_1");
        set_hydro_data(row_period2, ci_dm_pd);
        //set season_data
        int s_out = 0;
        String year = ci.getPostPlantSets().get(cc_size).get(0).trim();
        s_out = DSC.getDateRange(terminateDateFormat, season_line_parts, year);
        CI_DM.setSeasonOutData(ci_dm_pd, s_out, season_line_parts);

        String plant = ci.getPlantOpDates(cc_size);
        String preplant = ci.getPreOpDates(cc_size);
        String postplant = ci.getPostOpDate(cc_size);
        setDate0(ci, row_period2, cc_size, plant, postplant);
        //set data from season.out file
        set_season_data(s_out, season_line_parts, row_period2, cropName, ci_dm_pd);
        return row_id;
    }

    private int set_start_data(CropIntervalDataStruct ci, int row_id, int cc_size, int runIndex, int cycleNumber,
            String[] season_line_parts, String[] hydrobal_line_parts, String label) throws SQLException, ParseException {
        try {
            Object[] row_period1 = createNewRow(true);
            setRowValue(row_period1, COLUMN_RUNID_CI, runIndex);
            setRowValue(row_period1, COLUMN_CYCLENUMBER_CI, cycleNumber);
            setRowValue(row_period1, COLUMN_CROPINDEX_CI, row_id);
            row_id++;
            setRowValue(row_period1, COLUMN_INT_CI, false);
            setRowValue(row_period1, COLUMN_UNITS_CI, this.isUSUnits());
            setDate(row_period1, ci.getPreOpDates(cc_size), ci.getPlantOpDates(cc_size));
            String plant = hydrobal_line_parts[ci.getPlantIndex(cc_size) + 1];
            try {
                setRowValue(row_period1, COLUMN_CROP_CI, plant);
            } catch (SQLException e) {
                LOGGER.error("Error reading crop name.", e);
            }
            try {
                setRowValue(row_period1, COLUMN_KEY_CI, label);
            } catch (SQLException e) {
                LOGGER.error("Error parsing crop key.", e);
            }
            CropIntervalDataMembers ci_pd_start = new CropIntervalDataMembers();
            set_season_data(0, season_line_parts, row_period1, plant, ci_pd_start);

            int plantIndex = ci.getPlantIndex(cc_size);
            //fill hydrobal data for CI_DM object to pass data to set_hydro_data
            CI_DM.setHydrobalPeriod(ci_pd_start, hydrobal_line_parts, plantIndex, label);
            set_hydro_data(row_period1, ci_pd_start);

        } catch (NumberFormatException nfe) {
            JOptionPane.showMessageDialog(null, "Bad Value Found.\n"
                    + "Unable to properly load Crop Interval Data.", "Crop Interval Report", JOptionPane.WARNING_MESSAGE);
            LOGGER.error("Error parsing Double in method .set_start_data() of CropIntervalResultSet.java.");
        }
        return row_id;
    }

    private int set_start_data_h(CropIntervalDataStruct ci, int row_id, int cc_size, int runIndex, int cycleNumber,
            String[] season_line_parts, String[] hydrobal_line_parts, String label) throws SQLException, ParseException {
        try {
            Object[] row_period1 = createNewRow(true);
            setRowValue(row_period1, COLUMN_RUNID_CI, runIndex);
            setRowValue(row_period1, COLUMN_CYCLENUMBER_CI, cycleNumber);
            setRowValue(row_period1, COLUMN_CROPINDEX_CI, row_id);
            row_id++;
            setRowValue(row_period1, COLUMN_INT_CI, false);
            setRowValue(row_period1, COLUMN_UNITS_CI, this.isUSUnits());
            setDate(row_period1, ci.getPlantOpDates(cc_size), ci.getMultiHarvestDates().get(cc_size).get(0));
            String plant = hydrobal_line_parts[ci.getMultiHarvestDateInd().get(cc_size).get(0) + 1];
            try {
                setRowValue(row_period1, COLUMN_CROP_CI, plant);
            } catch (SQLException e) {
                LOGGER.error("Error reading crop name.", e);
            }
            try {
                setRowValue(row_period1, COLUMN_KEY_CI, label);
            } catch (SQLException e) {
                LOGGER.error("Error parsing crop key.", e);
            }
            int s_out = 0;
            ArrayList<Integer> used_index = null;

            String end_date = (ci.getMultiHarvestDates().get(cc_size).get(0));
            s_out = DSC.getDateRange(terminateDateFormat, season_line_parts, end_date);

            CropIntervalDataMembers ci_pd_start_h = new CropIntervalDataMembers();

            if (s_out < season_line_parts.length) {
                CI_DM.setSeasonOutData(ci_pd_start_h, s_out, season_line_parts);
            }
            set_season_data(0, season_line_parts, row_period1, plant, ci_pd_start_h);

            int harvestX_ind = ci.getMultiHarvestDateInd().get(cc_size).get(0);
            CI_DM.setHydrobalPeriod(ci_pd_start_h, hydrobal_line_parts, harvestX_ind, label);

            set_hydro_data(row_period1, ci_pd_start_h);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing Double in method .set_start_data_h() of CropIntervalResultSet.java.");
        }
        return row_id;
    }

    private void setDate0(CropIntervalDataStruct ci, Object[] row, int cc_size, String start, String end) throws SQLException, ParseException {

        String dateString = null;
        try {
            dateString = ci.getPostPlantSets().get(cc_size).get(0).trim();
            Date terminateDate = terminateDateFormat.parse(dateString);
            endDateX = terminateDate;
            setRowValue(row, COLUMN_TERMINATEDATE_CI, new java.sql.Date(terminateDate.getTime()));

        } catch (ParseException pe) {
            LOGGER.error("Error parsing terminate date.", pe);
        }
        //addition of PREVIOUS date for plant/harvest
        try {
            currentDate = start;
            Date terminateDate2 = terminateDateFormat.parse(currentDate);
            startDateX = terminateDate2;
            setRowValue(row, COLUMN_PREVIOUSDATE_CI, new java.sql.Date(terminateDate2.getTime()));

        } catch (ParseException pe) {
            LOGGER.error("Error parsing terminate date.", pe);
        }
        try {
            if (startDateX.equals(endDateX)) {
                int dur = 365;
                dur *= rot_nums;
                setRowValue(row, COLUMN_DURATION_CI, dur);
            } else {
                int sday = convertToJulian365(startDateX);
                int eday = convertToJulian365(endDateX);
                if (sday > eday) {
                    int num_rotation_years = getYear(startDateX);
                    int dur = ((num_rotation_years * 365) - sday + eday);
                    setRowValue(row, COLUMN_DURATION_CI, dur);

                } else {
                    int dur = (eday - sday);
                    setRowValue(row, COLUMN_DURATION_CI, dur);

                }
            }
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing terminate date.", nfe);
        }
    }

    private void setDate(Object[] row, String start, String end) throws SQLException, ParseException {
        String dateString = null;
        try {
            dateString = end.trim();
            Date terminateDate = terminateDateFormat.parse(dateString);
            endDateX = terminateDate;
            setRowValue(row, COLUMN_TERMINATEDATE_CI, new java.sql.Date(terminateDate.getTime()));

        } catch (ParseException pe) {
            LOGGER.error("Error parsing terminate date.", pe);
        }
        //addition of PREVIOUS date for plant/harvest
        try {
            currentDate = start.trim();
            Date terminateDate2 = terminateDateFormat.parse(currentDate);
            startDateX = terminateDate2;
            setRowValue(row, COLUMN_PREVIOUSDATE_CI, new java.sql.Date(terminateDate2.getTime()));

        } catch (ParseException pe) {
            LOGGER.error("Error parsing Start Interval date.", pe);
        }
        try {
            if (startDateX.equals(endDateX)) {
                int dur = 365;
                dur *= rot_nums;
                setRowValue(row, COLUMN_DURATION_CI, dur);
                //plantings + nxt
            } else {
                int sday = convertToJulian365(startDateX);
                int eday = convertToJulian365(endDateX);
                if (sday > eday) {
                    int num_rotation_years = getYear(startDateX);
                    int dur = ((num_rotation_years * 365) - sday + eday);
                    setRowValue(row, COLUMN_DURATION_CI, dur);

                } else {
                    int dur = (eday - sday);
                    setRowValue(row, COLUMN_DURATION_CI, dur);

                }
            }
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing terminate date.", nfe);
        }
    }

    //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;
    }

    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);
        return numDays;
    }

    public static int getYear(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int year = calendar.get(Calendar.YEAR);
        return year;
    }
}
