package ch.sahits.game.openpatrician.model;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.ResourceBundle;
import java.util.concurrent.CopyOnWriteArrayList;
import ch.sahits.game.event.TimeChangeEvent;
import ch.sahits.game.openpatrician.util.l10n.Locale;
/**
* Representation of the date within the game.
* This class is thread safe.
* The Date is implemented as a singleton. However there
* is no getInstance method to retrieve the once created instance,
* so it must be referenced elsewhere.
* @author Andi Hotz, (c) Sahits GmbH, 2011
* Created on Sep 16, 2011
*
*/
public class Date implements IClockUpdateListenable{
/** Tick update in minutes */
private int update;
/** Current date */
private final Calendar cal;
/** Start year */
private final int startYear;
/** This instance */
private static Date instance;
/** Lock for guaranteeing thread safty */
private static Object lock = new Object();
private CopyOnWriteArrayList<IClockUpdateListener> listeners = new CopyOnWriteArrayList<IClockUpdateListener>();
private ResourceBundle messages = ResourceBundle.getBundle("OpenPatricianMessages", Locale.getInstance().getCurrentLocal());
/**
* Initialize the date with start year.
* The initial date is 13th July of the start year.
* The default tick update is 5 minutes.
*/
private Date(int year){
startYear=year;
cal = GregorianCalendar.getInstance();
cal.setTimeInMillis(0);
cal.set(Calendar.YEAR, year);
cal.set(Calendar.DAY_OF_MONTH, 13);
cal.set(Calendar.MONTH, Calendar.JULY);
update = 5;
}
/**
* Constructor for testing purposes only
* @param cal
*/
Date(Calendar cal){
this.cal = cal;
startYear = cal.get(Calendar.YEAR);
update = 5;
}
/**
* Retrieve the singleton instance of the date.
* If the instance is not yet created, it will be
* created
* @param year
* @return
*/
public static Date getInstance(int year){
synchronized (lock) {
if (instance==null){
instance=new Date(year);
}
return instance;
}
}
// /**
// * Retrieve the singleton instance of this object.
// * If no such instance is initiated an {@link IllegalStateException}
// * will be thrown.
// * @return
// */
// public static Date getInstance(){
// if (instance!=null){
// return instance;
// }
// throw new IllegalStateException("Not yet initialized");
// }
/**
* Update the time by one tick.
*/
public void tick(){
synchronized (lock) {
int dayBefore = cal.get(Calendar.DATE);
cal.add(Calendar.MINUTE, update);
if (dayBefore!=cal.get(Calendar.DATE)){
new TimeChangeEvent().notify(null);
}
}
notifyTick();
}
/**
* Set the tick update in number of minutes
* @param minutes tick update
*/
public void setTickUpdate(int minutes){
update=minutes;
notifyTickUpdate();
}
/**
* Retrieve the start year
* @return the startYear
*/
public int getStartYear() {
return startYear;
}
// /**
// * @param field
// * @return
// * @see java.util.Calendar#get(int)
// */
// public int get(int field) {
// return cal.get(field);
// }
/**
* Retrieve the date of the start of the week in the form {Day of month}. {Month}.
* The week starts with Monday
* @return
*/
public String getStartOfWeek(){
Calendar lastMon = getStartOfWeekInternal();
return toShortDate(lastMon);
}
/**
* Retrieve the date of the start of the last week
* @return
*/
Calendar getStartOfWeekInternal() {
Calendar c = (Calendar) cal.clone();
// last week
c.add(Calendar.WEEK_OF_YEAR, -1);
// first day
c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek());
int cdow = cal.get(Calendar.DAY_OF_WEEK);
Calendar lastMon = (Calendar) cal.clone();
lastMon.roll(Calendar.DATE, -7 - (cdow - Calendar.MONDAY));
return lastMon;
}
/**
* Convert the date into the form {Day in month}. {Month}
* @param c
* @return
*/
private String toShortDate(Calendar c) {
final int day;
final int month;
synchronized (lock) {
day = c.get(Calendar.DAY_OF_MONTH);
month = c.get(Calendar.MONTH);
}
StringBuilder sb = new StringBuilder();
sb.append(day).append(". ");
sb.append(messages.getObject("month_short_"+month));
return sb.toString();
}
/**
* Retrieve the date of the end of the week in the form {Day of month}. {Month}.
* The week ends with Sunday
* @return
*/
public String getEndOfWeek(){
Calendar lastSun = getEndOfWeekInternal();
return toShortDate(lastSun);
}
/**
* Retrieve the date of the of the Sunday of last week
* @return
*/
Calendar getEndOfWeekInternal() {
int cdow = cal.get(Calendar.DAY_OF_WEEK);
Calendar lastMon = (Calendar) cal.clone();
lastMon.roll(Calendar.DATE, -7 - (cdow - Calendar.MONDAY));
Calendar lastSun = (Calendar) lastMon.clone();
lastSun.roll(Calendar.DATE, 6);
return lastSun;
}
/**
* Create a string representation for the user interface of the
* form {Day of month}. {Month} {Year}.
* @return
*/
public String toDisplayString(){
final int day;
final int month;
final int year;
synchronized (lock) {
day = cal.get(Calendar.DAY_OF_MONTH);
month = cal.get(Calendar.MONTH);
year = cal.get(Calendar.YEAR);
}
return todisplayString(day, month, year);
}
public String todisplayString(final int day, final int month, final int year) {
StringBuilder sb = new StringBuilder();
sb.append(day).append(". ");
sb.append(messages.getObject("month_short_"+month)).append(" ");
sb.append(year);
return sb.toString();
}
@Override
public void add(IClockUpdateListener listener) {
listeners.add(listener);
}
@Override
public void remove(IClockUpdateListener listener) {
listeners.remove(listener);
}
@Override
public void notifyTick() {
for (IClockUpdateListener listener : listeners) {
listener.ticked();
}
}
@Override
public void notifyTickUpdate() {
for (IClockUpdateListener listener : listeners) {
listener.updateTick(update);
}
}
/**
* Retrieve a copy of the current date
* @return
*/
public Calendar getCurrentDate(){
return (Calendar) cal.clone();
}
/**
* Retrieve the update interval
* @return
*/
public final int getUpdateInterval(){
return update;
}
/**
* Retrieve an index for the current weekday. Mondy is 0.
* @return
*/
public final int getWeekdayIndex(){
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
return (dayOfWeek-Calendar.MONDAY)%7;
}
}