/**
* Copyright © 2002 Instituto Superior Técnico
*
* This file is part of FenixEdu Academic.
*
* FenixEdu Academic is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FenixEdu Academic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FenixEdu Academic. If not, see <http://www.gnu.org/licenses/>.
*/
package org.fenixedu.academic.domain.space;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections.comparators.ComparatorChain;
import org.apache.commons.lang.StringUtils;
import org.fenixedu.academic.domain.ExecutionCourse;
import org.fenixedu.academic.domain.FrequencyType;
import org.fenixedu.academic.domain.Lesson;
import org.fenixedu.academic.domain.exceptions.DomainException;
import org.fenixedu.academic.util.DiaSemana;
import org.fenixedu.academic.util.HourMinuteSecond;
import org.fenixedu.commons.i18n.I18N;
import org.fenixedu.spaces.domain.Space;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.joda.time.TimeOfDay;
import org.joda.time.YearMonthDay;
public abstract class EventSpaceOccupation extends EventSpaceOccupation_Base {
public static final Comparator<EventSpaceOccupation> COMPARATOR_BY_BEGIN_DATE = new ComparatorChain();
static {
((ComparatorChain) COMPARATOR_BY_BEGIN_DATE).addComparator(new BeanComparator("period.startDate"));
((ComparatorChain) COMPARATOR_BY_BEGIN_DATE).addComparator(new BeanComparator("externalId"));
}
private static int SATURDAY_IN_JODA_TIME = 6, SUNDAY_IN_JODA_TIME = 7;
private static transient Locale locale = I18N.getLocale();
public abstract Boolean getDailyFrequencyMarkSaturday();
public abstract Boolean getDailyFrequencyMarkSunday();
public abstract YearMonthDay getBeginDate();
public abstract YearMonthDay getEndDate();
public abstract HourMinuteSecond getStartTimeDateHourMinuteSecond();
public abstract HourMinuteSecond getEndTimeDateHourMinuteSecond();
public abstract DiaSemana getDayOfWeek();
public abstract FrequencyType getFrequency();
protected EventSpaceOccupation() {
super();
}
public void setResource(Space resource) {
if (!(SpaceUtils.isRoom(resource) || SpaceUtils.isRoomSubdivision(resource))) {
throw new DomainException("error.EventSpaceOccupation.invalid.resource");
}
getSpaceSet().clear();
super.addSpace(resource);
}
public Space getRoom() {
return getSpace();
}
public Calendar getStartTime() {
HourMinuteSecond hms = getStartTimeDateHourMinuteSecond();
Date date =
(hms == null) ? null : new java.util.Date(0, 0, 1, hms.getHour(), hms.getMinuteOfHour(), hms.getSecondOfMinute());
if (date != null) {
Calendar result = Calendar.getInstance();
result.setTime(date);
return result;
}
return null;
}
public Calendar getEndTime() {
HourMinuteSecond hms = getEndTimeDateHourMinuteSecond();
Date date =
(hms == null) ? null : new java.util.Date(0, 0, 1, hms.getHour(), hms.getMinuteOfHour(), hms.getSecondOfMinute());
if (date != null) {
Calendar result = Calendar.getInstance();
result.setTime(date);
return result;
}
return null;
}
protected boolean intersects(YearMonthDay startDate, YearMonthDay endDate) {
return getBeginDate() != null && getEndDate() != null && !getBeginDate().isAfter(endDate)
&& !getEndDate().isBefore(startDate);
}
public boolean alreadyWasOccupiedBy(final EventSpaceOccupation occupation) {
if (this.equals(occupation)) {
return true;
}
if (occupation.isLessonInstanceSpaceOccupation() || occupation.isWrittenEvaluationSpaceOccupation()
|| intersects(occupation.getBeginDate(), occupation.getEndDate())) {
List<Interval> thisOccupationIntervals =
getEventSpaceOccupationIntervals(occupation.getBeginDate(), occupation.getEndDate());
List<Interval> passedOccupationIntervals =
occupation.getEventSpaceOccupationIntervals((YearMonthDay) null, (YearMonthDay) null);
for (Interval interval : thisOccupationIntervals) {
for (Interval passedInterval : passedOccupationIntervals) {
if (interval.getStart().isBefore(passedInterval.getEnd())
&& interval.getEnd().isAfter(passedInterval.getStart())) {
return true;
}
}
}
}
return false;
}
private boolean isWrittenEvaluationSpaceOccupation() {
return this instanceof WrittenEvaluationSpaceOccupation;
}
private boolean isLessonInstanceSpaceOccupation() {
return this instanceof LessonInstanceSpaceOccupation;
}
public boolean alreadyWasOccupiedIn(final YearMonthDay startDate, final YearMonthDay endDate,
final HourMinuteSecond startTime, final HourMinuteSecond endTime, final DiaSemana dayOfWeek,
final FrequencyType frequency, final Boolean dailyFrequencyMarkSaturday, final Boolean dailyFrequencyMarkSunday) {
startTime.setSecondOfMinute(0);
endTime.setSecondOfMinute(0);
if (intersects(startDate, endDate)) {
List<Interval> thisOccupationIntervals = getEventSpaceOccupationIntervals(startDate, endDate);
List<Interval> passedOccupationIntervals =
generateEventSpaceOccupationIntervals(startDate, endDate, startTime, endTime, frequency, dayOfWeek,
dailyFrequencyMarkSaturday, dailyFrequencyMarkSunday, null, null);
for (Interval interval : thisOccupationIntervals) {
for (Interval passedInterval : passedOccupationIntervals) {
if (interval.getStart().isBefore(passedInterval.getEnd())
&& interval.getEnd().isAfter(passedInterval.getStart())) {
return true;
}
}
}
}
return false;
}
@Override
public Boolean overlaps(List<Interval> intervals) {
return overlaps(intervals.toArray(new Interval[0]));
}
@Override
public boolean overlaps(final Interval... intervals) {
for (final Interval interval : intervals) {
if (overlaps(interval)) {
return true;
}
// final DateTime start = interval.getStart();
// final DateTime end = interval.getEnd();
// if (alreadyWasOccupiedIn(start.toYearMonthDay(), end.toYearMonthDay(), new HourMinuteSecond(start.toDate()),
// new HourMinuteSecond(end.toDate()), null, null, null, null)) {
// return true;
// }
}
return false;
}
protected abstract boolean overlaps(final Interval interval);
public List<Interval> getEventSpaceOccupationIntervals(DateTime start, DateTime end) {
final Interval i = new Interval(start, end);
final List<Interval> intervals = getEventSpaceOccupationIntervals(start.toYearMonthDay(), end.toYearMonthDay());
for (final Iterator<Interval> iterator = intervals.iterator(); iterator.hasNext();) {
final Interval interval = iterator.next();
if (!interval.overlaps(i)) {
iterator.remove();
}
}
return intervals;
}
@Override
public List<Interval> getIntervals() {
return getEventSpaceOccupationIntervals((YearMonthDay) null, (YearMonthDay) null);
}
public List<Interval> getEventSpaceOccupationIntervals(YearMonthDay startDateToSearch, YearMonthDay endDateToSearch) {
return generateEventSpaceOccupationIntervals(getBeginDate(), getEndDate(), getStartTimeDateHourMinuteSecond(),
getEndTimeDateHourMinuteSecond(), getFrequency(), getDayOfWeek(), getDailyFrequencyMarkSaturday(),
getDailyFrequencyMarkSunday(), startDateToSearch, endDateToSearch);
}
public static List<Interval> generateEventSpaceOccupationIntervals(YearMonthDay begin, final YearMonthDay end,
final HourMinuteSecond beginTime, final HourMinuteSecond endTime, final FrequencyType frequency,
final DiaSemana diaSemana, final Boolean dailyFrequencyMarkSaturday, final Boolean dailyFrequencyMarkSunday,
final YearMonthDay startDateToSearch, final YearMonthDay endDateToSearch) {
List<Interval> result = new ArrayList<Interval>();
begin = getBeginDateInSpecificWeekDay(diaSemana, begin);
if (frequency == null) {
if (!begin.isAfter(end)
&& (startDateToSearch == null || (!end.isBefore(startDateToSearch) && !begin.isAfter(endDateToSearch)))) {
result.add(createNewInterval(begin, end, beginTime, endTime));
return result;
}
} else {
int numberOfDaysToSum = frequency.getNumberOfDays();
while (true) {
if (begin.isAfter(end)) {
break;
}
if (startDateToSearch == null || (!begin.isBefore(startDateToSearch) && !begin.isAfter(endDateToSearch))) {
Interval interval = createNewInterval(begin, begin, beginTime, endTime);
if (!frequency.equals(FrequencyType.DAILY)
|| ((dailyFrequencyMarkSaturday || interval.getStart().getDayOfWeek() != SATURDAY_IN_JODA_TIME) && (dailyFrequencyMarkSunday || interval
.getStart().getDayOfWeek() != SUNDAY_IN_JODA_TIME))) {
result.add(interval);
}
}
begin = begin.plusDays(numberOfDaysToSum);
}
}
return result;
}
protected DateTime getInstant(boolean firstInstant, YearMonthDay begin, final YearMonthDay end,
final HourMinuteSecond beginTime, final HourMinuteSecond endTime, final FrequencyType frequency,
final DiaSemana diaSemana, final Boolean dailyFrequencyMarkSaturday, final Boolean dailyFrequencyMarkSunday) {
DateTime instantResult = null;
begin = getBeginDateInSpecificWeekDay(diaSemana, begin);
if (frequency == null) {
if (!begin.isAfter(end)) {
if (firstInstant) {
return begin.toDateTime(new TimeOfDay(beginTime.getHour(), beginTime.getMinuteOfHour(), 0, 0));
} else {
return end.toDateTime(new TimeOfDay(endTime.getHour(), endTime.getMinuteOfHour(), 0, 0));
}
}
} else {
int numberOfDaysToSum = frequency.getNumberOfDays();
while (true) {
if (begin.isAfter(end)) {
break;
}
DateTime intervalEnd = begin.toDateTime(new TimeOfDay(endTime.getHour(), endTime.getMinuteOfHour(), 0, 0));
if (!frequency.equals(FrequencyType.DAILY)
|| ((dailyFrequencyMarkSaturday || intervalEnd.getDayOfWeek() != SATURDAY_IN_JODA_TIME) && (dailyFrequencyMarkSunday || intervalEnd
.getDayOfWeek() != SUNDAY_IN_JODA_TIME))) {
if (firstInstant) {
return begin.toDateTime(new TimeOfDay(beginTime.getHour(), beginTime.getMinuteOfHour(), 0, 0));
} else {
instantResult = intervalEnd;
}
}
begin = begin.plusDays(numberOfDaysToSum);
}
}
return instantResult;
}
private static YearMonthDay getBeginDateInSpecificWeekDay(DiaSemana diaSemana, YearMonthDay begin) {
if (diaSemana != null) {
YearMonthDay newBegin =
begin.toDateTimeAtMidnight().withDayOfWeek(diaSemana.getDiaSemanaInDayOfWeekJodaFormat()).toYearMonthDay();
if (newBegin.isBefore(begin)) {
begin = newBegin.plusDays(Lesson.NUMBER_OF_DAYS_IN_WEEK);
} else {
begin = newBegin;
}
}
return begin;
}
protected static Interval createNewInterval(YearMonthDay begin, YearMonthDay end, HourMinuteSecond beginTime,
HourMinuteSecond endTime) {
return new Interval(begin.toDateTime(new TimeOfDay(beginTime.getHour(), beginTime.getMinuteOfHour(), 0, 0)),
end.toDateTime(new TimeOfDay(endTime.getHour(), endTime.getMinuteOfHour(), 0, 0)));
}
public DateTime getFirstInstant() {
return getInstant(true, getBeginDate(), getEndDate(), getStartTimeDateHourMinuteSecond(),
getEndTimeDateHourMinuteSecond(), getFrequency(), getDayOfWeek(), getDailyFrequencyMarkSaturday(),
getDailyFrequencyMarkSunday());
}
public DateTime getLastInstant() {
return getInstant(false, getBeginDate(), getEndDate(), getStartTimeDateHourMinuteSecond(),
getEndTimeDateHourMinuteSecond(), getFrequency(), getDayOfWeek(), getDailyFrequencyMarkSaturday(),
getDailyFrequencyMarkSunday());
}
public String getPrettyPrint() {
StringBuilder builder = new StringBuilder();
if (getFrequency() == null) {
builder.append(getBeginDate().toString("dd/MM/yyyy")).append(" ").append(getPresentationBeginTime());
builder.append(" - ").append(getEndDate().toString("dd/MM/yyyy")).append(" ").append(getPresentationEndTime());
} else {
builder.append(getBeginDate().toString("dd/MM/yyyy")).append(" - ").append(getEndDate().toString("dd/MM/yyyy"));
builder.append(" (").append(getPresentationBeginTime()).append(" - ").append(getPresentationEndTime()).append(")");
}
return builder.toString();
}
@Override
public String getSubject() {
return getPresentationString();
}
public String getPresentationString() {
return StringUtils.EMPTY;
}
public String getPresentationBeginTime() {
return getStartTimeDateHourMinuteSecond().toString("HH:mm");
}
public String getPresentationEndTime() {
return getEndTimeDateHourMinuteSecond().toString("HH:mm");
}
public String getPresentationBeginDate() {
return getBeginDate().toString("dd MMMM yyyy", locale) + " ("
+ getBeginDate().toDateTimeAtMidnight().toString("E", locale) + ")";
}
public String getPresentationEndDate() {
return getEndDate().toString("dd MMMM yyyy", locale) + " (" + getEndDate().toDateTimeAtMidnight().toString("E", locale)
+ ")";
}
public abstract boolean isOccupiedByExecutionCourse(final ExecutionCourse executionCourse, final DateTime start,
final DateTime end);
@Override
public void delete() {
setBennu(null);
getSpaceSet().clear();
super.deleteDomainObject();
}
}