/* Copyright (c) 2013 OpenPlans. All rights reserved.
 * This code is licensed under the BSD New License, available at the root
 * application directory.
 */
package org.geogit.di;

import java.util.Map;

import org.geogit.storage.ConfigDatabase;
import org.geogit.storage.GraphDatabase;
import org.geogit.storage.ObjectDatabase;
import org.geogit.storage.RefDatabase;
import org.geogit.storage.StagingDatabase;

import com.google.inject.Inject;
import com.google.inject.Provider;

public class PluginsModule extends GeogitModule {
    protected void configure() {
        bind(ObjectDatabase.class).toProvider(PluginObjectDatabaseProvider.class);
        bind(StagingDatabase.class).toProvider(PluginStagingDatabaseProvider.class);
        bind(RefDatabase.class).toProvider(PluginRefDatabaseProvider.class);
        bind(GraphDatabase.class).toProvider(PluginGraphDatabaseProvider.class);
    }

    private static class PluginObjectDatabaseProvider extends FormatSelector<ObjectDatabase> {
        private final PluginDefaults defaults;

        @Override
        protected final VersionedFormat readConfig(ConfigDatabase config) {
            String format = null, version = null;
            try {
                format = config.get("storage.objects").orNull();
                version = config.get(format + ".version").orNull();
            } catch (RuntimeException e) {
                // ignore, the config may not be available when we need this.
            }
            if (format == null || version == null) {
                // .get, not .orNull. we should only be using the plugin providers when there are
                // plugins set up
                return defaults.getObjects().get();
            } else {
                return new VersionedFormat(format, version);
            }
        }

        @Inject
        public PluginObjectDatabaseProvider(PluginDefaults defaults, ConfigDatabase config,
                Map<VersionedFormat, Provider<ObjectDatabase>> plugins) {
            super(config, plugins);
            this.defaults = defaults;
        }
    }

    private static class PluginStagingDatabaseProvider extends FormatSelector<StagingDatabase> {
        private final PluginDefaults defaults;

        @Override
        protected final VersionedFormat readConfig(ConfigDatabase config) {
            String format = null, version = null;
            try {
                format = config.get("storage.staging").orNull();
                version = config.get(format + ".version").orNull();
            } catch (RuntimeException e) {
                // ignore, the config may not be available when we need this
            }

            if (format == null || version == null) {
                // .get, not .orNull. we should only be using the plugin providers when there are
                // plugins set up
                return defaults.getStaging().get();
            } else {
                return new VersionedFormat(format, version);
            }
        }

        @Inject
        public PluginStagingDatabaseProvider(PluginDefaults defaults, ConfigDatabase config,
                Map<VersionedFormat, Provider<StagingDatabase>> plugins) {
            super(config, plugins);
            this.defaults = defaults;
        }
    }

    private static class PluginRefDatabaseProvider extends FormatSelector<RefDatabase> {
        private final PluginDefaults defaults;

        @Override
        protected final VersionedFormat readConfig(ConfigDatabase config) {
            String format = null, version = null;
            try {
                format = config.get("storage.refs").orNull();
                version = config.get(format + ".version").orNull();
            } catch (RuntimeException e) {
                // ignore, the config may not be available when we need this.
            }

            if (format == null || version == null) {
                // .get, not .orNull. we should only be using the plugin providers when there are
                // plugins set up
                return defaults.getRefs().get();
            } else {
                return new VersionedFormat(format, version);
            }
        }

        @Inject
        public PluginRefDatabaseProvider(PluginDefaults defaults, ConfigDatabase config,
                Map<VersionedFormat, Provider<RefDatabase>> plugins) {
            super(config, plugins);
            this.defaults = defaults;
        }
    }

    private static class PluginGraphDatabaseProvider extends FormatSelector<GraphDatabase> {
        private final PluginDefaults defaults;

        @Override
        protected final VersionedFormat readConfig(ConfigDatabase config) {
            String format = null, version = null;
            try {
                format = config.get("storage.graph").orNull();
                version = config.get(format + ".version").orNull();
            } catch (RuntimeException e) {
                // ignore, the config may not be available when we need this
            }

            if (format == null || version == null) {
                // .get, not .orNull. we should only be using the plugin providers when there are
                // plugins set up
                return defaults.getGraph().get();
            } else {
                return new VersionedFormat(format, version);
            }
        }

        @Inject
        public PluginGraphDatabaseProvider(PluginDefaults defaults, ConfigDatabase config,
                Map<VersionedFormat, Provider<GraphDatabase>> plugins) {
            super(config, plugins);
            this.defaults = defaults;
        }
    }
}

@@ -406,16 +406,57 @@
 abase;%0A%0A
+import com.google.inject.AbstractModule;%0A
 import c
@@ -514,16 +514,49 @@
 rovider;
+%0Aimport com.google.inject.Scopes;
 %0A%0Apublic
@@ -588,13 +588,15 @@
 nds 
-Geogi
+Abstrac
 tMod
@@ -597,24 +597,43 @@
 actModule %7B%0A
+    %0A    @Override%0A
     protecte
@@ -726,32 +726,53 @@
 eProvider.class)
+.in(Scopes.SINGLETON)
 ;%0A        bind(S
@@ -832,32 +832,53 @@
 eProvider.class)
+.in(Scopes.SINGLETON)
 ;%0A        bind(R
@@ -934,24 +934,45 @@
 vider.class)
+.in(Scopes.SINGLETON)
 ;%0A        bi
@@ -1040,16 +1040,37 @@
 r.class)
+.in(Scopes.SINGLETON)
 ;%0A    %7D%0A

Make PluginsModule bind plugin providers to the singleton scope and it doesn't need to extend GeogitModule