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;

/**
 * A basic implementation of the JDialog class.
 */
public class DisplayCalendar extends usda.weru.mcrew.gui.Calendar_n implements ComponentListener {
    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;
    
    public int getResult(){
        return c_result;
    }
    private JCEditableVectorDataSource dataSource;
    /**
     * The table that holds the values for the various crop & operation data
     */
//	GregorianCalendar curDate;
    
    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;
    
    
    /**
     * 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) {
        super(top);
        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
     */
    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);
        }
    }
    
    public void JLMonthDown_mouseClicked(java.awt.event.MouseEvent event) {
        // to do: code goes here.
        int monidx = JCB_month.getSelectedIndex();
        if (monidx == 0) {
            monidx = 11;
            try {
                int yr = Integer.parseInt(JTF_year.getText().trim());
                yr--;
                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());
    }
    
    public void JLMonthUp_mouseClicked(java.awt.event.MouseEvent event) {
        // to do: code goes here.
        int monidx = JCB_month.getSelectedIndex();
        if (monidx == 11) {
            monidx = 0;
            try {
                int yr = Integer.parseInt(JTF_year.getText().trim());
                yr++;
                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());
    }
    
    public void JLYearDown_mouseClicked(java.awt.event.MouseEvent event) {
        // to do: code goes here.
        try {
            int yr = Integer.parseInt(JTF_year.getText().trim());
            yr--;
            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 = "";
    public void JTFYear_actionPerformed(java.awt.event.ActionEvent event) {
        // to do: code goes here.        
        try{
            int monidx = JCB_month.getSelectedIndex();
            int yr = Integer.parseInt(JTF_year.getText().trim());            
            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);
        }
    }
    
    public void JTFYear_focusLost(java.awt.event.FocusEvent event) {
        // to do: code goes here.
        if (event.isTemporary()) return;
        JTFYear_actionPerformed(null);        
    }
    
    public void JLYearUp_mouseClicked(java.awt.event.MouseEvent event) {
        // to do: code goes here.
        try {
            int yr = Integer.parseInt(JTF_year.getText().trim());
            yr++;
            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);
        }
        
    }
    
    public void JCB_month_itemStateChanged(java.awt.event.ItemEvent event) {
        // to do: code goes here.
        
        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 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 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 cells = dataSource.getCells();
        Vector row = (Vector) cells.get(rdx);
        return (String) row.get(cdx);
    }
    
    private void setSelectedCell() {
        String temp = "" + curDate.get(GregorianCalendar.DAY_OF_MONTH);
        Vector cells = dataSource.getCells();
        outerLoop: for (int rdx = 0; rdx < 6; rdx++) {
            Vector row = (Vector) cells.get(rdx);
            for (int cdx = 0; cdx < 7; cdx ++) {
                String cellStr = (String) 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);
            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 cells = dataSource.getCells();
            outerLoop: for (int rdx = 0; rdx < 6; rdx++) {
                Vector row = (Vector) cells.get(rdx);
                for (int cdx = 0; cdx < 7; cdx ++) {
                    String cellStr = (String) 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;
                    }
                }
            }
            
        }
        public void afterTraverseCell(JCTraverseCellEvent e) {
            if (e.isCancelled()) {
                //setSelectedCell();
                return;
            }
//			setSelectedCell();
        }
        
        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;
            }
        }
    }
    
    
    public void OK_actionPerformed(java.awt.event.ActionEvent event) {
        // to do: code goes here.
        
        Collection scc = 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 );
        }
        
    }
    
    
    public void Cancel_actionPerformed(java.awt.event.ActionEvent event) {
        // to do: code goes here.
        c_result = CANCEL_OPTION;
        curDate = null;
        dispose();
    }
    
    public void componentResized(ComponentEvent e) {
        setColumnWidths();
        setRowHeights();
    }
    
    public void componentMoved(ComponentEvent e) {
    }
    
    public void componentShown(ComponentEvent e) {
    }
    
    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.
         */
        public void mouseClicked(MouseEvent ev) {
            Collection scc = 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");
            }
        }
    }
    
    
    
}
