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.io.FileNotFoundException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Calendar;
import org.apache.log4j.Logger;
import usda.weru.util.ConfigData;
import usda.weru.util.ConversionCalculator;
import usda.weru.util.ConversionCalculator.ConversionNotFoundException;
import usda.weru.util.ConversionCalculator.UnitNotFoundException;
import usda.weru.util.Util;

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

    private static final Logger LOGGER = Logger.getLogger(StirEnergyResultSet.class);
    public static final String NAME = "stir_energy";
    public static final String OUTPUT_FILE = "stir_energy.out";
    public static final String COLUMN_LINE = "line";
    public static final String COLUMN_STIR = "stir";
    public static final String COLUMN_ENERGY = "energy";
    public static final String COLUMN_DATE = "date";
    public static final String COLUMN_ROTATION = "rotation";
    public static final String COLUMN_OPERATION = "operation";
    public static final String COLUMN_CROP = "crop";
    public static final String COLUMN_FUEL = "fuel";
    public static final String COLUMN_FUELDEFAULT = "fueldefault";
    public static final String COLUMN_CROPNUMBER = "cropnumber";
    public static final String COLUMN_LASTHARVEST = "lastharvest";
    public static final String COLUMN_VOLUMENAME = "volumename";
    
    //These are used for coverstandingstirRunSummary (sub-report of the stir energy)
    public static final String COLUMN_CROPPLANTED = "planted";
    public static final String COLUMN_RANDOMROUGHNESS = "rr";
    public static final String COLUMN_SURFACECOVER = "surfacecover";
    public static final String COLUMN_EFFSTANDINGSIL = "silhouette";
    
    private final WepsConnection c_con;
    private boolean c_filled;

    public StirEnergyResultSet(WepsConnection con) throws SQLException {
        c_con = con;
        addColumn(RunsResultSet.COLUMN_RUNID, Types.INTEGER, 10, 0);

        //stir file specific 
        addColumn(COLUMN_LINE, Types.INTEGER, 10, 0);

        //Columns expected in the file
        addColumn(COLUMN_DATE, Types.DATE, 0, 0);
        addColumn(COLUMN_ROTATION, Types.INTEGER, 10, 0);
        addColumn(COLUMN_OPERATION, Types.VARCHAR, 50, 0);
        addColumn(COLUMN_CROP, Types.VARCHAR, 50, 0);
        addColumn(COLUMN_CROPPLANTED, Types.VARCHAR, 50, 0);
        addColumn(COLUMN_FUEL, Types.VARCHAR, 50, 0);
        addColumn(COLUMN_STIR, Types.DOUBLE, 10, 5);
        addColumn(COLUMN_ENERGY, Types.DOUBLE, 10, 5);
        addColumn(COLUMN_RANDOMROUGHNESS, Types.DOUBLE, 10, 5);
        addColumn(COLUMN_SURFACECOVER, Types.DOUBLE, 10, 5);
        addColumn(COLUMN_EFFSTANDINGSIL, Types.DOUBLE, 10, 5);
        addColumn(COLUMN_CROPNUMBER, Types.INTEGER, 10, 0);
        addColumn(COLUMN_LASTHARVEST, Types.BOOLEAN, 0, 0);
        addColumn(COLUMN_FUELDEFAULT, Types.BOOLEAN, 0, 0);
        addColumn(COLUMN_VOLUMENAME, Types.VARCHAR, 50, 0);
    }

    @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;
        }
        TFile[] files = c_con.getRunFiles();
        ArrayList<String> sub_report_data = getDetailReportData();
        
        for (int i = 0; i < files.length; i++) {
            TFile runDir = files[i];
            TFile output = new TFile(runDir, OUTPUT_FILE);
            if (!output.exists()) {
                //no stir_energy file, might be an old run
                continue;
            }

            //Stir energy file specific
            BufferedReader reader = null;
            try {
                String line;
                
                reader = new BufferedReader(new TFileReader(output));

                //first line, flexible header
                String header = reader.readLine();
                header = header.replaceFirst("#", "");
                String[] columns = header.split("\\|", -1);     //-1 so we grab blank columns, just in case

                int rotation = 0;
                String rotationLine = getLine(reader); 
                String[] rotationArray = rotationLine.split(" ");
                for(String s : rotationArray) {
                    try {
                        rotation = Integer.parseInt(s);
                        //System.out.println("Number of years in rotation is " + rotation);
                        break;
                    } catch(NumberFormatException e) {
                        e = new NumberFormatException("Not the year.");
                        System.err.println(e.getMessage());
                    }
                }
                int lineIndex = -1;
                while ((line = getLine(reader)) != null) {
                    //increment the line index for the next use.
                    lineIndex++;
                    Object[] row = createNewRow(true);
                    //Always add the run id so that joins will work
                    setRowValue(row, RunsResultSet.COLUMN_RUNID, i);

                    //Add the lineIndex so things can be sorted if need be specific to this file
                    setRowValue(row, COLUMN_LINE, lineIndex);

                    // Use -1 as the limit to allow String.split to return all 5 values regardless of whether they are
                    // blank or not.
                    String[] values = line.split("\\|", -1);
                    if (values.length != columns.length) {
                        //something changed in the file output format that is unexpected
                        LOGGER.warn("Unexpected number of columns on line " + lineIndex + "; found "
                                + values.length + ", expected " + columns.length + ". File: " + output.getAbsolutePath());
                        //go to the next line
                        continue;
                    }

                    try {
                        String dateText = values[column(columns, "dd/mm/yyyy")].trim();
                        Calendar cal1 = Calendar.getInstance();
                        String[] date1 = dateText.trim().split("/");
                        cal1.set(Integer.parseInt(date1[2].trim()), Integer.parseInt(date1[1].trim()) - 1, Integer.parseInt(date1[0].trim()));
                        setRowValue(row, COLUMN_DATE, new java.sql.Date(cal1.getTimeInMillis()));
                    } catch (NumberFormatException nfe) {
                        LOGGER.warn("Unable to parse date in file: " + output.getAbsolutePath() + " on line: " + lineIndex);
                    }
                    
                    //number of years in rotation
                    setRowValue(row, COLUMN_ROTATION, rotation);
                            
                    String operationText = values[column(columns, "operation")].trim();
                    setRowValue(row, COLUMN_OPERATION, operationText);
                    String cropText = values[column(columns, "crop name")].trim();
                    setRowValue(row, COLUMN_CROP, cropText);
                    
                    //obtain only planted crop names for planted crops
                    if((operationText.contains("Plant") || operationText.contains("Drill")) && !cropText.isEmpty()) {
                        setRowValue(row, COLUMN_CROPPLANTED, cropText);
                    }
                    
                    //only used in coverstandingstirRunSummary.jrxml file
                    if(lineIndex < sub_report_data.size()) {
                        try {
                            String[] data_line = sub_report_data.get(lineIndex).split(",");
                            String rrText = data_line[0];
                            Double rr = Double.valueOf(rrText);
                            
                            if(isUSUnits())
                                rr = ConversionCalculator.convert(rr, "mm", "in");
                            

                            String stdCoverText = data_line[1];
                            Double standingCover = Double.valueOf(stdCoverText);

                            String effStdSilText = data_line[2];
                            Double silhouette = Double.valueOf(effStdSilText);

                            setRowValue(row, COLUMN_RANDOMROUGHNESS, rr);
                            setRowValue(row, COLUMN_SURFACECOVER, standingCover);
                            setRowValue(row, COLUMN_EFFSTANDINGSIL, silhouette);
                        } catch(ConversionNotFoundException | UnitNotFoundException ce) {
                            LOGGER.warn("Unable to convert random roughness/standing cover/standing sil/ value from gui1_data.out file");
                        } catch(NumberFormatException nfe) {
                            LOGGER.warn("Unable to parse random roughness/standing cover/standing sil/ value from gui1_data.out file");
                        }
                    }
                    
                    if (column(columns, "fuel") >= 0) {
                        String fuelText = values[column(columns, "fuel")].trim();

                        //pull in default fuel if required
                        if (fuelText == null || fuelText.trim().length() == 0) {
                            fuelText = ConfigData.getDefault().getData(ConfigData.FuelDefault);
                            setRowValue(row, COLUMN_FUELDEFAULT, true);
                        } else {
                            setRowValue(row, COLUMN_FUELDEFAULT, false);
                        }

                        setRowValue(row, COLUMN_FUEL, fuelText);
                    } else {
                        String fuelText = ConfigData.getDefault().getData(ConfigData.FuelDefault);
                        setRowValue(row, COLUMN_FUEL, fuelText);
                        setRowValue(row, COLUMN_FUELDEFAULT, true);
                    }

                    try {
                        String stirText = values[column(columns, "stir")].trim();
                        Double stir = Double.valueOf(stirText);
                        setRowValue(row, COLUMN_STIR, stir);
                    } catch (NumberFormatException nfe) {
                        LOGGER.warn("Unable to parse stir value in file: " + output.getAbsolutePath()
                                + " on line: " + lineIndex);
                    }

                    try {
                        String energyText = values[column(columns, "energy")].trim();
                        Double energy = Double.valueOf(energyText);
                        if (isUSUnits()) {
                            energy = ConversionCalculator.convert(energy, "L/ha", "gal/ac");
                        }
                        setRowValue(row, COLUMN_ENERGY, energy);
                    } catch (UnitNotFoundException | ConversionNotFoundException unf) {
                        LOGGER.warn("Unable to convert energy value in file: " + output.getAbsolutePath()
                                + " on line: " + lineIndex);
                    } catch (NumberFormatException nfe) {
                        LOGGER.warn("Unable to parse energy value in file: " + output.getAbsolutePath()
                                + " on line: " + lineIndex);
                    }

                    try {
                        String cropSequenceText = values[column(columns, "crop sequence")].trim();
                        Integer cropSequence = Integer.valueOf(cropSequenceText);
                        setRowValue(row, COLUMN_CROPNUMBER, cropSequence - 1);
                    } catch (NumberFormatException nfe) {
                        LOGGER.warn("Unable to parse crop sequence value in file: " + output.getAbsolutePath()
                                + " on line: " + lineIndex);
                    }

                    try {
                        String lastHarvestText = values[column(columns, "1 if last")].trim();
                        Boolean lastHarvest = "1".equals(lastHarvestText);
                        setRowValue(row, COLUMN_LASTHARVEST, lastHarvest);
                    } catch (NumberFormatException nfe) {
                        LOGGER.warn("Unable to parse last harvest flag in file: " + output.getAbsolutePath()
                                + " on line: " + lineIndex);
                    }
                    
                    String volumeName;
                    if(isUSUnits()) {
                        volumeName = "gal";
                    }
                    else {
                        volumeName = "L";
                    }
                    setRowValue(row, COLUMN_VOLUMENAME, volumeName);
                }
            } catch (IOException ioe) {
                LOGGER.error("Error reading file: " + output.getAbsolutePath(), ioe);
                throw new SQLException("Error reading file: " + output.getAbsolutePath(), ioe);
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e) {
                        LOGGER.error("Error closing file: " + output.getAbsolutePath(), e);
                    }
                }
            }

        }

        c_filled = true;
    }
    
    /**
     * Retrieves surface cover/ effective standing silhouette/ random roughness
     * from the gui1_data.out file and used by _coverstandingstirRunSummary.jrxml
     * 
     * @return an ArrayList of strings with each value separated by commas
     */
    private ArrayList<String> getDetailReportData() {
        TFile gui1_datafile = new TFile(c_con.getRunFiles()[0], "gui1_data.out");
        ArrayList<String> sub_report_data = new ArrayList<>();
        String line;
        
        try (BufferedReader read = new BufferedReader(new TFileReader(gui1_datafile))) {
            while((line = read.readLine()) != null) {
                String[] line_elements = line.split("\\|", -1);
                String operation = line_elements[2].trim();
                if(!operation.isEmpty()) {
                    String surface_cover = line_elements[62].trim();
                    String standing_sil = line_elements[63].trim();
                    String rr = line_elements[71].trim();
                    
                    String data_collected = rr + "," + surface_cover + "," + standing_sil;
                    sub_report_data.add(data_collected);
                    
                    if(operation.contains("~")) {
                        sub_report_data.add(rr + "," + surface_cover + "," + standing_sil);
                    }
                }
            }
        } catch (FileNotFoundException fe) {
            System.out.println("Couldn't find file for Run Summary - Rotation Cover/Standing Silhouette/STIR. EXCEPTION: " + fe);
        } catch (IOException ex) {
            System.out.println("Couldn't Read filfe for Run Summary - Rotation Cover/Standing Silhouette/STIR. EXCEPTION: " + ex);
        }
        
        sub_report_data.remove(0);
        
        return sub_report_data;
    }

    private int column(String[] columns, String startsWith) {
        for (int i = 0; i < columns.length; i++) {
            if (columns[i].toLowerCase().trim().startsWith(startsWith.toLowerCase().trim())) {
                return i;
            }
        }

        return -1;
    }

    //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) {
                //blank line
                continue;
            }
            if (temp.charAt(0) != '#') {
                //not a comment
                return temp;
            }
        }
        return null;
    }

    public static String getDisplayUnits(String columnName, Boolean us) {
        if (COLUMN_ENERGY.equals(columnName)) {
            if (us) {
                return "gal diesel/ac";
            } else {
                return "L diesel/ha";
            }
        }
        return null;
    }
}
