package tc.oc.commons.bukkit.config;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.logging.Level;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;

import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import tc.oc.commons.bukkit.logging.MapdevLogger;
import tc.oc.minecraft.scheduler.MainThreadExecutor;
import tc.oc.file.PathWatcher;
import tc.oc.file.PathWatcherService;
import tc.oc.minecraft.api.configuration.InvalidConfigurationException;

/**
 * Base class for a configuration section that is loaded from the external
 * configuration folder in the maps repo if present, otherwise from the root
 * of this plugin's configuration.
 *
 * The external file is rechecked on some interval (1 minute by default).
 */
public abstract class ExternalConfiguration {

    @Inject private MapdevLogger mapdevLogger;
    @Inject private Configuration pluginConfig;

    private ConfigurationSection config;

    @Inject void init(@Named("configuration") Path configPath, PathWatcherService watcherService, MainThreadExecutor executor) throws IOException {
        final Path path = configPath.resolve(fileName() + ".yml");
        reload(path);
        watcherService.watch(path, executor, new PathWatcher() {
            @Override
            public void fileCreated(Path path) {
                reload(path);
            }

            @Override
            public void fileModified(Path path) {
                reload(path);
            }

            @Override
            public void fileDeleted(Path path) {
                reload(path);
            }
        });
    }

    private void reload(Path file) {
        try {
            final ConfigurationSection before = config;
            final ConfigurationSection after;
            if(Files.isRegularFile(file)) {
                after = new YamlConfiguration();
                ((YamlConfiguration) after).load(file.toFile());
            } else {
                after = pluginConfig.getSection(configName());
            }
            configChanged(before, after);
            config = after; // Don't change the config if the callback throws
        } catch (IOException | InvalidConfigurationException e) {
            mapdevLogger.log(Level.SEVERE, "Error loading " + fileName() + ".yml: " + e.getMessage(), e);
        }
    }

    protected abstract String configName();

    protected String fileName() {
        return configName();
    }

    protected void configChanged(@Nullable ConfigurationSection before, @Nullable ConfigurationSection after) throws InvalidConfigurationException {}

    protected ConfigurationSection config() {
        return config;
    }
}
