package org.jblooming.agenda;
import org.jblooming.tracer.Tracer;
import java.util.*;
import net.sf.json.JSONObject;
public class ScheduleMonthly extends ScheduleSupport implements Schedule {
private int dayOfWeek;
private int weekOfMonth;
private ScheduleMonthly() {
}
public ScheduleMonthly(int dayOfWeek, int weekOfMonth, Date start, int duration, boolean onlyWorkingDays) {
this(dayOfWeek, weekOfMonth, start, duration, 1, 0, onlyWorkingDays);
}
public ScheduleMonthly(int dayOfWeek, int weekOfMonth, Date start, int duration, int freq, int rep, boolean onlyWorkingDays) {
this.dayOfWeek = dayOfWeek;
this.weekOfMonth = weekOfMonth;
this.setStart(start);
this.setDuration(duration);
this.setFreq((freq > 0 ? freq : 1));
this.setRepeat(rep);
this.setOnlyWorkingDays(onlyWorkingDays);
recalculateFields();
}
public ScheduleMonthly(Date start, int duration, int freq, int rep, boolean onlyWorkingDays) {
this.setStart(start);
this.setDuration(duration);
this.setFreq((freq > 0 ? freq : 1));
this.setRepeat(rep);
this.setOnlyWorkingDays(onlyWorkingDays);
recalculateFields();
}
public ScheduleMonthly(int dayOfWeek, int weekOfMonth, Date start, int startTime, int duration, boolean onlyWorkingDays) {
this(dayOfWeek, weekOfMonth, start, startTime, duration, 1, 0, onlyWorkingDays);
}
public ScheduleMonthly(int dayOfWeek, int weekOfMonth, Date start, int startTime, int duration, int freq, int rep, boolean onlyWorkingDays) {
this.dayOfWeek = dayOfWeek;
this.weekOfMonth = weekOfMonth;
this.setStartTime(startTime);
this.setStart(start);
CompanyCalendar cal = new CompanyCalendar();
cal.setTimeInMillis(this.getValidityStartTime());
cal.setMillisFromMidnight(startTime);
this.setStart(cal.getTime());
this.setDuration(duration);
this.setFreq((freq > 0 ? freq : 1));
this.setRepeat(rep);
this.setOnlyWorkingDays(onlyWorkingDays);
recalculateFields();
}
public ScheduleMonthly(Date start, int startTime, int duration, int freq, int rep, boolean onlyWorkingDays) {
this.setStartTime(startTime);
this.setStart(start);
CompanyCalendar cal = new CompanyCalendar();
cal.setTimeInMillis(this.getValidityStartTime());
cal.setMillisFromMidnight(startTime);
this.setStart(cal.getTime());
this.setDuration(duration);
this.setFreq((freq > 0 ? freq : 1));
this.setRepeat(rep);
this.setOnlyWorkingDays(onlyWorkingDays);
recalculateFields();
}
public int getDayInWeek() {
return dayOfWeek;
}
public void setDayInWeek(int dayOfWeek) {
this.dayOfWeek = dayOfWeek;
recalculateFields();
}
public int getWeekInMonth() {
return weekOfMonth;
}
public void setWeekInMonth(int weekOfMonth) {
this.weekOfMonth = weekOfMonth;
recalculateFields();
}
protected int getDayOfWeek() {
return dayOfWeek;
}
protected void setDayOfWeek(int dayOfWeek) {
this.dayOfWeek = dayOfWeek;
}
protected int getWeekOfMonth() {
return weekOfMonth;
}
protected void setWeekOfMonth(int weekOfMonth) {
this.weekOfMonth = weekOfMonth;
}
public void recalculateFields() {
CompanyCalendar cal = new CompanyCalendar();
cal.setTimeInMillis(this.getValidityStartTime());
long startTime = cal.getTimeInMillis();
int freq = (this.getFreq()>0?this.getFreq() :1);
if (weekOfMonth > 0 && dayOfWeek>0) {
startTime = getDayInWeekInMonth(cal);
/* while (startTime < this.getValidityStartTime()) {
cal.add(Calendar.MONTH, freq);
startTime = getDayInWeekInMonth(cal);
} */
}
cal.setTimeInMillis(startTime);
this.setStart(cal.getTime());
this.setStartTime(cal.getMillisFromMidnight());
if (this.getRepeat() > 0) {
int val = ((this.getRepeat() - 1) * freq);
cal.add(CompanyCalendar.MONTH, val);
long endTime = cal.getTimeInMillis();
if (weekOfMonth > 0 && dayOfWeek > 0) {
endTime = getDayInWeekInMonth(cal);
}
cal.setTimeInMillis(endTime);
} else {
this.setRepeat(0);
cal.setTime(CompanyCalendar.MAX_DATE);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.clear(Calendar.MINUTE);
cal.clear(Calendar.SECOND);
cal.clear(Calendar.MILLISECOND);
cal.add(Calendar.MILLISECOND, getStartTimeInMillis());
}
cal.add(Calendar.MILLISECOND, (int) this.getDuration());
this.setEnd(cal.getTime());
}
public long getNextFireTimeAfter(long afterTime) {
long returnTime = Long.MAX_VALUE;
if (afterTime <= getEnd().getTime()) {
if (afterTime > getStart().getTime()) {
long lstart = getStart().getTime();
CompanyCalendar cal = new CompanyCalendar();
cal.setTimeInMillis(lstart);
TimeZone timeZone = cal.getTimeZone();
int ofset = (timeZone.getOffset(lstart) - timeZone.getOffset(afterTime)); // questo server per calcolare i millisecondi effettivi in caso di ora legale
long distInMillisec = afterTime - lstart - ofset;
long distInDays = distInMillisec / CompanyCalendar.MILLIS_IN_DAY;
long distInYear = distInDays / 365; //non sbaglia il conto fino a che non sono passati 1460 anni
if (distInYear > 0) {
cal.add(Calendar.YEAR, (int) distInYear);
distInDays -= (distInYear*365);
}
double distInMonth = (int) distInDays / 29; //in un anno dividendo per 29 i giorni ottengo sempre il numero corretto di mesi
int rep = 1;
int freq = (this.getFreq()>0?this.getFreq() :1);
if(distInMonth > 0 ) {
rep = (int) distInMonth / freq;
if ((distInMonth % freq) != 0)
rep++;
}
if (this.getRepeat() > 0 && rep > (this.getRepeat() - 1))
rep = this.getRepeat() - 1;
cal.add(Calendar.MONTH, rep * freq);
long next;
if (weekOfMonth > 0 && dayOfWeek > 0) {
next = getDayInWeekInMonth(cal);
cal.setTimeInMillis(next);
int watchDog = 1; // not more than 10 occurrences for event
while (next < afterTime && watchDog < 10) {
cal.add(Calendar.MONTH, freq);
next = getDayInWeekInMonth(cal);
cal.setTimeInMillis(next);
watchDog++;
}
if (watchDog>=10) {
Tracer.platformLogger.warn("watchDog barking on ScheduleMonthly.getNextFireTimeAfter");
}
returnTime = next;
} else {
next = getDayInMonthAfter(cal,afterTime);
int watchDog = 1; // not more than 10 occurrences for event
while (next < afterTime && watchDog < 10) {
cal.add(Calendar.MONTH, freq);
next = getDayInMonthAfter(cal,afterTime);
watchDog++;
}
if (watchDog>=10)
Tracer.platformLogger.warn("watchDog barking on ScheduleMonthly.getNextFireTimeAfter");
returnTime = next;
}
} else {
returnTime = getStart().getTime();
}
}
return returnTime;
}
public long getPreviousFireTimeBefore(long beforeTime) {
long returnTime = Long.MIN_VALUE;
if (beforeTime > getStart().getTime()) {
if(beforeTime > getEnd().getTime())
beforeTime = getEnd().getTime();
long lstart = getStart().getTime();
CompanyCalendar cal = new CompanyCalendar();
cal.setTimeInMillis(lstart);
TimeZone timeZone = cal.getTimeZone();
int ofset = (timeZone.getOffset(lstart) - timeZone.getOffset(beforeTime)); // questo server per calcolare i millisecondi effettivi in caso di ora legale
long distInMillisec = beforeTime - lstart - ofset;
long distInDays = distInMillisec / CompanyCalendar.MILLIS_IN_DAY;
long distInYear = distInDays / 365; //non sbaglia il conto fino a che non sono passati 1460 anni
if (distInYear > 0) {
cal.add(Calendar.YEAR, (int) distInYear);
distInDays -= (distInYear*365);
}
int distInMonth = (int) distInDays / 29; //in un anno dividendo per 29 i giorni ottengo sempre il numero corretto di mesi
int freq = (this.getFreq()>0?this.getFreq() :1);
int rep = distInMonth / freq;
if (this.getRepeat() == 0 || rep <= (this.getRepeat() - 1)) {
cal.add(Calendar.MONTH, rep * freq);
long before;
if (weekOfMonth > 0 && dayOfWeek > 0) {
before = getDayInWeekInMonth(cal);
if(before < beforeTime)
returnTime = before;
} else {
before = getDayInMonthBefore(cal,beforeTime);
if(before < beforeTime)
returnTime = before;
}
}
}
return returnTime;
}
private long getDayInMonthBefore(CompanyCalendar cal, long beforeTime){
return cal.getTimeInMillis();
}
private long getDayInMonthAfter(CompanyCalendar cal, long afterTime){
return cal.getTimeInMillis();
}
/*
E' stata scelta questa soluzione perchè il CompanyCalendar ha un comportamento assurdo!!!!
*/
private long getDayInWeekInMonth(CompanyCalendar cal) {
cal.set(Calendar.DAY_OF_MONTH,1);
int count = 0;
int month = cal.get(CompanyCalendar.MONTH);
long lastDay = 0;
for(int i = 1; i<32 ; i++) {
if(month != cal.get(CompanyCalendar.MONTH))
break;
if(cal.get(CompanyCalendar.DAY_OF_WEEK) == dayOfWeek) {
count++;
lastDay = cal.getTimeInMillis();
if(count == weekOfMonth)
break;
}
cal.add(CompanyCalendar.DAY_OF_MONTH, 1);
}
return lastDay;
}
public String getName() {
return "monthly";
}
public JSONObject jsonify() {
JSONObject ret = super.jsonify();
ret.element("type","monthly");
ret.element("dayOfWeek",getDayOfWeek());
ret.element("weekOfMonth",getWeekOfMonth());
return ret;
}
}