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.IOException;
import java.sql.SQLException;
import java.sql.Types;
import java.text.MessageFormat;
import java.text.ParseException;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; 
import usda.weru.util.About;
import usda.weru.util.ConversionCalculator;
import usda.weru.util.ConversionUnit;
import usda.weru.util.Util;
import usda.weru.util.table.Column;
import usda.weru.util.table.DataAdjustment;
import usda.weru.util.table.DataLimit;
import usda.weru.util.table.WepsTableMeta;
import usda.weru.weps.RunFileData;
import static usda.weru.weps.wepsDate.WepsDateUtils.getSqlDateWeps;

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

    private static final String PATH_META = "tables/detail.xml";
    private static final Logger LOGGER = LogManager.getLogger(OutputResultSet.class);
    public static final String NAME = "output";

    /**
     * Index for the run.
     */
    public static final String COLUMN_RUNID = "runid";
    /**
     * Line number in the file.  0 is the header.  1 is the first line of data.  Line 0 is not included in the resultset.
     */
    public static final String COLUMN_LINE = "line";

    /**
     * Line key indicates the type of record the row represents.
     */
    public static final String COLUMN_KEY = "key";

    private static final String COLUMN_SDEDMOYR = "sd ed mo yr";
    //these three fields are used for special handling of the "sd ed mo yr" column
    public static final String COLUMN_STARTDATE = "startdate";
    public static final String COLUMN_ENDDATE = "enddate";
    public static final String COLUMN_PERIOD = "period";
    public static final String COLUMN_OPERATION = "operation";
    public static final String COLUMN_CROP = "crop";
    public static final String COLUMN_AVE_NO_EVNT = "ave_no_evnt";
    public static final String COLUMN_TOT_NO_EVNT = "tot_no_evnt";
    public static final String COLUMN_GLOSS_PER_EVNT = "gloss_per_evnt";
    public static final String COLUMN_NLOSS_PER_EVNT = "nloss_per_evnt";
    public static final String COLUMN_GROSS_LOSS = "gross_loss";
    public static final String COLUMN_TOT_LOSS = "tot_loss";
    public static final String COLUMN_CRPSALT = "crp+salt";
    public static final String COLUMN_SUSPEN = "suspen";
    public static final String COLUMN_PM10 = "pm10";
    public static final String COLUMN_PM25 = "pm2.5";
    public static final String COLUMN_CS1 = "cs1";
    public static final String COLUMN_CS2 = "cs2";
    public static final String COLUMN_CS3 = "cs3";
    public static final String COLUMN_CS4 = "cs4";
    public static final String COLUMN_SS1 = "ss1";
    public static final String COLUMN_SS2 = "ss2";
    public static final String COLUMN_SS3 = "ss3";
    public static final String COLUMN_SS4 = "ss4";
    public static final String COLUMN_PM10_1 = "pm10_1";
    public static final String COLUMN_PM10_2 = "pm10_2";
    public static final String COLUMN_PM10_3 = "pm10_3";
    public static final String COLUMN_PM10_4 = "pm10_4";
    public static final String COLUMN_PM25_1 = "pm2.5_1";
    public static final String COLUMN_PM25_2 = "pm2.5_2";
    public static final String COLUMN_PM25_3 = "pm2.5_3";
    public static final String COLUMN_PM25_4 = "pm2.5_4";
    public static final String COLUMN_SALT_LOSS = "salt_loss";
    public static final String COLUMN_LOSS_AREA = "loss_area";
    public static final String COLUMN_LOSS_FRAC = "loss_frac";
    public static final String COLUMN_SALT_DEP = "salt_dep";
    public static final String COLUMN_DEP_AREA = "dep_area";
    public static final String COLUMN_DEP_FRAC = "dep_frac";
    public static final String COLUMN_FLUX_RATE = "flux_rate";
    public static final String COLUMN_FLUX_AREA = "flux_area";
    public static final String COLUMN_FLUX_FRAC = "flux_frac";
    public static final String COLUMN_SHELT_AREA = "shelt_area";
    public static final String COLUMN_SHELT_FRAC = "shelt_frac";
    public static final String COLUMN_PRECIP = "precip";
    public static final String COLUMN_W_ENERGY = "w_energy";
    public static final String COLUMN_SNOW_COVER = "snow_cover";
    public static final String COLUMN_IRRIG = "irrig"; // New column
    public static final String COLUMN_L_CAN_COV = "l_can_cov";
    public static final String COLUMN_L_F_FL_COV = "l_f_fl_cov"; // New column
    public static final String COLUMN_L_SIL_AREA = "l_sil_area";
    public static final String COLUMN_L_ST_MASS = "l_st_mass"; // Obsolete (unused) column
    public static final String COLUMN_L_LST_MASS = "l_lst_mass"; // New column (replaces "l_st_mass" column)
    public static final String COLUMN_L_ABOVEGND = "l_abovegnd"; // New column
    public static final String COLUMN_L_RT_MASS = "l_rt_mass";
    public static final String COLUMN_L_ST_HT = "l_st_ht";
    public static final String COLUMN_L_NO_STEM = "l_no_stem";
    public static final String COLUMN_D_FL_COV = "d_fl_cov";
    public static final String COLUMN_D_ST_SIL = "d_st_sil";
    public static final String COLUMN_D_FL_MASS = "d_fl_mass";
    public static final String COLUMN_D_ST_MASS = "d_st_mass";
    public static final String COLUMN_D_ABOVEGND = "d_abovegnd"; // New column
    public static final String COLUMN_D_BG_MASS = "d_bg_mass";
    public static final String COLUMN_D_RT_MASS = "d_rt_mass";
    public static final String COLUMN_D_ST_HT = "d_st_ht";
    public static final String COLUMN_D_NO_STEM = "d_no_stem";
    public static final String COLUMN_B_F_FL_COV = "b_f_fl_cov";
    public static final String COLUMN_B_F_ST_SIL = "b_f_st_sil";
    public static final String COLUMN_B_M_FL_COV = "b_m_fl_cov";
    public static final String COLUMN_B_M_ST_SIL = "b_m_st_sil";
    public static final String COLUMN_B_ABOVEGND = "b_abovegnd"; // New column
    public static final String COLUMN_B_M_BG_ALL = "b_m_bg_all";
    public static final String COLUMN_RDG_OR = "rdg_or";
    public static final String COLUMN_RDG_HT = "rdg_ht";
    public static final String COLUMN_RDG_SP = "rdg_sp";
    public static final String COLUMN_RR = "rr";
    public static final String COLUMN_SURF_84 = "surf_84";
    public static final String COLUMN_SURF_AS = "surf_AS";
    public static final String COLUMN_SURF_SURF_AG_DEN = "surf_ag_den";
    public static final String COLUMN_SURF_SURF_AG_CA = "surf_ag_CA";
    public static final String COLUMN_SURF_CRUST = "surf_crust";
    public static final String COLUMN_SURF_CRUST_AS = "crust_AS";
    public static final String COLUMN_SURF_CRUST_LM = "crust_LM";
    public static final String COLUMN_SURF_CRUST_THICK = "crust_thick";
    public static final String COLUMN_SURF_CRUST_DEN = "crust_den";
    public static final String COLUMN_SURF_CRUST_LF = "crust_LF";
    public static final String COLUMN_SURF_CRUST_CA = "crust_CA";
    public static final String COLUMN_SOIL_WATER = "soil_water"; // New column
    public static final String COLUMN_CROP_TRANS = "crop_trans"; // New column
    public static final String COLUMN_EVAPORAT = "evaporat"; // New column
    public static final String COLUMN_DAYS = "days"; // Obsolete (unused) column?
    public static final String COLUMN_LOWER_LIMIT = "_lowerlimit"; // Obsolete (unused) column?
    public static final String COLUMN_UPPER_LIMIT = "_upperlimit"; // Obsolete (unused) column?

    private final WepsConnection c_con;
    private boolean c_filled;
    private final Map<String, ConversionUnit> c_siUnits;
    private final Map<String, ConversionUnit> c_usUnits;

    private WepsTableMeta c_meta;

    public OutputResultSet(WepsConnection con) throws SQLException {
        c_con = con;
        addColumn(COLUMN_RUNID, Types.INTEGER, 10, 0);
        addColumn(COLUMN_LINE, Types.INTEGER, 10, 0);
        addColumn(COLUMN_KEY, Types.CHAR, 1, 0);
        addColumn(COLUMN_STARTDATE, Types.DATE, 0, 0);
        addColumn(COLUMN_ENDDATE, Types.DATE, 0, 0);
        addColumn(COLUMN_PERIOD, Types.INTEGER, 10, 0);
        addColumn(COLUMN_OPERATION, Types.VARCHAR, 255, 0);
        addColumn(COLUMN_CROP, Types.VARCHAR, 255, 0);
        addColumn(COLUMN_DAYS, Types.INTEGER, 0, 0);

        //create the unit maps
        c_siUnits = new HashMap<String, ConversionUnit>();
        c_usUnits = new HashMap<String, ConversionUnit>();
        addColumn(COLUMN_GROSS_LOSS, Types.DOUBLE, 10, 3, "kg/m^2", "t/ac");
        addColumn(COLUMN_GROSS_LOSS + COLUMN_LOWER_LIMIT, Types.INTEGER, 10, 0);
        addColumn(COLUMN_GROSS_LOSS + COLUMN_UPPER_LIMIT, Types.INTEGER, 10, 0);

        addColumn(COLUMN_TOT_LOSS, Types.DOUBLE, 10, 3, "kg/m^2", "t/ac");
        addColumn(COLUMN_TOT_LOSS + COLUMN_LOWER_LIMIT, Types.INTEGER, 10, 0);
        addColumn(COLUMN_TOT_LOSS + COLUMN_UPPER_LIMIT, Types.INTEGER, 10, 0);

        addColumn(COLUMN_CRPSALT, Types.DOUBLE, 10, 3, "kg/m^2", "t/ac");
        addColumn(COLUMN_CRPSALT + COLUMN_LOWER_LIMIT, Types.INTEGER, 10, 0);
        addColumn(COLUMN_CRPSALT + COLUMN_UPPER_LIMIT, Types.INTEGER, 10, 0);

        addColumn(COLUMN_SUSPEN, Types.DOUBLE, 10, 3, "kg/m^2", "t/ac");
        addColumn(COLUMN_SUSPEN + COLUMN_LOWER_LIMIT, Types.INTEGER, 10, 0);
        addColumn(COLUMN_SUSPEN + COLUMN_UPPER_LIMIT, Types.INTEGER, 10, 0);

        addColumn(COLUMN_PM10, Types.DOUBLE, 10, 3, "kg/m^2", "t/ac");
        addColumn(COLUMN_PM10 + COLUMN_LOWER_LIMIT, Types.INTEGER, 10, 0);
        addColumn(COLUMN_PM10 + COLUMN_UPPER_LIMIT, Types.INTEGER, 10, 0);

        addColumn(COLUMN_PM25, Types.DOUBLE, 10, 3, "kg/m^2", "t/ac");
        addColumn(COLUMN_PM25 + COLUMN_LOWER_LIMIT, Types.INTEGER, 10, 0);
        addColumn(COLUMN_PM25 + COLUMN_UPPER_LIMIT, Types.INTEGER, 10, 0);

        addColumn(COLUMN_AVE_NO_EVNT, Types.DOUBLE, 10, 3, "fraction", "fraction");
        addColumn(COLUMN_TOT_NO_EVNT, Types.DOUBLE, 10, 3, "fraction", "fraction");

        addColumn(COLUMN_GLOSS_PER_EVNT, Types.DOUBLE, 10, 3, "kg/m^2", "t/ac");
        addColumn(COLUMN_NLOSS_PER_EVNT, Types.DOUBLE, 10, 3, "kg/m^2", "t/ac");

        addColumn(COLUMN_CS1, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");
        addColumn(COLUMN_CS2, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");
        addColumn(COLUMN_CS3, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");
        addColumn(COLUMN_CS4, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");

        addColumn(COLUMN_SS1, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");
        addColumn(COLUMN_SS2, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");
        addColumn(COLUMN_SS3, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");
        addColumn(COLUMN_SS4, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");

        addColumn(COLUMN_PM10_1, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");
        addColumn(COLUMN_PM10_2, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");
        addColumn(COLUMN_PM10_3, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");
        addColumn(COLUMN_PM10_4, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");

        addColumn(COLUMN_PM25_1, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");
        addColumn(COLUMN_PM25_2, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");
        addColumn(COLUMN_PM25_3, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");
        addColumn(COLUMN_PM25_4, Types.DOUBLE, 10, 3, "kg/m", "tn/1000ft");

        addColumn(COLUMN_SALT_LOSS, Types.DOUBLE, 10, 3, "kg/m^2", "t/ac");
        addColumn(COLUMN_LOSS_AREA, Types.DOUBLE, 10, 3, "ha", "ac");
        addColumn(COLUMN_LOSS_FRAC, Types.DOUBLE, 10, 3, "fraction", "fraction");

        addColumn(COLUMN_SALT_DEP, Types.DOUBLE, 10, 3, "kg/m^2", "t/ac");
        addColumn(COLUMN_DEP_AREA, Types.DOUBLE, 10, 3, "ha", "ac");
        addColumn(COLUMN_DEP_FRAC, Types.DOUBLE, 10, 3, "fraction", "fraction");

//        addColumn(COLUMN_FLUX_RATE, Types.DOUBLE, 10, 3, "kg/m^2", "t/ac"); // Don't know correct units?
        addColumn(COLUMN_FLUX_AREA, Types.DOUBLE, 10, 3, "ha", "ac");
        addColumn(COLUMN_FLUX_FRAC, Types.DOUBLE, 10, 3, "fraction", "fraction");

        addColumn(COLUMN_SHELT_AREA, Types.DOUBLE, 10, 3, "ha", "ac");
        addColumn(COLUMN_SHELT_FRAC, Types.DOUBLE, 10, 3, "fraction", "fraction");

        addColumn(COLUMN_PRECIP, Types.DOUBLE, 10, 3, "mm", "in");
        addColumn(COLUMN_W_ENERGY, Types.DOUBLE, 10, 3, "KJ/m^2/day", "KJ/m^2/day");
        addColumn(COLUMN_SNOW_COVER, Types.DOUBLE, 10, 3, "fraction", "fraction");
        addColumn(COLUMN_IRRIG, Types.DOUBLE, 10, 3, "mm", "in"); // New column

        addColumn(COLUMN_L_CAN_COV, Types.DOUBLE, 10, 3, "fraction", "fraction");
        addColumn(COLUMN_L_SIL_AREA, Types.DOUBLE, 10, 3, "fraction", "fraction");
        addColumn(COLUMN_L_F_FL_COV, Types.DOUBLE, 10, 3, "fraction", "fraction"); // New column
        addColumn(COLUMN_L_ST_MASS, Types.DOUBLE, 10, 3, "kg/m^2", "lb/ac"); // Obsolete (unused) column
        addColumn(COLUMN_L_LST_MASS, Types.DOUBLE, 10, 3, "kg/m^2", "lb/ac"); // New column (replaces "l_st_mass" column)
        addColumn(COLUMN_L_ABOVEGND, Types.DOUBLE, 10, 3, "kg/m^2", "lb/ac");
        addColumn(COLUMN_L_RT_MASS, Types.DOUBLE, 10, 3, "kg/m^2", "lb/ac");
        addColumn(COLUMN_L_ST_HT, Types.DOUBLE, 10, 3, "mm", "in");
        addColumn(COLUMN_L_NO_STEM, Types.DOUBLE, 10, 3, "#/m^2", "#/ac");

        addColumn(COLUMN_D_FL_COV, Types.DOUBLE, 10, 3, "fraction", "fraction");
        addColumn(COLUMN_D_ST_SIL, Types.DOUBLE, 10, 3, "fraction", "fraction");
        addColumn(COLUMN_D_FL_MASS, Types.DOUBLE, 10, 3, "kg/m^2", "lb/ac");
        addColumn(COLUMN_D_ST_MASS, Types.DOUBLE, 10, 3, "kg/m^2", "lb/ac");
        addColumn(COLUMN_D_ABOVEGND, Types.DOUBLE, 10, 3, "kg/m^2", "lb/ac"); // New column
        addColumn(COLUMN_D_BG_MASS, Types.DOUBLE, 10, 3, "kg/m^2", "lb/ac");
        addColumn(COLUMN_D_RT_MASS, Types.DOUBLE, 10, 3, "kg/m^2", "lb/ac");
        addColumn(COLUMN_D_ST_HT, Types.DOUBLE, 10, 3, "mm", "in");
        addColumn(COLUMN_D_NO_STEM, Types.DOUBLE, 10, 3, "#/m^2", "#/ac");

        addColumn(COLUMN_B_F_FL_COV, Types.DOUBLE, 10, 3, "fraction", "fraction");
        addColumn(COLUMN_B_F_ST_SIL, Types.DOUBLE, 10, 3, "fraction", "fraction");
        addColumn(COLUMN_B_M_FL_COV, Types.DOUBLE, 10, 3, "kg/m^2", "lb/ac");
        addColumn(COLUMN_B_M_ST_SIL, Types.DOUBLE, 10, 3, "kg/m^2", "lb/ac");
        addColumn(COLUMN_B_ABOVEGND, Types.DOUBLE, 10, 3, "kg/m^2", "lb/ac"); // New column
        addColumn(COLUMN_B_M_BG_ALL, Types.DOUBLE, 10, 3, "kg/m^2", "lb/ac");

        addColumn(COLUMN_RDG_OR, Types.DOUBLE, 10, 3, "degree", "degree");
        addColumn(COLUMN_RDG_HT, Types.DOUBLE, 10, 3, "mm", "in");
        addColumn(COLUMN_RDG_SP, Types.DOUBLE, 10, 3, "mm", "in");
        addColumn(COLUMN_RR, Types.DOUBLE, 10, 3, "mm", "in");

        addColumn(COLUMN_SURF_84, Types.DOUBLE, 10, 3, "fraction", "fraction");
        addColumn(COLUMN_SURF_AS, Types.DOUBLE, 10, 3, "ln(J/m^2)", "ln(J/m^2)");
        addColumn(COLUMN_SURF_SURF_AG_DEN, Types.DOUBLE, 10, 3, "Mg/m^3", "lb/ft^3");
        addColumn(COLUMN_SURF_SURF_AG_CA, Types.DOUBLE, 10, 3, "fraction", "fraction");
        addColumn(COLUMN_SURF_CRUST_AS, Types.DOUBLE, 10, 3, "ln(J/m^2)", "ln(J/m^2)");

        addColumn(COLUMN_SURF_CRUST, Types.DOUBLE, 10, 3, "fraction", "fraction");
        addColumn(COLUMN_SURF_CRUST_LM, Types.DOUBLE, 10, 3, "Mg/m^3", "lb/ft^3");
        addColumn(COLUMN_SURF_CRUST_THICK, Types.DOUBLE, 10, 3, "mm", "in");
        addColumn(COLUMN_SURF_CRUST_DEN, Types.DOUBLE, 10, 3, "Mg/m^3", "lb/ft^3");
        addColumn(COLUMN_SURF_CRUST_LF, Types.DOUBLE, 10, 3, "fraction", "fraction");
        addColumn(COLUMN_SURF_CRUST_CA, Types.DOUBLE, 10, 3, "1/m", "1/ft");
        addColumn(COLUMN_SOIL_WATER, Types.DOUBLE, 10, 3, "mm", "in"); // New column
        addColumn(COLUMN_CROP_TRANS, Types.DOUBLE, 10, 3, "mm", "in"); // New column
        addColumn(COLUMN_EVAPORAT, Types.DOUBLE, 10, 3, "mm", "in"); // New column
    }

    private void addColumn(String columnName, int type, int p, int s, String si, String us) throws SQLException {
        try {
            ConversionUnit siUnit = ConversionCalculator.getUnitFromTag(si);
            ConversionUnit usUnit = ConversionCalculator.getUnitFromTag(us);
            c_siUnits.put(columnName, siUnit);
            c_usUnits.put(columnName, usUnit);
            addColumn(columnName, type, p, s);
        } catch (ConversionCalculator.UnitNotFoundException e) {
            //fail fast
            throw new SQLException("Unsupported units.", e);
        }

    }

    @Override
    public String getName() {
        return NAME;
    }
    
    protected boolean isUSUnits() {
        return Util.USUnits.equals(c_con.getUnits());
    }

    @Override
    public synchronized void fill() throws SQLException {
        if (c_filled) {
            return;
        }

        if (c_meta == null) {
            c_meta = new WepsTableMeta();
            c_meta.fromFile(new TFile(About.getUserWorking() + "/" + PATH_META));
        }

        TFile[] files = c_con.getRunFiles();

        for (int i = 0; i < files.length; i++) {
            TFile runDir = files[i];
            TFile outputFile = new TFile(runDir, RunFileData.WepsOutput);

            if (outputFile.exists()) {

                BufferedReader in = null;
                try {
                    in = new BufferedReader(new TFileReader(outputFile));

                    // First line is the headers.
                    // We create a list of the column headers so we know what column to deal with.
                    // The order is not likely to change.
                    // Therefore, instead of dealing with column 1, we deal with the column mapped to "sd ed mo yr";
                    List<String> columns = new LinkedList<String>();
                    String headerLine = in.readLine();
                    if (headerLine == null) {
                        return;
                    }
                    String[] headerParts = headerLine.split("\\|", -1);
                    for (String columnName : headerParts) {
                        //trim and add each column to the list
                        columnName = columnName.trim();
                        columns.add(columnName);
                    }

                    int keyIndex = columns.indexOf(COLUMN_KEY);
                    String line;
                    int lineIndex = 0; //0 = header, not included in resultset
                    while ((line = in.readLine()) != null) {
                        //blank line
                        if (line.trim().length() == 0) {
                            continue;
                        }
                        //split the line into the value parts
                        String[] parts = line.split("\\|", -1);
                        if (parts.length != columns.size()) {
                            //little sanity check
                            LOGGER.error("Mismatched column count.  Line: " + lineIndex + " of file: " + outputFile.getAbsolutePath());
                            continue;
                        }

                        Object[] row = createNewRow(true);
                        setRowValue(row, COLUMN_RUNID, i);

                        //Add the line index for sorting convienence
                        lineIndex++;
                        setRowValue(row, COLUMN_LINE, lineIndex);

                        //handle the key column
                        String key = parts[keyIndex].trim();
                        setRowValue(row, COLUMN_KEY, key);

                        for (int ci = 1; ci < parts.length; ci++) {
                            String columnName = columns.get(ci);
                            if (columnName.length() == 0) {
                                continue;
                            }
                            String part = parts[ci].trim();
                            handleColumn(row, key, columnName, part);
                        }

                    }

                } catch (IOException ioe) {
                    LOGGER.error("Error reading output file: " + outputFile.getAbsolutePath(), ioe);
                } finally {
                    if (in != null) {
                        try {
                            in.close();
                        } catch (IOException e) {
                            LOGGER.error("Error closing output file: " + outputFile.getAbsolutePath(), e);
                        }
                    }
                    c_filled = true;
                }
            }
        }

    }
    private static final MessageFormat FORMAT_PERIODDATES = new MessageFormat("{0}-{1}/{2}/{3}");
    private static final MessageFormat FORMAT_ROTATIONYEARPERIOD = new MessageFormat("Rot. yr:{0}");
    private static final MessageFormat FORMAT_MONTHPERIOD = new MessageFormat("Month:{0}");
    private static final MessageFormat FORMAT_YEARPERIOD = new MessageFormat("Year:{0}");

    private void handleColumn(Object[] row, String key, String columnName, String text) throws SQLException {
        if (COLUMN_SDEDMOYR.equals(columnName)) {
            //handle the dates
            switch (key) {
                case "P":
                    try {
                        //Period row, sd-ed/mo/yr
                        Object[] parts = FORMAT_PERIODDATES.parse(text);

                        int start = Integer.valueOf(parts[0].toString().trim());
                        int end = Integer.valueOf(parts[1].toString().trim());
                        int month = Integer.valueOf(parts[2].toString().trim());
                        month--;    //java months are 0 based
                        int year = Integer.valueOf(parts[3].toString().trim());
                        setRowValue(row, COLUMN_STARTDATE, getSqlDateWeps(year, month, start));
                        if (month == 1 && end == 29) {
                            GregorianCalendar gc = new GregorianCalendar();
                            if (!gc.isLeapYear(year)) {
                                end = 28;
                            }                            
                        }
                        setRowValue(row, COLUMN_ENDDATE, getSqlDateWeps(year, month, end));
                        //      setRowValue(row,COLUMN_DAYS,0);
                    } catch (ParseException pe) {
                        LOGGER.error("Error parsing period date: " + text, pe);
                    }
                    break;
                case "Y":
                    //rotation year row, Rot. yr: #
                    try {
                        Object[] parts = FORMAT_ROTATIONYEARPERIOD.parse(text);
                        Integer period = Integer.valueOf(parts[0].toString().trim());
                        setRowValue(row, COLUMN_PERIOD, period);
                    } catch (ParseException pe) {
                        LOGGER.error("Error parsing rotation year period: " + text, pe);
                    }
                    break;
                case "m":
                    //month row, Month:    #
                    try {
                        Object[] parts = FORMAT_MONTHPERIOD.parse(text);
                        Integer period = Integer.valueOf(parts[0].toString().trim());
                        setRowValue(row, COLUMN_PERIOD, period);
                    } catch (ParseException pe) {
                        LOGGER.error("Error parsing rotation year period: " + text, pe);
                    }
                    break;
                case "y":
                    //year row, Year:     #
                    try {
                        Object[] parts = FORMAT_YEARPERIOD.parse(text);
                        Integer period = Integer.valueOf(parts[0].toString().trim());
                        setRowValue(row, COLUMN_PERIOD, period);
                    } catch (ParseException pe) {
                        LOGGER.error("Error parsing rotation year period: " + text, pe);
                    }
                    break;
            }
        } else if (COLUMN_OPERATION.equals(columnName)) {
            if (text != null && text.length() > 0) {
                setRowValue(row, COLUMN_OPERATION, text);
            }

        } else if (COLUMN_CROP.equals(columnName)) {
            if (text != null && text.length() > 0) {
                setRowValue(row, COLUMN_CROP, text);
            }
        } else {
            try {
                Double value = Double.valueOf(text);
                Column column = c_meta.getColumn(columnName);

                //apply adjustments, some soil loss values need to be multiplied by -1
                if (column != null && column.getAdjustmentList() != null) {
                    for (DataAdjustment adjust : column.getAdjustmentList()) {
                        value = (Double) adjust.adjust(value);
                    }
                }

                if (isUSUnits() && c_siUnits.containsKey(columnName)) {

                    ConversionUnit siUnit = c_siUnits.get(columnName);
                    ConversionUnit usUnit = c_usUnits.get(columnName);
                    value = ConversionCalculator.convert(value, siUnit, usUnit);
                }
                if (Math.abs(value) == 0) {
                    value = 0d;
                }
                setRowValue(row, columnName, value);

                //handle limits
                if (column != null) {
                    DataLimit limit = column.getLimit(isUSUnits() ? Util.USUnits : Util.SIUnits);
                    if (limit != null && findColumn(columnName + COLUMN_LOWER_LIMIT) > 0) {
                        if (limit.outisdeLowerBound(value)) {
                            setRowValue(row, columnName + COLUMN_LOWER_LIMIT, 1);
                        } else {
                            setRowValue(row, columnName + COLUMN_LOWER_LIMIT, 0);
                        }
                    }
                    if (limit != null && findColumn(columnName + COLUMN_UPPER_LIMIT) > 0) {
                        if (limit.outisdeUpperBound(value)) {
                            setRowValue(row, columnName + COLUMN_UPPER_LIMIT, 1);
                        } else {
                            setRowValue(row, columnName + COLUMN_UPPER_LIMIT, 0);
                        }

                    }
                }

            } catch (NumberFormatException | SQLException nfe) {
            } catch (ConversionCalculator.ConversionNotFoundException e) {
                LOGGER.error("Error converting value.", e);
            }
        }

    }

}
