package er.chronic.repeaters;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import er.chronic.tags.Pointer;
import er.chronic.tags.Pointer.PointerType;
import er.chronic.utils.Range;
import er.chronic.utils.Span;
import er.chronic.utils.Time;
import er.chronic.utils.Token;
public abstract class RepeaterDayPortion<T> extends Repeater<T> {
private static final Pattern AM_PATTERN = Pattern.compile("^ams?$");
private static final Pattern PM_PATTERN = Pattern.compile("^pms?$");
private static final Pattern MORNING_PATTERN = Pattern.compile("^mornings?$");
private static final Pattern AFTERNOON_PATTERN = Pattern.compile("^afternoons?$");
private static final Pattern EVENING_PATTERN = Pattern.compile("^evenings?$");
private static final Pattern NIGHT_PATTERN = Pattern.compile("^(night|nite)s?$");
private static final int FULL_DAY_SECONDS = 60 * 60 * 24;
public static enum DayPortion {
AM, PM, MORNING, AFTERNOON, EVENING, NIGHT
}
private Range _range;
private Span _currentSpan;
public RepeaterDayPortion(T type) {
super(type);
_range = createRange(type);
}
@Override
protected Span _nextSpan(PointerType pointer) {
Calendar rangeStart;
Calendar rangeEnd;
if (_currentSpan == null) {
long nowSeconds = (getNow().getTimeInMillis() - Time.ymd(getNow()).getTimeInMillis()) / 1000;
if (nowSeconds < _range.getBegin()) {
if (pointer == Pointer.PointerType.FUTURE) {
rangeStart = Time.cloneAndAdd(Time.ymd(getNow()), Calendar.SECOND, _range.getBegin());
}
else if (pointer == Pointer.PointerType.PAST) {
rangeStart = Time.cloneAndAdd(Time.cloneAndAdd(Time.ymd(getNow()), Calendar.DAY_OF_MONTH, -1), Calendar.SECOND, _range.getBegin());
}
else {
throw new IllegalArgumentException("Unable to handle pointer type " + pointer);
}
}
else if (nowSeconds > _range.getBegin()) {
if (pointer == Pointer.PointerType.FUTURE) {
rangeStart = Time.cloneAndAdd(Time.cloneAndAdd(Time.ymd(getNow()), Calendar.DAY_OF_MONTH, 1), Calendar.SECOND, _range.getBegin());
}
else if (pointer == Pointer.PointerType.PAST) {
rangeStart = Time.cloneAndAdd(Time.ymd(getNow()), Calendar.SECOND, _range.getBegin());
}
else {
throw new IllegalArgumentException("Unable to handle pointer type " + pointer);
}
}
else {
if (pointer == Pointer.PointerType.FUTURE) {
rangeStart = Time.cloneAndAdd(Time.cloneAndAdd(Time.ymd(getNow()), Calendar.DAY_OF_MONTH, 1), Calendar.SECOND, _range.getBegin());
}
else if (pointer == Pointer.PointerType.PAST) {
rangeStart = Time.cloneAndAdd(Time.cloneAndAdd(Time.ymd(getNow()), Calendar.DAY_OF_MONTH, -1), Calendar.SECOND, _range.getBegin());
}
else {
throw new IllegalArgumentException("Unable to handle pointer type " + pointer);
}
}
_currentSpan = new Span(rangeStart, Time.cloneAndAdd(rangeStart, Calendar.SECOND, _range.getWidth()));
}
else {
if (pointer == Pointer.PointerType.FUTURE) {
// WARN: Does not use Calendar
_currentSpan = _currentSpan.add(RepeaterDayPortion.FULL_DAY_SECONDS);
}
else if (pointer == Pointer.PointerType.PAST) {
// WARN: Does not use Calendar
_currentSpan = _currentSpan.subtract(RepeaterDayPortion.FULL_DAY_SECONDS);
}
else {
throw new IllegalArgumentException("Unable to handle pointer type " + pointer);
}
}
return _currentSpan;
}
@Override
protected Span _thisSpan(PointerType pointer) {
Calendar rangeStart = Time.cloneAndAdd(Time.ymd(getNow()), Calendar.SECOND, _range.getBegin());
_currentSpan = new Span(rangeStart, Time.cloneAndAdd(rangeStart, Calendar.SECOND, _range.getWidth()));
return _currentSpan;
}
@Override
public Span getOffset(Span span, float amount, PointerType pointer) {
setStart(span.getBeginCalendar());
Span portionSpan = nextSpan(pointer);
int direction = (pointer == Pointer.PointerType.FUTURE) ? 1 : -1;
portionSpan = portionSpan.add(direction * (amount - 1) * RepeaterDay.DAY_SECONDS);
return portionSpan;
}
@Override
public int getWidth() {
if (_range == null) {
throw new IllegalStateException("Range has not been set");
}
int width;
if (_currentSpan != null) {
width = (int) _currentSpan.getWidth();
}
else {
width = _getWidth(_range);
}
return width;
}
protected abstract int _getWidth(Range range);
protected abstract Range createRange(T type);
@Override
public String toString() {
return super.toString() + "-dayportion-" + getType();
}
public static EnumRepeaterDayPortion scan(Token token) {
Map<Pattern, RepeaterDayPortion.DayPortion> scanner = new HashMap<>();
scanner.put(RepeaterDayPortion.AM_PATTERN, RepeaterDayPortion.DayPortion.AM);
scanner.put(RepeaterDayPortion.PM_PATTERN, RepeaterDayPortion.DayPortion.PM);
scanner.put(RepeaterDayPortion.MORNING_PATTERN, RepeaterDayPortion.DayPortion.MORNING);
scanner.put(RepeaterDayPortion.AFTERNOON_PATTERN, RepeaterDayPortion.DayPortion.AFTERNOON);
scanner.put(RepeaterDayPortion.EVENING_PATTERN, RepeaterDayPortion.DayPortion.EVENING);
scanner.put(RepeaterDayPortion.NIGHT_PATTERN, RepeaterDayPortion.DayPortion.NIGHT);
for (Pattern scannerItem : scanner.keySet()) {
if (scannerItem.matcher(token.getWord()).matches()) {
return new EnumRepeaterDayPortion(scanner.get(scannerItem));
}
}
return null;
}
}