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.Array;
import java.sql.SQLException;
import java.sql.Types;
import java.text.DateFormat;
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 alex.blum
 */
public class CoverCropResultSet extends WepsResultSet {

    private static final Logger LOGGER = Logger.getLogger(HarvestsResultSet.class);
    private boolean flag = false;

    // check if rain and irrig data are split (true if so)
    private boolean checkFlag() {
        return this.flag;
    }

    private void setFlag(boolean flag) {
        this.flag = flag;
    }

    private boolean rot_check = true;

    private boolean checkRot() {
        return rot_check;
    }

    private void setRot(boolean b) {
        rot_check = b;
    }

    private boolean ccDataSet;

    private boolean ccDataCheck() {
        return ccDataSet;
    }

    private void setCCDataCheck(boolean b) {
        ccDataSet = b;
    }

    private boolean empty_CC;

    private boolean check_EmptyCC() {
        return empty_CC;
    }

    private void set_EmptyCC(boolean b) {
        empty_CC = b;
    }

    protected String currentDate = null;
    protected Date startDateX = null;
    protected Date endDateX = null;

    public static final String NAME = "covercrop";
    public static final String COLUMN_CROP = "crop";
    public static final String COLUMN_RUNID = "runid";
    public static final String COLUMN_CYCLENUMBER = "cyclenumber";
    public static final String COLUMN_TERMINATEDATE = "terminatedate";
    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_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";
    //Season.out data fields
    public static final String COLUMN_STANDING_STEM = "standing_stem";
    public static final String COLUMN_STANDING_LEAF = "standing_leaf";
    public static final String COLUMN_AVG_HEIGHT = "avg_height";
    public static final String COLUMN_STEM_COUNT = "stem_count";
    public static final String COLUMN_CAN_COVER = "canopy_cover";

    private final WepsConnection c_con;
    private boolean c_filled;
    public static final String COLUMN_UNITS = "units";
    private int rot_nums;

    private final String runName;

    public ArrayList<Integer> getUsed_index() {
        return used_index;
    }

    public ArrayList<Integer> getOp_index() {
        return op_index;
    }

    public ArrayList<Integer> getDate_index() {
        return date_index;
    }

    public ArrayList<String> getDateStrings() {
        return dateStrings;
    }

    public ArrayList<String> getHarvest_ops() {
        return harvest_ops;
    }

    private final ArrayList<Integer> used_index;
    private final ArrayList<Integer> op_index;
    private final ArrayList<Integer> date_index;
    private final ArrayList<String> dateStrings;
    private final ArrayList<String> harvest_ops;

    public CoverCropResultSet(WepsConnection con, String name) throws SQLException {
        runName = name;
        c_con = con;
        rot_nums = 0;
        ccDataSet = true;
        empty_CC = true;
        //hydrobal.out data
        addColumn(COLUMN_TERMINATEDATE, Types.DATE, 10, 0);
        addColumn(COLUMN_PREVIOUSDATA, Types.DATE, 10, 0);
        addColumn(COLUMN_DURATION, Types.INTEGER, 10, 0);

        addColumn(COLUMN_CROPINDEX, Types.INTEGER, 10, 0);
        addColumn(COLUMN_RUNID, Types.INTEGER, 10, 0);
        addColumn(COLUMN_CYCLENUMBER, Types.INTEGER, 10, 0);

        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);

        //season.out
        addColumn(COLUMN_CROP, Types.VARCHAR, 255, 0);
        addColumn(COLUMN_STANDING_STEM, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_STANDING_LEAF, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_AVG_HEIGHT, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_STEM_COUNT, Types.DOUBLE, 10, 3);
        addColumn(COLUMN_CAN_COVER, Types.DOUBLE, 10, 3);

        addColumn(COLUMN_UNITS, Types.BOOLEAN, 10, 3);

        used_index = new ArrayList<>();
        op_index = new ArrayList<>();
        date_index = new ArrayList<>();
        dateStrings = new ArrayList<>();
        harvest_ops = new ArrayList<>();

    }

    @Override
    public String getName() {
        return NAME;
    }

    protected boolean isUSUnits() {
        return Util.USUnits.equals(c_con.getUnits());
    }

    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]);
        }
        try {
            man_f.close();
        } catch (IOException ex) {
            LOGGER.error("Error closing man_f.");
        }
    }

    static boolean lockMessage = true;

    void setLockMessage(boolean bool) {
        lockMessage = bool;
    }

    boolean getLockMessage() {
        return lockMessage;
    }

    static ArrayList<String> currentRanFiles = new ArrayList<>();

    DateFormat terminateDateFormat = new SimpleDateFormat("dd/MM/yyyy");

    @Override
    public synchronized void fill() throws SQLException {

        if (currentRanFiles.contains(runName)) {
        } else {
            currentRanFiles.add(runName);
            setLockMessage(true);
        }

        if (c_filled) {
            return;
        }

        TFile[] files = c_con.getRunFiles();
        //System.out.println("CoverCropResultSet");
        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 {

        try {
            TFile runDir = files[runIndex];
            if (checkRot()) {
                getRotationYears(runDir);
                setRot(false);
            }
            //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);

            if (seasonFile.exists() && hydrobalFile.exists() && harvestFile.exists()) {
                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;
                    while (((season_l = getLine(season_f)) != null) && ((hydro_l = getLine(hydobal_f)) != null) && ((harvest_l = getLine(harvest_f)) != null)) {
                        process_files(cycleNumber, runIndex, season_l, hydro_l, harvest_l);
                    }
                    //if files harvest.out, season.out, hydrobal.out are empty
                    //no cover crop data to report.

                } catch (IOException ioe) {
                    LOGGER.error("Error reading season.out file or hydrobal.out file: " + seasonFile.getAbsolutePath()
                            + hydrobalFile.getAbsolutePath(), ioe);
                } finally {
                    if (season_f != null) {
                        try {
                            season_f.close();
                        } catch (IOException e) {
                            LOGGER.error("Error closing season.out file: " + seasonFile.getAbsolutePath(), e);
                        }
                    }
                    if (hydobal_f != null) {
                        try {
                            hydobal_f.close();
                        } catch (IOException e) {
                            LOGGER.error("Error closing hydrobal.out file: " + hydrobalFile.getAbsolutePath(), e);
                        }
                    }
                    if (harvest_f != null) {
                        try {
                            harvest_f.close();
                        } catch (IOException e) {
                            LOGGER.error("Error closing hydrobal.out file: " + harvestFile.getAbsolutePath(), e);
                        }
                    }
                }
            }
        } catch (FileNotFoundException ex) {
            Exceptions.printStackTrace(ex);
        }

    }

    private void process_files(int cycleNumber, int runIndex, String season_l, String hydro_l, String harvest_l) throws SQLException {

        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);
        String[] rainIrrigSplit = hydrobal_line_parts[10].split("\\,", -1);
        if (!rainIrrigSplit[1].equals("irrigation")) {
            if (getLockMessage()) {
                JOptionPane.showMessageDialog(null, "Older output data incompatible with Cover Crop Reports.\n"
                        + "Unable to load Cover Crop Data.", "Cover Crop Report", JOptionPane.WARNING_MESSAGE);
                setLockMessage(false);
            }
            return;
        }

        //get cover crop details
        if (this.ccDataCheck()) {
            getCoverCropDetails(harvest_l, hydrobal_line_parts);
            this.setCCDataCheck(false);
        }

        if (op_index != null) {
            for (int cc_size = 0; cc_size < op_index.size(); cc_size++) {
                setCover(runIndex, cycleNumber, cc_size, season_line_parts, hydrobal_line_parts);
            }
        }
        used_index.clear();
    }

    //crop details
    private void getCoverCropDetails(String harvest_l, String[] hydrobal_line_parts) {

        //Determine/Store dates in harvest file 
        String[] harvest_line_parts = harvest_l.split("\\|", -1);
        ArrayList<String> harvest_dates = new ArrayList<>();
        for (String harvest_line_part : harvest_line_parts) {
            if (isValidDate(harvest_line_part)) {
                harvest_dates.add(harvest_line_part.trim());
            }
        }
        //check for cover crop...
        //when cover crop operation determine how many exist...
        String[] first_delim = null;
        ArrayList<String[]> data = new ArrayList<>();
        for (String hydrobal_line_part : hydrobal_line_parts) {
            first_delim = hydrobal_line_part.split("\\W+", -1);
            data.add(first_delim);
        }

        harvest_ops.add("Harvest");
        harvest_ops.add("Bale");
        harvest_ops.add("Graze");
        cover_crop_line_data(hydrobal_line_parts, harvest_dates);
    }

    /* This method is responsible for capurting data including: 
     1. operation indices to be used in determining where data needs to be pulled from the hydrobal.out file 
     2. date indicies are caputured to be used in determining when the crop interval ended
     3. harvest_dates are used to compare against what crop is harvested to allow the correct cover crop information to be captured 
     4. date strings contains the actaul string corresponding to the date needed for the cover crop
     the Lists are passed in blanked and initilizied during the processing of the line data
     */
    private void cover_crop_line_data(String[] hydrobal_line_parts, ArrayList<String> harvest_dates) {

        for (int i = 0; i < hydrobal_line_parts.length; i++) {
            String dateX = hydrobal_line_parts[i].trim();

            if (isValidDate(dateX)) {
                if (!harvest_dates.contains(dateX)) {

                    double trans = Double.parseDouble(hydrobal_line_parts[i + 15].trim());
                    if (trans > 0) {
                        dateStrings.add(hydrobal_line_parts[i].trim());
                        date_index.add(i);
                        op_index.add(i + 1);
                    }
                }
            }
        }
    }

    private void setCover(int runIndex, int cycleNumber, int cc_size, String[] season, String[] hydro) throws SQLException {
        Object[] row = createNewRow(true);
        setRowValue(row, COLUMN_RUNID, runIndex);
        setRowValue(row, COLUMN_CYCLENUMBER, cycleNumber);
        setRowValue(row, COLUMN_CROPINDEX, cc_size);
        setRowValue(row, COLUMN_UNITS, this.isUSUnits());
        String dateString = null;
        int s_out = 0;
        //find start index of cover crop date from season.out
        //System.out.println("before getDateRange...");
        s_out = getDateRange(terminateDateFormat, row, season, hydro, s_out, date_index, cc_size, op_index, dateStrings, dateString, used_index);
        //sanity check, determine if we ran off the end of the array looking for cover crop detail
        if (s_out >= season.length) {
        } else {
            setCCFields(row, season, hydro, s_out, cc_size);
        }
    }

    private void setCCFields(Object[] row, String[] season_line_parts, String[] hydrobal_line_parts, int s_out, int cc_size) throws SQLException {

        try {
            String cropName = season_line_parts[s_out + 1].trim();
            //System.out.println(cropName);
            setRowValue(row, COLUMN_CROP, cropName);
        } catch (SQLException e) {
            LOGGER.error("Error reading crop name.");
        }
        try {
            Double standing_stem = Double.valueOf(season_line_parts[s_out + 2]);
            //System.out.println("standing_stem: " + standing_stem);
            standing_stem = isUSUnits() ? standing_stem * 8921.79 : standing_stem;
            setRowValue(row, COLUMN_STANDING_STEM, standing_stem);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing standing_stem.");
        }
        try {
            Double standing_leaf = Double.valueOf(season_line_parts[s_out + 3]);
            //System.out.println("standing_leaf: " + standing_leaf);
            standing_leaf = isUSUnits() ? standing_leaf * 8921.79 : standing_leaf;
            setRowValue(row, COLUMN_STANDING_LEAF, standing_leaf);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing standing_leaf.");
        }
        try {
            Double stem_count = Double.valueOf(season_line_parts[s_out + 12]);
            //System.out.println("stem_count: " + stem_count);
            stem_count = isUSUnits() ? stem_count / 10.7639 : stem_count;
            setRowValue(row, COLUMN_STEM_COUNT, stem_count);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing stem_count.");
        }
        try {
            Double avg_height = Double.valueOf(season_line_parts[s_out + 11]);
            //System.out.println("avg_height: " + avg_height);
            avg_height = isUSUnits() ? avg_height * 39.3701 : avg_height;
            setRowValue(row, COLUMN_AVG_HEIGHT, avg_height);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing avg_height.");
        }
        try {
            Double can_cover = Double.valueOf(season_line_parts[s_out + 16]);
            //System.out.println(can_cover);
            setRowValue(row, COLUMN_CAN_COVER, can_cover);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing avg_height.");
        }
        //hydrobal.out data section
        //System.out.println("hydrobal.out section...");
        try {
            Double rain = Double.valueOf(hydrobal_line_parts[op_index.get(cc_size) + 10]);
            //System.out.println("rain: " + rain);
            rain = isUSUnits() ? rain * 0.03937 : rain;
            setRowValue(row, COLUMN_RAIN, rain);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing rain.");
        }
        // Rain (and Irrig)
        try {
            Double irrig = Double.valueOf(hydrobal_line_parts[op_index.get(cc_size) + 11]);
            //System.out.println("irrig: " + irrig);
            irrig = isUSUnits() ? irrig * 0.03937 : irrig;
            setRowValue(row, COLUMN_IRRIG, irrig);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing irrig.");
        }
        try {
            Double transp = Double.valueOf(hydrobal_line_parts[op_index.get(cc_size) + 14]);
            //System.out.println("transp: " + transp);
            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.");
        }
        try {
            Double initswc = Double.valueOf(hydrobal_line_parts[op_index.get(cc_size) + 3]);
            //System.out.println("initswc: " + initswc);
            initswc = isUSUnits() ? initswc * 0.03937 : initswc;
            setRowValue(row, COLUMN_INITSWC, initswc);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing Initial SWC.");
        }
        try {
            Double finalswc = Double.valueOf(hydrobal_line_parts[op_index.get(cc_size) + 7]);
            //System.out.println("finalswc: " + finalswc);
            finalswc = isUSUnits() ? finalswc * 0.03937 : finalswc;
            setRowValue(row, COLUMN_FINALSWC, finalswc);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing Final SWC.");
        }
        try {
            Double deltaswc = Double.valueOf(hydrobal_line_parts[op_index.get(cc_size) + 3])
                    - Double.valueOf(hydrobal_line_parts[op_index.get(cc_size) + 7]);
            //System.out.println("deltaswc: " + deltaswc);
            deltaswc = isUSUnits() ? deltaswc * 0.03937 : deltaswc;
            setRowValue(row, COLUMN_DELTASWC, deltaswc);
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing Delta SWC.");
        }
    }

    /* This method is responsible for determining the date range of the specified cover crop */
 /* 
    input: date_index, dateString determine what dates are required to gather the correct information from season.out,
    the return value s_out tells the calling object where in the season.out file to begin looking for correct fields;
    each iteration of the for loop of the calling object specifies a new index to begin getting data from; cc_size is the index of the current
    for loop, which corresponds to the index of the ArrayList of dates to determine what date to find in the season.out file;
    because the dates are different from the hydrobal.out and season.out the year is swapped in order to match up the dates correctly,
    so for instance if a year in season.out is 180 it will be swapped out with the corresponding year pertaining to the hydrobal.out file;
    in order to prevent the same year being used on multiple instance, the index of the years are collected and compared against the index of 
    the year in question to insure that the same year and data from season.out are not used multiple times for the differnt data in the hydrobal file. 
     */
    private int checkz = 0;

    public int checkZero() {
        return checkz;
    }

    public void setcheckZero(int i) {
        checkz = i;
    }

    private int getDateRange(DateFormat terminateDateFormat, Object[] row, String[] season_line_parts,
            String[] hydrobal_line_parts, int s_out, ArrayList<Integer> date_index, int cc_size,
            ArrayList<Integer> op_index, ArrayList<String> dateStrings,
            String dateString, ArrayList<Integer> used_index) throws SQLException {

        //FIND INDEX FOR DATA CORRESPONDING TO SEASON.OUT DATE MATCH
        String season_date = null;
        String set_date = null;
        int zero_check = 2;
        while (s_out < season_line_parts.length) {
            set_date = season_line_parts[s_out];
            //System.out.println(set_date);
            if (isValidDate(set_date)) {
                try {
                    Date sDate = terminateDateFormat.parse(set_date);
                    Date hDate = terminateDateFormat.parse(dateStrings.get(cc_size));
                    //System.out.println("checking... " + set_date + " " + dateStrings.get(cc_size));
                    if (sDate.equals(hDate) && !used_index.contains(s_out)) {
                        season_date = season_line_parts[s_out].trim();
                        used_index.add(s_out);
                        break;
                    }
                } catch (ParseException ex) {
                    Exceptions.printStackTrace(ex);
                }
            }
            s_out++;
        }
        try {
            dateString = hydrobal_line_parts[op_index.get(cc_size) - 1].trim();
            Date terminateDate = terminateDateFormat.parse(dateString);
            endDateX = terminateDate;
            setRowValue(row, COLUMN_TERMINATEDATE, new java.sql.Date(terminateDate.getTime()));
        } catch (ParseException pe) {
            LOGGER.error("Error parsing terminate date.", pe);
        }
        //addition of PREVIOUS date for plant/harvest
        try {
            if ((date_index.get(cc_size) - 1) <= 0) {
                for (int k = hydrobal_line_parts.length - 1; k > 0; k--) {
                    if (isValidDate(hydrobal_line_parts[k].trim())) {
                        currentDate = hydrobal_line_parts[k].trim();
                        break;
                    }
                }
            } else {
                for (int x = date_index.get(cc_size) - 1; x >= 0; x--) {
                    if (isValidDate(hydrobal_line_parts[x].trim())) {
                        currentDate = hydrobal_line_parts[x].trim();
                        break;
                    }
                }
            }

            Date terminateDate2 = terminateDateFormat.parse(currentDate);
            startDateX = terminateDate2;
            setRowValue(row, COLUMN_PREVIOUSDATA, new java.sql.Date(terminateDate2.getTime()));
            //System.out.println("X2");
        } catch (ParseException pe) {
            LOGGER.error("Error parsing terminate date.", pe);
        }
        try {
            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, dur);

            } else {
                int dur = (eday - sday);
                setRowValue(row, COLUMN_DURATION, dur);
            }
        } catch (NumberFormatException nfe) {
            LOGGER.error("Error parsing terminate date.", nfe);
        }
        return s_out;
    }

    //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;
    }
}
