/* Copyright (c) 2015-2016 Boundless and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Distribution License v1.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/edl-v10.html
 *
 * Contributors:
 * Gabriel Roldan (Boundless) - initial implementation
 */
package org.locationtech.geogig.model.experimental.internal;

import java.util.Iterator;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;

import org.locationtech.geogig.model.ObjectId;
import org.locationtech.geogig.model.RevObject;
import org.locationtech.geogig.model.RevTree;
import org.locationtech.geogig.storage.ObjectStore;

import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Iterables;

class TreeCache {

    private final AtomicInteger idSequence = new AtomicInteger();

    private final BiMap<Integer, ObjectId> oidMapping = HashBiMap.create(10_000);

    private final LoadingCache<Integer, RevTree> cache;

    private final ObjectStore store;

    public TreeCache(final ObjectStore store) {
        this.store = store;

        final CacheLoader<Integer, RevTree> loader = new CacheLoader<Integer, RevTree>() {
            @Override
            public RevTree load(Integer key) throws Exception {
                ObjectId treeId = oidMapping.get(key);
                Preconditions.checkState(treeId != null, "No tree id mapped to " + key);
                RevTree tree = TreeCache.this.store.getTree(treeId);
                return tree;
            }
        };
        this.cache = CacheBuilder.newBuilder().concurrencyLevel(1).maximumSize(100_000)
                .recordStats().build(loader);
    }

    public RevTree getTree(final ObjectId treeId) {
        Integer internalId = oidMapping.inverse().get(treeId);
        final RevTree tree;
        if (internalId == null) {
            tree = store.getTree(treeId);
            getTreeId(tree);
            if (!tree.buckets().isEmpty()) {
                preload(Iterables.transform(tree.buckets().values(), (b) -> b.getObjectId()));
            }
        } else {
            tree = resolve(internalId.intValue());
        }
        return tree;
    }

    public RevTree resolve(final int leafRevTreeId) {
        RevTree tree;
        try {
            tree = cache.get(Integer.valueOf(leafRevTreeId));
        } catch (ExecutionException e) {
            throw Throwables.propagate(e);
        }
        Preconditions.checkNotNull(tree);
        return tree;
    }

    public Integer getTreeId(RevTree tree) {
        Integer cacheId = oidMapping.inverse().get(tree.getId());
        if (cacheId == null) {
            cacheId = Integer.valueOf(idSequence.incrementAndGet());
            cache.put(cacheId, tree);
        }
        return cacheId;
    }

    public void preload(Iterable<ObjectId> trees) {
        // Stopwatch sw = Stopwatch.createStarted();
        Iterator<RevObject> preloaded = store.getAll(trees);
        // int c = 0;
        while (preloaded.hasNext()) {
            getTreeId((RevTree) preloaded.next());
            // c++;
        }
        // System.err.printf("preloaded %d trees in %s\n", c, sw.stop());
    }
}
@@ -657,22 +657,20 @@
 odel.Rev
-Object
+Tree
 ;%0Aimport
@@ -690,37 +690,46 @@
 tech.geogig.
-model.RevTree
+storage.BulkOpListener
 ;%0Aimport org
@@ -1282,16 +1282,17 @@
 reate(10
+0
 _000);%0A%0A
@@ -2015,22 +2015,8 @@
     
-.recordStats()
 .bui
@@ -3065,24 +3065,75 @@
 tAndGet());%0A
+            oidMapping.put(cacheId, tree.getId());%0A
             
@@ -3255,61 +3255,8 @@
 ) %7B%0A
-        // Stopwatch sw = Stopwatch.createStarted();%0A
     
@@ -3271,22 +3271,20 @@
 ator%3CRev
-Object
+Tree
 %3E preloa
@@ -3311,31 +3311,54 @@
 rees
-);%0A        // int c = 0
+, BulkOpListener.NOOP_LISTENER, RevTree.class)
 ;%0A  
@@ -3419,18 +3419,8 @@
 eId(
-(RevTree) 
 prel
@@ -3446,103 +3446,9 @@
     
-    // c++;%0A        %7D%0A        // System.err.printf(%22preloaded %25d trees in %25s%5Cn%22, c, sw.stop());
+%7D
 %0A   

Fix new revtree builder's TreeCache missing integer -> oid mapping