/**
 * 
 */
package anoncard;

import mixconfig.tools.dataretention.smartcard.ApduConstants;
import javacard.framework.ISOException;

/**
 * This class saves the current date. It is meant to be reseted on applet
 * selection, so it can throw Exceptions, if not initialized.
 * 
 * @author christoph
 * 
 */
public class Date {

	/**
	 * The day.
	 */
	private byte day = -0x01;

	/**
	 * The month.
	 */
	private byte month = -0x01;

	/**
	 * The year, first the left two digits then the right two digits.
	 * 
	 * i.e.:
	 * 
	 * 2010 = { 20, 10 } = { 0x14, 0x0A }
	 */
	private byte[] year = null;

	/**
	 * @return the day
	 */
	public byte getDay() {
		checkInitialisationAndThrowException();
		return day;
	}

	/**
	 * @return the month
	 */
	public byte getMonth() {
		checkInitialisationAndThrowException();
		return month;
	}

	/**
	 * @return the year
	 */
	public byte[] getYear() {
		checkInitialisationAndThrowException();
		return year;
	}

	/**
	 * 
	 * @return -the date [DMYY]
	 */
	public byte[] getDate() {
		checkInitialisationAndThrowException();
		return new byte[] { day, month, year[0], year[1] };
	}

	/**
	 * Unsets the date.
	 */
	public void reset() {
		day = -0x01;
		month = -0x01;
		year = null;
	}

	/**
	 * Returns true, iff the date has been set.
	 * 
	 * @return
	 */
	public boolean isSet() {
		return (day > 0 && month > 0 && year != null);
	}

	/**
	 * Throws an ISOException, when the date is not set.
	 * 
	 * @see ISOException#throwIt(short)
	 * @see #isSet()
	 */
	public void checkInitialisationAndThrowException() {
		if (!isSet()) {
			ISOException.throwIt(ApduConstants.EXCEPTION_DATE_NOT_SET);
		}
	}

	/**
	 * @param day
	 *            the day to set
	 */
	public void setDay(byte day) {
		this.day = day;
	}

	/**
	 * @param month
	 *            the month to set
	 */
	public void setMonth(byte month) {
		this.month = month;
	}

	/**
	 * Sets the year bytes to year[offset], year[offset+1], iff year is long
	 * enough.
	 * 
	 * @param year
	 * @param offset
	 */
	public void setYear(byte[] year, short offset) {
		if ((short) (year.length - offset) >= 2) {
			this.year = new byte[2];
			this.year[0] = year[offset];
			this.year[1] = year[(short) (offset + 1)];
		}
	}

	/**
	 * Wrapper for {@link #setDate(byte[], short)}
	 * 
	 * @param date
	 *            - {D, M, Y, Y}, i.e.: 27.01.2010 = {27, 01, 20, 10}
	 */
	public void setDate(byte[] date) {
		setDate(date, (short) 0);
	}

	/**
	 * Sets the date, iff date.length - offset >= 4
	 * 
	 * @param date
	 *            - {..., D, M, Y, Y}, i.e.: 27.01.2010 = {.., 27, 01, 20, 10}
	 * @param offset
	 *            - position of D in date
	 */
	public void setDate(byte[] date, short offset) {
		if ((short)(date.length - offset) >= 4) {
			day = date[offset];
			month = date[(short) (offset + 1)];
			year = new byte[] { date[(short) (offset + 2)], date[(short) (offset + 3)] };
		}
	}

	/**
	 * Returns true, iff the Date is set and the old date is not more then
	 * months months ago. Calls {@link #checkInitialisationAndThrowException()}
	 * .
	 * 
	 * @param months
	 *            - the maximal number of months allowed to be passed since the
	 *            old date
	 * @param oldDate
	 *            - an array containing the old date [DMYY], where D is at
	 *            position offset
	 * @param offset
	 *            - the offset, where the date stands in the array oldDate
	 * @return
	 */
	public boolean isNotOutdated(byte maximalPassedMonths, byte[] oldDate, short offset) {
		checkInitialisationAndThrowException();
		// TODO: could also throw an IllegalArgumentException
		if ((short) (oldDate.length - offset) < 4 || maximalPassedMonths < 0) {
			return false;
		}
		byte maximalMonthValue = (byte) (oldDate[(short) (offset + 1)] + maximalPassedMonths - 1);
		byte maximalYear1Value = (byte) (oldDate[(short) (offset + 3)] + maximalMonthValue / 12);
		byte maximalYear0Value = (byte) (oldDate[(short) (offset + 2)] + maximalYear1Value / 100);
		maximalMonthValue = (byte) (maximalMonthValue % 12 + 1);
		maximalYear1Value %= 100;

		if (year[0] < maximalYear0Value) {
			return true;
		} else if (year[0] == maximalYear0Value && year[1] < maximalYear1Value) {
			return true;
		} else if (year[1] == maximalYear1Value && month < maximalMonthValue) {
			return true;
		} else if (month == maximalMonthValue && day <= oldDate[offset]) {
			return true;
		}

		return false;
	}
}
