package usda.weru.mcrew;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.StringTokenizer;


/**
 * This class is responsible for getting the dates or forming the date string in the
 * Julian calendar year format
 */
// public class JulianCalendar extends GregorianCalendar implements Comparable<Calendar>
public class JulianCalendar extends GregorianCalendar
{
    private static final long serialVersionUID = 1L;
    
    /**
     * De-limiter thats used to separate the month,day and year in a date string
     */    
	public static final String
		sDateDelimeter = "/"
		;
		
    /**
     * Location of the month in a date string for the julian calendar to understand.
     */
    public static final int kMonthField = 1;
    
    /**
     * Location of the day in a date string to be used in the julian calendar.
     */
    public static final int kDayField = 0;
    
    /**
     * Location of the year in a date string to be used in the julian calendar.
     */
    public static final int kYearField = 2;
    
    /**
     * The dates are in month/day/year format in the XML files
     */    
	public static final int
		 
		BC = GregorianCalendar.BC;
		;
	
	//private JulianDay mJulianDay; /* JulianDay unused presently */
	
        /**
         * Default constructor that initialises the month, day and year with predefined values
         * and sets the era.
         */        
	public JulianCalendar()
	{            
            super(UtilConstants.kDefaultYear, UtilConstants.kDefaultMonth, UtilConstants.kDefaultDay, 0, 0, 0);
            //mJulianDay = new JulianDay(UtilConstants.kDefaultYear, UtilConstants.kDefaultMonth, UtilConstants.kDefaultDate);
            set(Calendar.ERA, BC);
            setLenient(false);
            getDate();
		////System.out.println("Setting calendar to BC " + get(Calendar.ERA)); 
	}
	
    /**
     * The three argument constructor that sets the date values to the values passed as
     * arguments as month, day and year.
     * @param pYear The year value of the julian calendar date string.
     * @param pMonth The month value of the julian calendar date string.
     * @param pDay The day value of the julian calendar date string.
     */    
	public JulianCalendar(int pYear, int pMonth, int pDay)
	{
            super(pYear, pMonth, pDay, 0, 0, 0);
            getDate();
            setLenient(false);
		//mJulianDay = new JulianDay(pYear, pMonth, pDate);
	}
	
    /**
     * One argument constructor that sets the month,day and year values of the date string
     * based on the type of calendar passed as argument.
     * @param pCalendar The type of calendar to be used as a reference.
     */    
	public JulianCalendar(Calendar pCalendar)
	{
			super(UtilConstants.kDefaultYear, UtilConstants.kDefaultMonth, UtilConstants.kDefaultDay, 0, 0, 0);
			/* Call to super() has to be the first statement in the constructor. So we can't do,
			 * if(pCalendar == null)
			 *	super(...);
			 */
			setLenient(false);
			if(pCalendar != null)
			{
				set(Calendar.YEAR,	pCalendar.get(Calendar.YEAR));
				set(Calendar.MONTH,	pCalendar.get(Calendar.MONTH));
				set(Calendar.DATE,	pCalendar.get(Calendar.DATE));                                
			}
                        getDate();
		//mJulianDay = new JulianDay(pCalendar);
	}
	
    /**
     * Constructor that sets the month/day/year of the date string according to 
     * the programming rules i:e month -1 OR day -1 OR year -1 etc. from the calendar
     * based on mouse events that occur.
     * @param pCalStr The date string received from the calendar mouse click events.
     */    
	public JulianCalendar(String pCalStr)
	{
            super(0, 0, 0, 0, 0, 0);
            setLenient(false);
		setDate(pCalStr);
                getDate();
	}
// Resurrected "old" JulianCalendar code, but added two args to distinguish between previous definition to get a quick workaround for "calibration" problem - LEW

	/**
	 *
	 * @param pCalStr
	 * @param pCalStr2
	 */
		public JulianCalendar(String pCalStr, String pCalStr2)
	{
                super(0, 0, 0, 0, 0, 0);
		StringTokenizer tokenizer = new StringTokenizer(pCalStr, sDateDelimeter);
		
		int date[] = new int[3];
		int tokenCount = 0;
		while(tokenizer.hasMoreElements())
		{
			String token = tokenizer.nextToken().trim();
			date[tokenCount++] = (new Integer(token)).intValue();
		}
		
		date[kMonthField]--; // because the month values in man or xml files have index starting from 1 i.e Jan = 1
		// super(date[kYearField], date[kMonthField], date[kDayField]); // 2,1,0 -- final function
		super.set(Calendar.YEAR, date[kYearField]);
		super.set(Calendar.MONTH, date[kMonthField]);
		super.set(Calendar.DATE, date[kDayField]);
                getDate();
	}

                String date;
	/**
	 *
	 * @return
	 */
	public String getDate() {
            date = get(Calendar.DATE)+sDateDelimeter+(get(Calendar.MONTH)+1)+sDateDelimeter+get(Calendar.YEAR);
        return date;
    }
	
    /**
     * This method sets the date in the date cell of the table row to the date string
     * formed by the calendar from the events that occured during date selection.
     * @param pCalStr The date string from the calendar to be set.
     * @throws IllegalArgumentException Exceptin that occurs when the date string 
     * format is not the same as expected i:e maybe the tokenizer is different, etc.
     */    
	public void setDate(String pCalStr) throws java.lang.IllegalArgumentException
	{
		StringTokenizer tokenizer = new StringTokenizer(pCalStr, sDateDelimeter);
		if (tokenizer.countTokens() != 3) {
			throw new java.lang.IllegalArgumentException("Wrong token count " + pCalStr);
		}
		int date[] = new int[3];
		int tokenCount = 0;
		while(tokenizer.hasMoreElements())
		{
			String token = tokenizer.nextToken();
			date[tokenCount++] = (new Integer(token)).intValue();
		}
//		if                 
		date[kMonthField]--; // because the month values in man or xml files have index starting from 1 i.e Jan = 1
                if (date[kYearField] <= 0 || date[kYearField] > 99){
                    throw new IllegalStateException("Year out of bounds " + date[kYearField]);
                }
		// super(date[kYearField], date[kMonthField], date[kDayField]); // 2,1,0 -- final function
                super.set(date[kYearField], date[kMonthField], date[kDayField]);
	}
        
        public boolean feb29()
        {
            if(this.get(Calendar.MONTH) == Calendar.FEBRUARY
                    && this.get(Calendar.DATE) == 29) return true;
            else return false;
        }
        
        @Override
        public boolean after(Object when)
        {
            if(!(when instanceof JulianCalendar)) return false;
            JulianCalendar other = (JulianCalendar) when;
            if(this.get(Calendar.YEAR) > other.get(Calendar.YEAR)) return true;
            if(this.get(Calendar.YEAR) < other.get(Calendar.YEAR)) return false;
            if(this.get(Calendar.MONTH) > other.get(Calendar.MONTH)) return true;
            if(this.get(Calendar.MONTH) < other.get(Calendar.MONTH)) return false;
            if(this.get(Calendar.DATE) > other.get(Calendar.DATE)) return true;
            if(this.get(Calendar.DATE) < other.get(Calendar.DATE)) return false;
            return false;
        }
        
        @Override
        public boolean equals(Object when)
        {
            if(!(when instanceof JulianCalendar)) return false;
            JulianCalendar other = (JulianCalendar) when;
            if(this.get(Calendar.YEAR) != other.get(Calendar.YEAR)) return false;
            if(this.get(Calendar.MONTH) != other.get(Calendar.MONTH)) return false;
            if(this.get(Calendar.DATE) != other.get(Calendar.DATE)) return false;
            return true;
        }
        
        public JulianCalendar cloneDate()
        {
            return new JulianCalendar(get(Calendar.YEAR), get(Calendar.MONTH), get(Calendar.DATE));
        }
        
        public JulianCalendar increment()
        {
            if(withinMonth(get(Calendar.MONTH), get(Calendar.DATE) + 1))
            {
                set(Calendar.DATE, get(Calendar.DATE) + 1);
                return this;
            }
            else
            {
                set(Calendar.DATE, 1);
                return this;
            }
        }
                
        public JulianCalendar increment(int input)
        {
            if(withinMonth(get(Calendar.MONTH), get(Calendar.DATE) + input))
            {
                set(Calendar.DATE, get(Calendar.DATE) + input);
                return this;
            }
            else
            {
                if(input < 0)
                {
                   
                    input += get(Calendar.DATE);
                    set(Calendar.MONTH, get(Calendar.MONTH) - 1);
                    input += dPM[get(Calendar.MONTH)];
                    set(Calendar.DATE, input);
                }
                else if(input > 0)
                {
                    input += get(Calendar.DATE);
                    input -= dPM[get(Calendar.MONTH)];
                    set(Calendar.MONTH, get(Calendar.MONTH) + 1);
                    set(Calendar.DATE, input);
                }
                return this;
            }
        }
        
        public JulianCalendar incrementYear(int numYears)
        {
            set(Calendar.YEAR, get(Calendar.YEAR) + numYears);
            getDate();
            return this;
        }
        
        public JulianCalendar decrementYear(int numYears)
        {
            int next = get(Calendar.YEAR) - numYears;
            set(Calendar.YEAR, next > 0 ? next : 1);
            getDate();
            return this;
        }
        
        private final int[] dPM = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
        public boolean withinMonth(int month, int day)
        {
            if(day < 1) return false;
            else
            {
                int totDays = dPM[month];
                if(day > totDays) return false;
                else return true;
            }
        }
	
	/*
	public JulianDay getJulianDay()
	{
		return mJulianDay;
	}
		
	public double getDifferenceDays(JulianCalendar pMCal)
	{
		return mJulianDay.diff(pMCal.getJulianDay());
	}
		
	public double getDifferenceDays(Calendar pCal)
	{
		return mJulianDay.diff(new JulianDay(pCal));
	}
			
	public double getDifferenceDays(JulianDay pJulianDay)
	{
		return mJulianDay.diff(pJulianDay);
	}

	*/
	
    /**
     * This method converts the numeric date to a string formatted date.
     * @return The new date in a string object format.
     */    
    @Override
	public String toString()
	{
		String retStr;
		
		/*
		String year = (new Integer(mJulianDay.get(YEAR))).toString();
		String month = (new Integer(mJulianDay.get(MONTH) + 1)).toString(); 
		String day = (new Integer(mJulianDay.get(DAY_OF_MONTH))).toString();
		*/
		
		String year = Integer.toString(get(YEAR));
		String month = Integer.toString(get(MONTH) + 1);
		// +1 becuase this program works with 0 based index whereas other programs using man files work with 1 based index
		String day = Integer.toString(get(DAY_OF_MONTH));
		
		
		/* WEPS needs all values in a 2 char string */
		/* To be REPLACED with NumberFormat later */
		if(year.length() < 2)
			year = "0".concat(year);
		if(month.length() < 2)
			month = "0".concat(month);
		if(day.length() < 2)
			day = "0".concat(day);
			
		retStr = day + sDateDelimeter + month + sDateDelimeter + year;
			
		return retStr;
	}
}


   
