package com.github.davidmoten.logan;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.TreeMap;

import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;

public class Data {

	private TreeMap<Long, Collection<LogEntry>> map;
	private ListMultimap<Long, LogEntry> facade;

	public Data() {
		map = Maps.newTreeMap();
		facade = Multimaps.newListMultimap(map, new Supplier<List<LogEntry>>() {
			@Override
			public List<LogEntry> get() {
				return Lists.newArrayList();
			}
		});
	}

	public Data add(LogEntry entry) {
		facade.put(entry.getTime(), entry);
		return this;
	}

	public Iterable<LogEntry> find(final long startTime, final long finishTime,
			final String name) {

		return new Iterable<LogEntry>() {

			@Override
			public Iterator<LogEntry> iterator() {
				return createIterator(startTime, finishTime, name);
			}

		};

	}

	private Iterator<LogEntry> createIterator(final long startTime,
			final long finishTime, String name) {

		return new Iterator<LogEntry>() {

			Long t = map.ceilingKey(startTime);
			Long last = map.floorKey(finishTime);
			Iterator<LogEntry> it = null;

			@Override
			public boolean hasNext() {
				if (it == null || !it.hasNext())
					return t != null && t < last;
				else
					return it.hasNext();
			}

			@Override
			public LogEntry next() {
				while (it == null || !it.hasNext()) {
					it = map.get(t).iterator();
					t = map.higherKey(t);
				}
				return it.next();
			}

			@Override
			public void remove() {
				throw new RuntimeException("not implemented");
			}

		};

	}

	public Buckets find(final BucketQuery query) {

		Iterable<LogEntry> entries = find(query.getStartTime().getTime(),
				query.getFinishTime(), query.getName());
		Iterable<LogEntry> filtered = Iterables.filter(entries,
				new Predicate<LogEntry>() {
					@Override
					public boolean apply(LogEntry entry) {
						return entry.getProperties().containsKey(
								query.getName());
					}
				});
		Buckets buckets = new Buckets(query);
		for (LogEntry entry : filtered) {
			String s = entry.getProperties().get(query.getName());
			try {
				double d = Double.parseDouble(s);
				buckets.add(entry.getTime(), d);
			} catch (NumberFormatException e) {
				// ignored value because non-numeric
			}
		}

		return buckets;
	}

	public long getNumEntries() {
		return facade.size();
	}
}

@@ -768,24 +768,37 @@
 %09%7D%0A%0A%09public 
+synchronized 
 Data add(Log
@@ -812,16 +812,16 @@
 ntry) %7B%0A
-
 %09%09facade
@@ -873,24 +873,37 @@
 %09%7D%0A%0A%09public 
+synchronized 
 Iterable%3CLog
@@ -935,17 +935,20 @@
 artTime,
- 
+%0A%09%09%09
 final lo
@@ -957,28 +957,25 @@
  finishTime,
-%0A%09%09%09
+ 
 final String
@@ -1157,16 +1157,29 @@
 private 
+synchronized 
 Iterator
@@ -1204,16 +1204,20 @@
 terator(
+%0A%09%09%09
 final lo
@@ -1221,36 +1221,33 @@
  long startTime,
-%0A%09%09%09
+ 
 final long finis
@@ -1867,16 +1867,29 @@
 %09public 
+synchronized 
 Buckets 

add synchronized keyword to Data