package com.googlecode.totallylazy.collections;
import com.googlecode.totallylazy.functions.Function1;
import com.googlecode.totallylazy.functions.Function2;
import com.googlecode.totallylazy.functions.Callables;
import com.googlecode.totallylazy.First;
import com.googlecode.totallylazy.Option;
import com.googlecode.totallylazy.Pair;
import com.googlecode.totallylazy.predicates.Predicate;
import com.googlecode.totallylazy.predicates.Predicates;
import com.googlecode.totallylazy.Segment;
import com.googlecode.totallylazy.Sequences;
import java.util.Iterator;
import java.util.NoSuchElementException;
import static com.googlecode.totallylazy.Pair.pair;
import static com.googlecode.totallylazy.Unchecked.cast;
import static com.googlecode.totallylazy.predicates.Predicates.is;
import static com.googlecode.totallylazy.predicates.Predicates.not;
import static com.googlecode.totallylazy.collections.PersistentList.constructors.list;
import static com.googlecode.totallylazy.collections.PersistentList.constructors.reverse;
public class ListMap<K, V> extends AbstractMap<K, V> {
private PersistentList<Pair<K, V>> list;
private ListMap(PersistentList<Pair<K, V>> list) {
this.list = list;
}
public static <K, V> ListMapFactory<K, V> factory() {
return new ListMapFactory<K, V>();
}
public static <K, V> PersistentMap<K, V> emptyListMap(Class<K> kClass, Class<V> vClass) {
return emptyListMap();
}
public static <K, V> PersistentMap<K, V> emptyListMap() {
return listMap(PersistentList.constructors.<Pair<K, V>>empty());
}
public static <K, V> PersistentMap<K, V> listMap(K key, V value) {
return listMap(pair(key, value));
}
public static <K, V> PersistentMap<K, V> listMap(K key1, V value1, K key2, V value2) {
return listMap(Sequences.sequence(pair(key1, value1), pair(key2, value2)));
}
public static <K, V> PersistentMap<K, V> listMap(K key1, V value1, K key2, V value2, K key3, V value3) {
return listMap(Sequences.sequence(pair(key1, value1), pair(key2, value2), pair(key3, value3)));
}
public static <K, V> PersistentMap<K, V> listMap(K key1, V value1, K key2, V value2, K key3, V value3, K key4, V value4) {
return listMap(Sequences.sequence(pair(key1, value1), pair(key2, value2), pair(key3, value3), pair(key4, value4)));
}
public static <K, V> PersistentMap<K, V> listMap(K key1, V value1, K key2, V value2, K key3, V value3, K key4, V value4, K key5, V value5) {
return listMap(Sequences.sequence(pair(key1, value1), pair(key2, value2), pair(key3, value3), pair(key4, value4), pair(key5, value5)));
}
public static <K, V> PersistentMap<K, V> listMap(Iterable<? extends Pair<K, V>> pairs) {
return listMap(reverse(Sequences.sequence((pairs))));
}
public static <K, V> PersistentMap<K, V> listMap(Pair<K, V> pair) {
return listMap(list(pair));
}
public static <K, V> PersistentMap<K, V> listMap(PersistentList<Pair<K, V>> list1) {
return new ListMap<K, V>(list1);
}
@Override
public PersistentMap<K, V> empty() {
return emptyListMap();
}
@Override
public PersistentMap<K, V> cons(Pair<K, V> head) {
return contains(head.first()) ? listMap(map(list, replace(head))) : listMap(list.cons(head));
}
private static <A,B> PersistentList<B> map(PersistentList<A> list, Function1<A, B> mapper) {
if(list.isEmpty()) return cast(list);
return map(list.tail(), mapper).cons(mapper.apply(list.head()));
}
private Function1<Pair<K, V>, Pair<K, V>> replace(final Pair<K, V> newValue) {
return oldValue -> oldValue.first().equals(newValue.first()) ? newValue : oldValue;
}
@Override
public <C extends Segment<Pair<K, V>>> C joinTo(C rest) {
return list.joinTo(rest);
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public Pair<K, V> head() throws NoSuchElementException {
return list.head();
}
@Override
public Option<Pair<K, V>> headOption() {
return list.headOption();
}
@Override
public PersistentMap<K, V> tail() throws NoSuchElementException {
return listMap(list.tail());
}
@Override
public Option<V> lookup(K key) {
return list.toSequence().find(key(key)).map(Pair::getValue);
}
@Override
public PersistentMap<K, V> insert(K key, V value) {
return cons(Pair.pair(key, value));
}
@Override
public PersistentMap<K, V> delete(K key) {
return listMap(toSequence().reject(p -> p.getKey().equals(key)));
}
@Override
public int size() {
return list.size();
}
@Override
public PersistentList<Pair<K, V>> toPersistentList() {
return reverse(list);
}
@Override
public boolean contains(Object other) {
return list.exists(key(other));
}
@Override
public boolean exists(Predicate<? super K> predicate) {
return list.exists(key(predicate));
}
@Override
public Iterator<Pair<K, V>> iterator() {
return toPersistentList().iterator();
}
private Predicate<First<K>> key(Predicate<? super K> predicate) {
return Predicates.first(predicate);
}
private Predicate<First<K>> key(Object key) {
return key(is(key));
}
@Override
public boolean equals(Object obj) {
return obj instanceof ListMap && ((ListMap) obj).list.equals(list);
}
@Override
public int hashCode() {
return list.hashCode();
}
@Override
public String toString() {
return toPersistentList().toString();
}
@Override
public <S> S fold(S seed, Function2<? super S, ? super Pair<K, V>, ? extends S> callable) {
return list.toSequence().fold(seed, callable);
}
}