package com.github.pfichtner.jrunalyser.base.datasource;
import static com.github.pfichtner.jrunalyser.base.datasource.CachingDatasourceFascadeProxy.fromTo;
import static com.google.common.base.Predicates.compose;
import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Iterables.tryFind;
import java.io.IOException;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Set;
import com.github.pfichtner.jrunalyser.base.data.Distance;
import com.github.pfichtner.jrunalyser.base.data.Duration;
import com.github.pfichtner.jrunalyser.base.data.SegmentationUnit;
import com.github.pfichtner.jrunalyser.base.data.WayPoint;
import com.github.pfichtner.jrunalyser.base.data.floater.HighlightableSegment;
import com.github.pfichtner.jrunalyser.base.data.segmenter.Segmenter;
import com.github.pfichtner.jrunalyser.base.data.segmenter.Segmenters;
import com.github.pfichtner.jrunalyser.base.data.stat.DefaultStatistics;
import com.github.pfichtner.jrunalyser.base.data.stat.Orderings;
import com.github.pfichtner.jrunalyser.base.data.stat.Predicates;
import com.github.pfichtner.jrunalyser.base.data.stat.Statistics;
import com.github.pfichtner.jrunalyser.base.data.track.DefaultTrack;
import com.github.pfichtner.jrunalyser.base.data.track.Id;
import com.github.pfichtner.jrunalyser.base.data.track.Track;
import com.github.pfichtner.jrunalyser.base.data.track.Tracks;
import com.github.pfichtner.jrunalyser.base.data.track.comparator.TrackComparators;
import com.github.pfichtner.jrunalyser.base.datasource.DatasourceFascadeEvent.Type;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.mathieubolla.guava.FluentParallelIterable;
/**
* A DatasourceFascade that adapts a {@link Datasource} to
* {@link DatasourceFascade} by calculating all attributes. Since nothing gets
* cached this adapter should be used in conjunction with
* {@link com.github.pfichtner.jrunalyser.base.datasource.CachingDatasourceFascadeProxy}.
*
* @author Peter Fichtner
*/
public class DataSourceDatasourceFascadeAdapter extends
AbstractDatasourceFascade {
private final Datasource datasource;
private final Function<Id, Track> loadTrack = new Function<Id, Track>() {
@Override
public Track apply(Id id) {
try {
return loadTrack(id);
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
};
public DataSourceDatasourceFascadeAdapter(Datasource datasource) {
this.datasource = datasource;
}
@Override
public Set<Id> getTrackIds() throws IOException {
return this.datasource.getTrackIds();
}
@Override
public Iterable<Id> getTrackIds(Date start, Date end) throws IOException {
List<Track> tracks = Orderings.time.sortedCopy(FluentParallelIterable
.from(getTrackIds()).parallel().transform(this.loadTrack)
.filter(fromTo(this, start, end)));
return transform(tracks, com.github.pfichtner.jrunalyser.base.data.stat.Functions.Tracks.id);
}
@Override
public Track addTrack(Track track) throws IOException {
Track result = this.datasource.addTrack(track);
fire(new DefaultDatasourceFascadeEvent(Type.ADDED, result));
return result;
}
@Override
public Track removeTrack(Id id) throws IOException {
Track result = this.datasource.removeTrack(id);
fire(new DefaultDatasourceFascadeEvent(Type.REMOVED, result));
return result;
}
public Track loadTrack(Id id) throws IOException {
Track loadTrack = this.datasource.loadTrack(id);
return new DefaultTrack(loadTrack.getId(), loadTrack.getMetadata(),
loadTrack.getWaypoints(), loadTrack.getSegments(), null);
}
@Override
public Iterable<Track> loadTracks(Iterable<Id> ids) throws IOException {
return Iterables.transform(ids, this.loadTrack);
}
@Override
public Optional<Statistics> loadBestSegment(Id id,
SegmentationUnit segmentationUnit) throws IOException {
Function<Id, Track> loadSegmented = Functions.compose(
createSegmentFunc(Segmenters
.getFloatingSegmenter(segmentationUnit)),
this.loadTrack);
Optional<HighlightableSegment> segment = getHlSegment(loadSegmented
.apply(id));
return segment.isPresent() ? Optional.of(getFixedStats(
segmentationUnit, segment.get())) : Optional
.<Statistics> absent();
}
private Statistics getFixedStats(SegmentationUnit segmentationUnit,
HighlightableSegment segment) {
Statistics statistics;
if (segmentationUnit instanceof Duration) {
statistics = new FixedStatistics((Duration) segmentationUnit,
segment.getStatistics());
} else if (segmentationUnit instanceof Distance) {
statistics = new FixedStatistics((Distance) segmentationUnit,
segment.getStatistics());
} else {
throw new IllegalStateException("Cannot handle " + segmentationUnit);
}
return DefaultStatistics.copyOf(statistics);
}
private static Optional<HighlightableSegment> getHlSegment(
Track segmentedTrack) {
return tryFind(
filter(segmentedTrack.getSegments(), HighlightableSegment.class),
Predicates.HighlightableSegments.isHighligted);
}
@Override
public List<Id> listTracks(SegmentationUnit segmentationUnit)
throws IOException {
List<Track> in = loadOrdered(segmentationUnit);
return FluentParallelIterable.from(in).parallel()
.transform(com.github.pfichtner.jrunalyser.base.data.stat.Functions.Tracks.id)
.toList();
}
private List<Track> loadOrdered(final SegmentationUnit segmentationUnit)
throws IOException {
return FluentIterable
.from(getSegmented(Segmenters
.getFloatingSegmenter(segmentationUnit))).toSortedList(
Orderings.highlightedSpeedOrdering);
}
// ------------------------------------------------------------------------
private Iterable<Track> getSegmented(Segmenter segmenter)
throws IOException {
return FluentParallelIterable.from(getTrackIds()).parallel()
.transform(this.loadTrack)
.transform(createSegmentFunc(segmenter));
}
private static Function<Track, Track> createSegmentFunc(
final Segmenter segmenter) {
return new Function<Track, Track>() {
@Override
public Track apply(Track track) {
return segmenter.segment(track);
}
};
}
// ----------------------------------------------------------------------
public Set<Id> getSimilarTracks(final Id id) throws IOException {
return Sets
.filter(getTrackIds(), com.google.common.base.Predicates.and(
compose(similar(id), this.loadTrack), not(equalTo(id))));
}
@Override
public boolean isAwayEqReturn(Id id) {
try {
return Tracks.isAwayEqReturn(loadTrack(id));
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
@Override
public Set<WayPoint> getCommonWaypoints() {
return this.datasource.getCommonWaypoints();
}
private Predicate<Track> similar(final Id id) throws IOException {
return com.google.common.base.Predicates.compose(
equalTo(Integer.valueOf(0)), new Function<Track, Integer>() {
Track ref = loadTrack(id);
// we cannot provide byAttributes since this will lead to
// accesses to the statistics we don't provide
Comparator<Track> trackComparator = TrackComparators.byDescription;
@Override
public Integer apply(Track track) {
return Integer.valueOf(this.trackComparator.compare(
this.ref, track));
}
});
};
}