package com.conveyal.gtfs.model;

import org.mapdb.Fun;
import org.mapdb.Fun.Tuple2;

import java.util.AbstractMap;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentNavigableMap;

/**
 * A map of a single shape_id with points indexed by shape_point_sequence.
 * Backed by a submap, but eliminates the need to refer to shape points always by shape ID.
 * @author mattwigway
 */
public class ShapeMap implements Map<Integer, Shape> {
    private String shapeId;
    
    /** A map from (shape_id, shape_pt_sequence) to shapes */
    private Map<Tuple2, Shape> wrapped;

    public ShapeMap (ConcurrentNavigableMap<Tuple2, Shape> allShapes, String shapeId) {
        this.wrapped = allShapes.subMap(
                new Tuple2 (shapeId, 0),
                new Tuple2 (shapeId, Fun.HI)
                );
        this.shapeId = shapeId;
    }

    @Override
    public int size() {
        return wrapped.size();
    }

    @Override
    public boolean isEmpty() {
        return wrapped.isEmpty();
    }

    @Override
    public boolean containsKey(Object key) {
        return wrapped.containsKey(makeKey(key));
    }

    @Override
    public boolean containsValue(Object value) {
        return wrapped.containsValue(value);
    }

    @Override
    public Shape get(Object key) {		
        return wrapped.get(makeKey(key));
    }

    @Override
    public Shape put(Integer key, Shape value) {
        return wrapped.put(makeKey(key), value);
    }

    @Override
    public Shape remove(Object key) {
        return wrapped.remove(makeKey(key));
    }

    @Override
    public void putAll(Map<? extends Integer, ? extends Shape> m) {
        for (Integer i : m.keySet()) {
            wrapped.put(makeKey(i), m.get(i));
        }
    }

    @Override
    public void clear() {
        wrapped.clear();
    }

    @Override
    public Collection<Shape> values() {
        return wrapped.values();
    }

    // these two are hard because the sets have to update the corresponding map.
    // We currently just expose them as immutable sets in RAM, since all of the modification operations are optional. 
    @Override
    public Set<Integer> keySet() {
        Set<Integer> ret = new HashSet<Integer>(size());

        for (Tuple2<String, Integer> t : wrapped.keySet()) {
            ret.add(t.b);
        }

        // Don't let the user modify the set as it won't do what they expect (change the map)
        return Collections.unmodifiableSet(ret);
    }

    @Override
    public Set<Map.Entry<Integer, Shape>> entrySet() {
        // it's ok to pull all the values into RAM as this represents a single shape not all shapes

        HashSet<Map.Entry<Integer, Shape>> ret = new HashSet<Map.Entry<Integer, Shape>>(size());

        for (Map.Entry<Tuple2, Shape> e : wrapped.entrySet()) {
            ret.add(new AbstractMap.SimpleImmutableEntry(e.getKey().b, e.getValue()));
        }

        return Collections.unmodifiableSet(ret);
    }

    private Tuple2<String, Integer> makeKey (Object i) {
        return new Tuple2<String, Integer> (this.shapeId, (Integer) i);
    }

}

@@ -100,148 +100,9 @@
 til.
-AbstractMap;%0Aimport java.util.Collection;%0Aimport java.util.Collections;%0Aimport java.util.HashSet;%0Aimport java.util.Map;%0Aimport java.util.Set
+*
 ;%0Aim
@@ -2121,32 +2121,91 @@
 ger%3E keySet() %7B%0A
+        // use a linkedhashset so values come out in order%0A
         Set%3CInte
@@ -2219,24 +2219,30 @@
 t = new 
+Linked
 HashSet%3C
 Integer%3E
@@ -2237,23 +2237,10 @@
 Set%3C
-Integer%3E(size()
+%3E(
 );%0A%0A
@@ -2656,25 +2656,24 @@
  shapes%0A
-%0A
         
 HashSet%3C
@@ -2664,28 +2664,79 @@
         
-HashSet%3CMap.
+// use a linkedhashset so values come out in order%0A        Set%3C
 Entry%3CIn
@@ -2760,24 +2760,30 @@
 t = new 
+Linked
 HashSet%3C
 Map.Entr
@@ -2778,41 +2778,10 @@
 Set%3C
-Map.Entry%3CInteger, Shape%3E%3E(size()
+%3E(
 );%0A%0A

make sure shapes come out in order from ShapeMap.