package usda.weru.mcrew;

import java.awt.*;
import javax.swing.*;
import java.util.*;

import java.awt.event.*;
import com.klg.jclass.table.*;
import com.klg.jclass.table.data.JCEditableVectorDataSource;
import org.apache.log4j.Logger;
import usda.weru.util.Caster;

/**
 * A basic implementation of the JDialog class.
 */
public class DisplayCalendar extends usda.weru.mcrew.gui.Calendar_n implements ComponentListener {

    private static final long serialVersionUID = 1L;

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

    /**
     *
     */
    public static final int OK_OPTION = 0;

    /**
     *
     */
    public static final int CANCEL_OPTION = 1;
    private int c_result = CANCEL_OPTION;

    /**
     *
     * @return
     */
    public int getResult() {
        return c_result;
    }
    private JCEditableVectorDataSource dataSource;
    /**
     * The table that holds the values for the various crop & operation data
     */
//	GregorianCalendar curDate;

    public JulianCalendar curDate = null;
    // These variables are used in UpdateCalendar() and need to be initialiized here
    int yr = 0;
    int daySet = 0;
    int desiredDay = 0;
    boolean flag = true;
    boolean longMonth = false;
    
    //this variable is used to determine which class is using the calander 
    //in order to determine which year range should be used
    String user;

    /**
     * The constructor for display calender object. It creates the tables and the grids
     * that holds the days of the month for each month forming the actual yearly calendar.
     */
    public DisplayCalendar() {
        super(new JFrame());
        setTitle("Rotation Calendar");
        makeTable();

    }

    /**
     * Two argument contructor that helps build the calendar GUI which accepts
     * the value in date format in the table cell when clicked from this GUI
     * containing days of the month set under the weekday headers for all the
     * months in a year.
     * @param top The frame that holds this GUI.
     * @param gc The gregorian(new) calendar object used to correlate between julian(old)
     * calendar dates & events with gregorian version of calendar which is used
     * as a reference calendar.
     */
    public DisplayCalendar(JFrame top, GregorianCalendar gc, String user) {
 
        super(top);
        this.user = user;
        curDate = (JulianCalendar) gc.clone();
        int mox = gc.get(Calendar.MONTH);
        int yrx = gc.get(Calendar.YEAR);
        JP_calendar.addComponentListener(this);
        setTitle("Rotation Calendar");
        makeTable();
        JCB_month.setSelectedIndex(mox);
        JTF_year.setText("" + yrx);
        lastYrStr = Integer.toString(yrx);
        updateCalendar(mox, "" + yrx);
        setModal(true);
//		setVisible(true);
        //}}
    }

    /**
     * This method controls the visibility of the calendar GUI.
     * @param b Visible if true else hidden
     */
    @Override
    public void setVisible(boolean b) {
        c_result = CANCEL_OPTION;
        super.setVisible(b);
    }

    /**
     * This method displays the calendar GUI if run as a separate application in
     * which this becomes the starting point.
     * @param args The parameter needed if anything needs to be initialised from
     * command prompt
     */
    static public void main(String[] args) {
        try {
            JulianCalendar jc = new JulianCalendar();
            DisplayCalendar dc = new DisplayCalendar(new JFrame("DisplayCalendar"), jc, "");
            dc.setModal(true);
            dc.setVisible(true);
            //System.out.println("DC_m: " + jc);
        } catch (java.lang.Exception e) {
            LOGGER.error("Unexpected error.", e);
        }
    }

    @Override
    public void JLMonthDown_mouseClicked(java.awt.event.MouseEvent event) {
        int monidx = JCB_month.getSelectedIndex();
        if (monidx == 0) {
            monidx = 11;
            try {
                int yr = Integer.parseInt(JTF_year.getText().trim());
                yr--;
                if (this.user.equals("sim")) {
 
                } else {
                    yr = boundYear(yr);
                }
                JTF_year.setText("" + yr);
            } catch (NumberFormatException e) {
                LOGGER.warn("Unable to parse Integer: " + JTF_year.getText().trim(), e);
            }
        } else {
            monidx--;
        }
        JCB_month.setSelectedIndex(monidx);
        updateCalendar(monidx, JTF_year.getText().trim());
    }

    @Override
    public void JLMonthUp_mouseClicked(java.awt.event.MouseEvent event) {
        int monidx = JCB_month.getSelectedIndex();
        if (monidx == 11) {
            monidx = 0;
            try {
                int yr = Integer.parseInt(JTF_year.getText().trim());
                yr++;
                if (this.user.equals("sim")) {
 
                } else {
                    yr = boundYear(yr);
                }
                JTF_year.setText(" " + yr);
            } catch (NumberFormatException e) {
                LOGGER.warn("Unable to parse Integer: " + JTF_year.getText().trim(), e);
            }
        } else {
            monidx++;
        }

        JCB_month.setSelectedIndex(monidx);
        updateCalendar(monidx, JTF_year.getText().trim());
    }

    @Override
    public void JLYearDown_mouseClicked(java.awt.event.MouseEvent event) {
        try {
            int yr = Integer.parseInt(JTF_year.getText().trim());
            yr--;
            if (this.user.equals("sim")) {

            } else {
                yr = boundYear(yr);
            }
            JTF_year.setText("" + yr);
            int monidx = JCB_month.getSelectedIndex();
            updateCalendar(monidx, JTF_year.getText().trim());
        } catch (NumberFormatException e) {
            LOGGER.warn("Unable to parse Integer: " + JTF_year.getText().trim(), e);
        }

    }

    private int boundYear(int year) {
        if (year > 99) {
            return 99;
        } else if (year < 1) {
            return 1;
        } else {
            return year;
        }
    }

    String lastYrStr = "";

    @Override
    public void JTFYear_actionPerformed(java.awt.event.ActionEvent event) {
        try {
            int monidx = JCB_month.getSelectedIndex();
            int yr = Integer.parseInt(JTF_year.getText().trim());
            if (this.user.equals("sim")) {

            } else {
                yr = boundYear(yr);
            }
            String yrStr = Integer.toString(yr);
            updateCalendar(monidx, yrStr);
            lastYrStr = yrStr;
            if (!JTF_year.getText().equals(yrStr)) {
                JTF_year.setText(yrStr);
            }
        } catch (Exception e) {
            JTF_year.setText(lastYrStr);
        }
    }

    @Override
    public void JTFYear_focusLost(java.awt.event.FocusEvent event) {
        if (event.isTemporary()) {
            return;
        }
        JTFYear_actionPerformed(null);
    }

    @Override
    public void JLYearUp_mouseClicked(java.awt.event.MouseEvent event) {
        try {
            int yr = Integer.parseInt(JTF_year.getText().trim());
            yr++;
            if (this.user.equals("sim")) {

            } else {
                yr = boundYear(yr);
            }
            JTF_year.setText(" " + yr);
            int monidx = JCB_month.getSelectedIndex();
            updateCalendar(monidx, JTF_year.getText().trim());
        } catch (NumberFormatException e) {
            LOGGER.warn("Unable to parse Integer: " + JTF_year.getText().trim(), e);
        }

    }

    @Override
    public void JCB_month_itemStateChanged(java.awt.event.ItemEvent event) {
        if (event.getStateChange() != java.awt.event.ItemEvent.SELECTED) {
            return;
        }
        ////System.out.println("NC_miSC: " + event);
        int monidx = JCB_month.getSelectedIndex();
        //  //System.out.println("Month Selected:"+monidx);
        JCB_month.setSelectedIndex(monidx);
        updateCalendar(monidx, JTF_year.getText().trim());

    }

    private void setColumnWidths() {
        double width = jcTable.getWidth() / (double) 7;
        int fix = jcTable.getWidth() - (int) width * 7;
        for (int cdx = 0; cdx < 7; cdx++) {
            if (cdx == 0) {
                jcTable.setPixelWidth(cdx, (int) (width + fix));
            } else {
                jcTable.setPixelWidth(cdx, (int) width);
            }
        }
    }

    private void setRowHeights() {
        jcTable.setPixelHeight(-1, 25);

        double height = (jcTable.getHeight() - jcTable.getPixelHeight(-1)) / (double) 6;
        int fix = (jcTable.getHeight() - jcTable.getPixelHeight(-1)) - (int) height * 6;
        for (int cdx = 0; cdx < 6; cdx++) {
            if (cdx == 0) {
                jcTable.setPixelHeight(cdx, (int) (height + fix));
            } else {
                jcTable.setPixelHeight(cdx, (int) height);
            }
        }
    }
    //                       J   F  M   A    M    J    J   A   S    O
    private final int[] monArr = {0, // Jan
        31, // Feb
        59, // Mar
        90, // Apr
        120,// May
        151,// Jun
        181,// Jul
        212,// Aug
        243,// Sep
        273,// Oct
        304,// Nov
        334};// Dec

    private final int[] endDates = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    private int calcJulDate(int mon, int day, int yr) {

        int rtn = yr * 365 + ((yr - 1) / 4);
        rtn += monArr[mon];
        rtn += (((yr % 4) == 0) && mon > 1) ? 1 : 0;
        rtn += day - 1;

        return rtn;

    }

    private String getCellContents(int rdx, int cdx) {
        Vector<Vector<String>> cells = Caster.<Vector<Vector<String>>>cast(dataSource.getCells());
        Vector<String> row = cells.get(rdx);
        return row.get(cdx);
    }

    private void setSelectedCell() {
        String temp = "" + curDate.get(GregorianCalendar.DAY_OF_MONTH);
        Vector<Vector<String>> cells = Caster.<Vector<Vector<String>>>cast(dataSource.getCells());
        outerLoop:
        for (int rdx = 0; rdx < 6; rdx++) {
            Vector<String> row = cells.get(rdx);
            for (int cdx = 0; cdx < 7; cdx++) {
                String cellStr = row.get(cdx);
                // //System.out.println("DC_sSC: " + rdx + " " + cdx + " |" + cellStr + "|" + temp + "|");
                if (temp.equals(cellStr)) {
                    jcTable.setSelection(rdx, cdx, rdx, cdx);
                    jcTable.setCurrentCell(new JCCellPosition(rdx, cdx));
                    break outerLoop;
                }
            }
        }

    }

    private void updateCalendar(int monidx, String yrStr) {

        try {
            yr = Integer.parseInt(yrStr);
            if (this.user.equals("sim")) {

            } else {
                yr = boundYear(yr);
            }
        } catch (NumberFormatException e) {
            return;
        }
        // The Desired day is set to the first day already selected when the Calendar first comes up.
        if (flag == true) {
            flag = false;
            desiredDay = curDate.get(Calendar.DAY_OF_MONTH);
        }

        /** If day 31 is selected in March month and we go to April, then it gets changed to 30
         * Similarly if we change to Feb from a day > 28/29 we go to 28/29 depending on if its a
         * leap year.
         */
        daySet = curDate.get(Calendar.DAY_OF_MONTH);
        //System.out.println("Month is:"+monidx+" Day desired:"+desiredDay+" Day set:"+daySet);
        int leapYr = ((yr % 4) == 0) ? 1 : 0;
        if ((monidx == 3) || (monidx == 5) || (monidx == 8) || (monidx == 10)) {
            longMonth = false;
        } else {
            longMonth = true;
        }

        if (longMonth == true) {
            if (desiredDay == 31 || daySet != desiredDay) {
                curDate.set(Calendar.DAY_OF_MONTH, desiredDay);
            }
        } else {
            if (desiredDay == 31) {
                curDate.set(Calendar.DAY_OF_MONTH, 30);
            } else if (daySet != desiredDay) {
                curDate.set(Calendar.DAY_OF_MONTH, desiredDay);
            }

        }
        //The settings for the month February are set at the end, after bypassing all other conditions
        if (monidx == 1 && desiredDay > 28) {
            if (leapYr == 1) {
                curDate.set(Calendar.DAY_OF_MONTH, 29);
            } else {
                curDate.set(Calendar.DAY_OF_MONTH, 28);
            }
        }

        for (int rdx = 0; rdx < 6; rdx++) {
            for (int cdx = 0; cdx < 7; cdx++) {
                dataSource.setTableDataItem("", rdx, cdx);
            }
        }

        int sdow = calcJulDate(monidx, 1, yr) % 7;
        int edom = endDates[monidx];
        if (((yr % 4) == 0) && monidx == 1) {
            edom++;
        }
        for (int ddx = 0; ddx < edom; ddx++) {
            int rdx = (ddx + sdow) / 7;
            int cdx = (ddx + sdow) % 7;
            dataSource.setTableDataItem("" + (ddx + 1), rdx, cdx);
        }
        setSelectedCell();
    }

    private void makeTable() {
        int cntCols = 7;
        int cntRows = 6;

        String[][] tableData = new String[cntRows][];

        for (int rdx = 0; rdx < cntRows; rdx++) {
            String[] rowStr = new String[cntCols];
            for (int cdx = 0; cdx < cntCols; cdx++) {
                rowStr[cdx] = "";
            }
            tableData[rdx] = rowStr;
        }

        JP_calendar.setLayout(new java.awt.GridLayout());
        // Create table object

        jcTable.addMouseListener(new MyMouseAdapter(this));

        // Create a vector data source to contain our data
        dataSource = new JCEditableVectorDataSource();

        jcTable.setColumnLabelDisplay(true);
        jcTable.setRowLabelDisplay(false);
        jcTable.setDataSource(dataSource);
        jcTable.setTrackCursor(false);

        dataSource.setNumRows(cntRows);
        dataSource.setNumColumns(cntCols);
        dataSource.setColumnLabels(new String[]{"Sun", "Mon", "Tue", "Wed",
            "Thu", "Fri", "Sat"});

        dataSource.setCells(tableData);

        jcTable.setDoubleBuffered(true);
        jcTable.setAutoScroll(JCTableEnum.AUTO_SCROLL_BOTH);
        jcTable.setAllowCellResize(JCTableEnum.RESIZE_NONE);

        JCCellStyle cs = new JCCellStyle(jcTable.getDefaultLabelStyle());
        cs.setBackground(new Color(255, 255, 210));
        cs.setForeground(Color.black);
        cs.setHorizontalAlignment(JCTableEnum.CENTER);
        cs.setVerticalAlignment(JCTableEnum.CENTER);
        cs.setClipHints(JCTableEnum.SHOW_NONE);
        cs.setCellBorderSides(JCTableEnum.BORDERSIDE_ALL);
        cs.setEditable(false);
        cs.setTraversable(true);

        jcTable.setCellStyle(JCTableEnum.ALLCELLS, JCTableEnum.ALLCELLS, cs);

        JCCellStyle headerStyleModel = new JCCellStyle(jcTable.getDefaultLabelStyle());
        headerStyleModel.setFont(new Font("Dialog", Font.BOLD, 11));
        headerStyleModel.setHorizontalAlignment(JCTableEnum.CENTER);
        headerStyleModel.setVerticalAlignment(JCTableEnum.CENTER);
        headerStyleModel.setCellBorderSides(JCTableEnum.BORDERSIDE_ALL);
        jcTable.setCellStyle(-1, JCTableEnum.ALLCELLS, headerStyleModel);
        jcTable.setVertSBDisplay(JCTableEnum.SBDISPLAY_NEVER);
        jcTable.setHorizSBDisplay(JCTableEnum.SBDISPLAY_NEVER);

        jcTable.addTraverseCellListener(new MyTraverseCellListener());

        setColumnWidths();
        setRowHeights();

        jcTable.setSelectionPolicy(JCTableEnum.SELECT_SINGLE);

        JP_calendar.add(jcTable);
        repaint();
    }

    class MyTraverseCellListener implements JCTraverseCellListener {

        private void setSelectedCell() {
            String temp = "" + curDate.get(GregorianCalendar.DAY_OF_MONTH);
            Vector<Vector<String>> cells = Caster.<Vector<Vector<String>>>cast(dataSource.getCells());
            outerLoop:
            for (int rdx = 0; rdx < 6; rdx++) {
                Vector<String> row = cells.get(rdx);
                for (int cdx = 0; cdx < 7; cdx++) {
                    String cellStr = row.get(cdx);
                    //   //System.out.println("DC_sSC: " + rdx + " " + cdx + " |" + cellStr + "|" + temp + "|");
                    if (temp.equals(cellStr)) {
                        jcTable.setSelection(rdx, cdx, rdx, cdx);
                        jcTable.setCurrentCell(new JCCellPosition(rdx, cdx));
                        break outerLoop;
                    }
                }
            }

        }

        @Override
        public void afterTraverseCell(JCTraverseCellEvent e) {
            if (e.isCancelled()) {
                //setSelectedCell();
                return;
            }
//			setSelectedCell();
        }

        @Override
        public void traverseCell(JCTraverseCellEvent e) {
            if (e.getNextRow() < 0) {
                e.setCancelled(true);
                return;
            }

            if (getCellContents(e.getNextRow(), e.getNextColumn()).trim().equals("")) {
                e.setCancelled(true);
                return;
            }
        }
    }

    @Override
    public void OK_actionPerformed(java.awt.event.ActionEvent event) {
        Collection<JCCellRange> scc = Caster.<Collection<JCCellRange>>cast(jcTable.getSelectedCells());
        JCCellRange jccr = (JCCellRange) scc.toArray()[0];

        try {
            curDate.set(Calendar.ERA, GregorianCalendar.AD);
            curDate.set(Calendar.MONTH, JCB_month.getSelectedIndex());
            curDate.set(Calendar.DAY_OF_MONTH, Integer.parseInt((String) dataSource.getTableDataItem(jccr.start_row, jccr.start_column)));
            curDate.set(Calendar.YEAR, Integer.parseInt(JTF_year.getText().trim()));
            c_result = OK_OPTION;
            dispose();
        } catch (NumberFormatException e) {
            JOptionPane.showMessageDialog(null, "Error parsing day or year",
                    "Error updating date", JOptionPane.ERROR_MESSAGE);
        }

    }

    @Override
    public void Cancel_actionPerformed(java.awt.event.ActionEvent event) {
        c_result = CANCEL_OPTION;
        curDate = null;
        dispose();
    }

    /**
     *
     * @param e
     */
    @Override
    public void componentResized(ComponentEvent e) {
        setColumnWidths();
        setRowHeights();
    }

    /**
     *
     * @param e
     */
    @Override
    public void componentMoved(ComponentEvent e) {
    }

    /**
     *
     * @param e
     */
    @Override
    public void componentShown(ComponentEvent e) {
    }

    /**
     *
     * @param e
     */
    @Override
    public void componentHidden(ComponentEvent e) {
    }

    /**
     * This is an inner class that recognizes the mouse click events from the calendar GUI to
     * get the selected dates in a date format to be assigned to a specific cell in the date
     * column in the main MCREW table.
     */
    public class MyMouseAdapter extends MouseAdapter {

        DisplayCalendar nc = null;

        MyMouseAdapter(DisplayCalendar nc) {
            this.nc = nc;
        }

        /**
         * The method that recognises the mouse events from the calendar GUI to
         * get the selected dates.
         * @param ev The event generated from the mouse click.
         */
        @Override
        public void mouseClicked(MouseEvent ev) {
            Collection<JCCellRange> scc = Caster.<Collection<JCCellRange>>cast(nc.jcTable.getSelectedCells());
            JCCellRange jccr = (JCCellRange) scc.toArray()[0];

            if (ev.getModifiers() == InputEvent.BUTTON1_MASK && ev.getClickCount() > 1) //if(ev.getModifiers() == InputEvent.BUTTON1_MASK)
            {
                try {
                    String value = (String) nc.dataSource.getTableDataItem(jccr.start_row, jccr.start_column);
                    if (value == "") {
                        int monidx = JCB_month.getSelectedIndex();
                        updateCalendar(monidx, JTF_year.getText().trim());
                        //System.out.println("Double Click:Empty selection");
                        return;
                    }
                    if (curDate.get(Calendar.ERA) == GregorianCalendar.BC) {
                        curDate.set(Calendar.ERA, GregorianCalendar.AD);
                    }
                    curDate.set(Calendar.MONTH, JCB_month.getSelectedIndex());
                    curDate.set(Calendar.DAY_OF_MONTH, Integer.parseInt((String) nc.dataSource.getTableDataItem(jccr.start_row, jccr.start_column)));
                    desiredDay = Integer.parseInt((String) nc.dataSource.getTableDataItem(jccr.start_row, jccr.start_column));
                    //System.out.println("Date:"+curDate.get(Calendar.DAY_OF_MONTH)+"Gregorian:"+curDate.get(GregorianCalendar.DAY_OF_MONTH));
                    curDate.set(Calendar.YEAR, Integer.parseInt(JTF_year.getText().trim()));
                    OK_actionPerformed(null);
                    nc.dispose();
                } catch (NumberFormatException e) {
                    JOptionPane.showMessageDialog(null, "Error parsing day or year",
                            "Error updating date", JOptionPane.ERROR_MESSAGE);
                    curDate.set(Calendar.DAY_OF_MONTH, desiredDay);
                }
            } // If only the date is changed with a single mouse click,then this selection should be
            // preserved so that if we change the month after this, the date change will be preserved.
            else if (ev.getModifiers() == InputEvent.BUTTON1_MASK && ev.getClickCount() == 1) {

                try {
                    String value = (String) nc.dataSource.getTableDataItem(jccr.start_row, jccr.start_column);
                    if (value == "") {
                        int monidx = JCB_month.getSelectedIndex();
                        updateCalendar(monidx, JTF_year.getText().trim());
                        //System.out.println("Single Click:Empty selection");
                        return;
                    }
                    if (curDate.get(Calendar.ERA) == GregorianCalendar.BC) {
                        curDate.set(Calendar.ERA, GregorianCalendar.AD);
                    }
                    curDate.set(Calendar.DAY_OF_MONTH, Integer.parseInt((String) nc.dataSource.getTableDataItem(jccr.start_row, jccr.start_column)));
                    desiredDay = Integer.parseInt((String) nc.dataSource.getTableDataItem(jccr.start_row, jccr.start_column));
                    jcTable.setCurrentCell(new JCCellPosition(jccr.start_row, jccr.start_column));
                } catch (NumberFormatException e) {
                    JOptionPane.showMessageDialog(null, "Error parsing day or year",
                            "Error updating date", JOptionPane.ERROR_MESSAGE);
                    curDate.set(Calendar.DAY_OF_MONTH, desiredDay);
                }

            } else {
                jcTable.setCurrentCell(new JCCellPosition(jccr.start_row, jccr.start_column));
                //System.out.println("Mouse clicked");
            }
        }
    }

}
