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();
	}
}

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 synchronized Data add(LogEntry entry) {
		facade.put(entry.getTime(), entry);
		return this;
	}

	public synchronized 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 synchronized 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 synchronized 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();
	}
}

add synchronized keyword to Data