package org.atlasapi.persistence.content.schedule.mongo;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.atlasapi.media.channel.Channel;
import org.atlasapi.media.channel.ChannelResolver;
import org.atlasapi.media.entity.Broadcast;
import org.atlasapi.media.entity.Item;
import org.atlasapi.media.entity.Publisher;
import org.atlasapi.media.entity.ScheduleEntry;
import org.atlasapi.media.entity.ScheduleEntry.ItemRefAndBroadcast;
import org.atlasapi.media.entity.Version;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.Interval;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.metabroadcast.common.base.Maybe;
import com.metabroadcast.common.time.DateTimeZones;
public class ScheduleEntryBuilder {
private final Log log = LogFactory.getLog(ScheduleEntryBuilder.class);
private final static long BIN_MILLIS = Duration.standardHours(1).getMillis();
private final Duration maxBroadcastAge;
private ChannelResolver channelResolver;
public ScheduleEntryBuilder(ChannelResolver channelResolver, Duration maxBroadcastAge) {
this.maxBroadcastAge = maxBroadcastAge;
this.channelResolver = channelResolver;
}
public ScheduleEntryBuilder(ChannelResolver channelResolver) {
this(channelResolver, Duration.standardDays(28));
}
public Map<String, ScheduleEntry> toScheduleEntries(Iterable<? extends Item> items) {
Map<String, ScheduleEntry> entries = new HashMap<String, ScheduleEntry>();
for (Item item : items) {
for (Version version : item.nativeVersions()) {
for (Broadcast broadcast : version.getBroadcasts()) {
ItemRefAndBroadcast itemAndBroadcast = new ItemRefAndBroadcast(item, broadcast);
Publisher publisher = item.getPublisher();
Maybe<Channel> channel = channelResolver.fromUri(broadcast.getBroadcastOn());
if (channel.hasValue()) {
toScheduleEntryFromBroadcast(channel.requireValue(), publisher, itemAndBroadcast, entries);
} else {
log.warn("No channel for " + broadcast.getTransmissionTime().toString() + " of " + item.getCanonicalUri());
}
}
}
}
return entries;
}
public void toScheduleEntryFromBroadcast(Channel channel, Publisher publisher, ItemRefAndBroadcast itemAndBroadcast, Map<String, ScheduleEntry> entries) {
Broadcast broadcast = itemAndBroadcast.getBroadcast();
if(!broadcast.isActivelyPublished() && Publisher.PA.equals(publisher)) {
return;
}
if(!broadcast.getTransmissionTime().isBefore(new DateTime(DateTimeZones.UTC).minus(maxBroadcastAge))) {
for (Interval interval: intervalsFor(broadcast.getTransmissionTime(), broadcast.getTransmissionEndTime())) {
String key = ScheduleEntry.toKey(interval, channel, publisher);
if (entries.containsKey(key)) {
ScheduleEntry entry = entries.get(key);
entry.withItems(Iterables.concat(entry.getItemRefsAndBroadcasts(), ImmutableList.of(itemAndBroadcast)));
} else {
ScheduleEntry entry = new ScheduleEntry(interval, channel, publisher, ImmutableList.of(itemAndBroadcast));
entries.put(key, entry);
}
}
}
}
List<Interval> intervalsFor(DateTime start, DateTime end) {
long startMillis = millisBackToNearestBin(start);
long endMillis = end.getMillis();
ImmutableList.Builder<Interval> intervals = ImmutableList.builder();
while (startMillis < endMillis) {
Interval interval = new Interval(new DateTime(startMillis, DateTimeZones.UTC), new DateTime(startMillis + BIN_MILLIS - 1, DateTimeZones.UTC));
intervals.add(interval);
startMillis += (BIN_MILLIS);
}
return intervals.build();
}
private long millisBackToNearestBin(DateTime when) {
long div = when.getMillis() / BIN_MILLIS;
int asInt = (int) div;
return asInt * BIN_MILLIS;
}
}