package er.chronic.repeaters;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
import er.chronic.Options;
import er.chronic.tags.Pointer;
import er.chronic.tags.Pointer.PointerType;
import er.chronic.tags.Scalar;
import er.chronic.utils.Span;
import er.chronic.utils.StringUtils;
import er.chronic.utils.Tick;
import er.chronic.utils.Time;
import er.chronic.utils.Token;
public class RepeaterTime extends Repeater<Tick> {
private static final Pattern TIME_PATTERN = Pattern.compile("^\\d{1,2}(:?\\d{2})?([\\.:]?\\d{2})?$");
public RepeaterTime(String time) {
super(null);
String t = time.replaceAll(":", "");
Tick type;
int length = t.length();
if (length <= 2) {
int hours = Integer.parseInt(t);
int hoursInSeconds = hours * 60 * 60;
if (hours == 12) {
type = new Tick(0 * 60 * 60, true);
}
else {
type = new Tick(hoursInSeconds, true);
}
}
else if (length == 3) {
int hoursInSeconds = Integer.parseInt(t.substring(0, 1)) * 60 * 60;
int minutesInSeconds = Integer.parseInt(t.substring(1)) * 60;
type = new Tick(hoursInSeconds + minutesInSeconds, true);
}
else if (length == 4) {
// DIFF: we need to support 05:45 vs 5:45
//boolean ambiguous = (time.contains(":") && Integer.parseInt(t.substring(0, 1) != 0 && Integer.parseInt(t.substring(0, 2)) <= 12);
boolean ambiguous = (time.contains(":") && Integer.parseInt(t.substring(0, 2)) <= 12);
int hours = Integer.parseInt(t.substring(0, 2));
int hoursInSeconds = hours * 60 * 60;
int minutesInSeconds = Integer.parseInt(t.substring(2)) * 60;
if (hours == 12) {
type = new Tick(0 * 60 * 60 + minutesInSeconds, ambiguous);
}
else {
type = new Tick(hoursInSeconds + minutesInSeconds, ambiguous);
}
}
else if (length == 5) {
int hoursInSeconds = Integer.parseInt(t.substring(0, 1)) * 60 * 60;
int minutesInSeconds = Integer.parseInt(t.substring(1, 3)) * 60;
int seconds = Integer.parseInt(t.substring(3));
type = new Tick(hoursInSeconds + minutesInSeconds + seconds, true);
}
else if (length == 6) {
// DIFF: we need to support 05:45 vs 5:45
//boolean ambiguous = (time.contains(":") && Integer.parseInt(t.substring(0, 1)) != 0 && Integer.parseInt(t.substring(0, 2)) <= 12);
boolean ambiguous = (time.contains(":") && Integer.parseInt(t.substring(0, 2)) <= 12);
int hours = Integer.parseInt(t.substring(0, 2));
int hoursInSeconds = hours * 60 * 60;
int minutesInSeconds = Integer.parseInt(t.substring(2, 4)) * 60;
int seconds = Integer.parseInt(t.substring(4, 6));
//type = new Tick(hoursInSeconds + minutesInSeconds + seconds, ambiguous);
if (hours == 12) {
type = new Tick(0 * 60 * 60 + minutesInSeconds + seconds, ambiguous);
}
else {
type = new Tick(hoursInSeconds + minutesInSeconds + seconds, ambiguous);
}
}
else {
throw new IllegalArgumentException("Time cannot exceed six digits");
}
setType(type);
}
private Calendar _currentTime;
@Override
protected Span _nextSpan(PointerType pointer) {
int halfDay = RepeaterDay.DAY_SECONDS / 2;
int fullDay = RepeaterDay.DAY_SECONDS;
Calendar now = getNow();
Tick tick = getType();
boolean first = false;
if (_currentTime == null) {
first = true;
Calendar midnight = Time.ymd(now);
Calendar yesterdayMidnight = Time.cloneAndAdd(midnight, Calendar.SECOND, -fullDay);
Calendar tomorrowMidnight = Time.cloneAndAdd(midnight, Calendar.SECOND, fullDay);
int offsetFix = (midnight.get(Calendar.ZONE_OFFSET) - tomorrowMidnight.get(Calendar.ZONE_OFFSET)) / 1000;
tomorrowMidnight.add(Calendar.SECOND, offsetFix);
boolean done = false;
if (pointer == Pointer.PointerType.FUTURE) {
if (tick.isAmbiguous()) {
List<Calendar> futureDates = new LinkedList<>();
futureDates.add(Time.cloneAndAdd(midnight, Calendar.SECOND, tick.intValue() + offsetFix));
futureDates.add(Time.cloneAndAdd(midnight, Calendar.SECOND, halfDay + tick.intValue() + offsetFix));
futureDates.add(Time.cloneAndAdd(tomorrowMidnight, Calendar.SECOND, tick.intValue()));
for (Calendar futureDate : futureDates) {
if (futureDate.after(now) || futureDate.equals(now)) {
_currentTime = futureDate;
done = true;
break;
}
}
}
else {
List<Calendar> futureDates = new LinkedList<>();
futureDates.add(Time.cloneAndAdd(midnight, Calendar.SECOND, tick.intValue() + offsetFix));
futureDates.add(Time.cloneAndAdd(tomorrowMidnight, Calendar.SECOND, tick.intValue()));
for (Calendar futureDate : futureDates) {
if (futureDate.after(now) || futureDate.equals(now)) {
_currentTime = futureDate;
done = true;
break;
}
}
}
}
else {
if (tick.isAmbiguous()) {
List<Calendar> pastDates = new LinkedList<>();
pastDates.add(Time.cloneAndAdd(midnight, Calendar.SECOND, halfDay + tick.intValue() + offsetFix));
pastDates.add(Time.cloneAndAdd(midnight, Calendar.SECOND, tick.intValue() + offsetFix));
pastDates.add(Time.cloneAndAdd(yesterdayMidnight, Calendar.SECOND, tick.intValue() + halfDay));
for (Calendar pastDate : pastDates) {
if (pastDate.before(now) || pastDate.equals(now)) {
_currentTime = pastDate;
done = true;
break;
}
}
}
else {
List<Calendar> pastDates = new LinkedList<>();
pastDates.add(Time.cloneAndAdd(midnight, Calendar.SECOND, tick.intValue() + offsetFix));
pastDates.add(Time.cloneAndAdd(yesterdayMidnight, Calendar.SECOND, tick.intValue()));
for (Calendar pastDate : pastDates) {
if (pastDate.before(now) || pastDate.equals(now)) {
_currentTime = pastDate;
done = true;
break;
}
}
}
}
if (!done && _currentTime == null) {
throw new IllegalStateException("Current time cannot be null at this point.");
}
}
if (!first) {
int increment = (tick.isAmbiguous()) ? halfDay : fullDay;
int direction = (pointer == Pointer.PointerType.FUTURE) ? 1 : -1;
_currentTime.add(Calendar.SECOND, direction * increment);
}
return new Span(_currentTime, Time.cloneAndAdd(_currentTime, Calendar.SECOND, getWidth()));
}
@Override
protected Span _thisSpan(PointerType pointer) {
if (pointer == Pointer.PointerType.NONE) {
pointer = Pointer.PointerType.FUTURE;
}
return nextSpan(pointer);
}
@Override
public Span getOffset(Span span, float amount, PointerType pointer) {
throw new IllegalStateException("Not implemented.");
}
@Override
public int getWidth() {
return 1;
}
@Override
public String toString() {
return super.toString() + "-time-" + getType();
}
public static RepeaterTime scan(Token token, List<Token> tokens, Options options) {
// MS: added an exclusion so 3.25 doesn't match a time ...
if (RepeaterTime.TIME_PATTERN.matcher(token.getWord()).matches() && !Scalar.FRACTIONAL_SCALAR_PATTERN.matcher(token.getWord()).matches()) {
// if (RepeaterTime.TIME_PATTERN.matcher(token.getWord()).matches()) {
return new RepeaterTime(token.getWord());
}
Integer intStrValue = StringUtils.integerValue(token.getWord());
if (intStrValue != null) {
return new RepeaterTime(intStrValue.toString());
}
return null;
}
}