package com.elmakers.mine.bukkit.magic.command;

import com.elmakers.mine.bukkit.api.magic.MagicAPI;
import com.elmakers.mine.bukkit.utility.NMSUtils;
import org.apache.commons.lang.StringUtils;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class MagicConfigCommandExecutor extends MagicTabExecutor {

    public MagicConfigCommandExecutor(MagicAPI api) {
        super(api);
    }

    @Override
    public Collection<String> onTabComplete(CommandSender sender, String commandName, String[] args) {
        Set<String> options = new HashSet<>();
        if (!sender.hasPermission("Magic.commands.mconfig")) return options;

        if (args.length == 1) {
            addIfPermissible(sender, options, "Magic.commands.mconfig.clean", "clean");
        }
        return options;
    }

    @Override
    public boolean onCommand(CommandSender sender, Command command, String alias, String[] args) {
        if (!api.hasPermission(sender, "Magic.commands.mconfig"))
        {
            sendNoPermission(sender);
            return true;
        }

        if (args.length == 0)
        {
            sender.sendMessage("Usage: mconfig [clean]");
            return true;
        }

        String subCommand = args[0];
        if (!api.hasPermission(sender, "Magic.commands.mconfig." + subCommand)) {
            sendNoPermission(sender);
            return true;
        }

        if (subCommand.equals("clean")) {
            onMagicClean(sender, args.length > 1 ? args[1] : "");
        } else {
            sender.sendMessage("Usage: mconfig [clean]");
        }
        return true;
    }

    protected void onMagicClean(CommandSender sender, String configName) {
        List<String> configFiles = new ArrayList<>();
        if (!configName.isEmpty()) {
            configFiles.add(configName);
        } else {
            configFiles.add("spells");
            configFiles.add("wands");
            configFiles.add("paths");
            configFiles.add("mobs");
            configFiles.add("items");
            configFiles.add("crafting");
            configFiles.add("materials");
            configFiles.add("messages");
        }

        File pluginFolder = api.getPlugin().getDataFolder();
        for (String configFileName : configFiles) {
            sender.sendMessage(ChatColor.AQUA + "Checking " + ChatColor.DARK_AQUA + configFileName);
            try {
                File defaultsFile = new File(pluginFolder, "defaults/" + configFileName + ".defaults.yml");
                File configFile = new File(pluginFolder, configFileName + ".yml");

                YamlConfiguration cleanConfig = new YamlConfiguration();
                YamlConfiguration currentConfig = new YamlConfiguration();
                currentConfig.load(configFile);

                YamlConfiguration defaultConfig = new YamlConfiguration();
                defaultConfig.load(defaultsFile);

                Collection<String> allKeys = currentConfig.getKeys(true);
                for (String key : allKeys) {
                    Object defaultValue = defaultConfig.get(key);
                    Object configValue = currentConfig.get(key);

                    // Will be covered by children
                    if (configValue instanceof ConfigurationSection) continue;

                    if (areDifferent(configValue, defaultValue)) {
                        cleanConfig.set(key, configValue);
                    }
                }

                int originalTopSize = currentConfig.getKeys(false).size();
                int cleanTopSize = cleanConfig.getKeys(false).size();
                int cleanSize = cleanConfig.getKeys(true).size();
                int removedCount = allKeys.size() - cleanSize;
                if (removedCount > 0) {
                    int removedTopCount = originalTopSize - cleanTopSize;
                    sender.sendMessage(ChatColor.LIGHT_PURPLE + "Removed " + ChatColor.GOLD + removedTopCount
                            + ChatColor.LIGHT_PURPLE + " top-level sections and " + ChatColor.GOLD + removedCount
                            + ChatColor.LIGHT_PURPLE + " total sections."
                    );

                    File backupFile = new File(pluginFolder, configFileName + ".yml.bak");
                    if (backupFile.exists()) {
                        sender.sendMessage(ChatColor.YELLOW + "  Backup file exists, will not overwrite: " + backupFile.getName());
                    } else {
                        sender.sendMessage(ChatColor.DARK_PURPLE + "  Saved backup file to " + backupFile.getName() + ", delete this file if all looks good.");
                        copyFile(configFile, backupFile);
                    }
                    String[] lines = StringUtils.split(cleanConfig.saveToString(), '\n');
                    PrintWriter out = new PrintWriter(configFile);
                    out.println("#");
                    out.println("# Default options have been removed from the file via /mconfig clean");
                    out.println("#");
                    for (String line : lines) {
                        if (!line.startsWith(" ")) {
                            out.println("");
                        }
                        out.println(line);
                    }
                    out.close();;
                } else {
                    sender.sendMessage(ChatColor.GOLD + "Looks clean to me!");
                }

            } catch (Exception ex) {
                sender.sendMessage(ChatColor.RED + "An error occurred, please check logs!");
                ex.printStackTrace();
            }

        }
    }

    private static void copyFile(File srcFile, File destFile) throws IOException {
        FileInputStream input = new FileInputStream(srcFile);
        try {
            FileOutputStream output = new FileOutputStream(destFile);
            try {
                copyStream(input, output);
            } finally {
                output.close();
            }
        } finally {
            input.close();
        }
    }

    public static long copyStream(InputStream input, OutputStream output) throws IOException {
        byte[] buffer = new byte[4096];
        long count = 0L;

        int n;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
            count += (long)n;
        }

        return count;
    }

    @SuppressWarnings("unchecked")
    private boolean areDifferent(Object configValue, Object defaultValue) {
        if (defaultValue == null) return true;

        if (configValue instanceof ConfigurationSection || configValue instanceof Map) {
            if (!(defaultValue instanceof ConfigurationSection) && !(defaultValue instanceof Map)) return true;

            Map<String, Object> configMap = configValue instanceof ConfigurationSection ?
                    NMSUtils.getMap((ConfigurationSection)configValue) : (Map<String, Object>)configValue;

            Map<String, Object> defaultMap = defaultValue instanceof ConfigurationSection ?
                    NMSUtils.getMap((ConfigurationSection)defaultValue) : (Map<String, Object>)defaultValue;

            return !configMap.equals(defaultMap);
        }
        if (configValue instanceof List) {
             if (!(defaultValue instanceof List)) return true;

            List<Object> configList = (List<Object>)configValue;
            List<Object> defaultList = (List<Object>)defaultValue;
            if (configList.size() != defaultList.size()) return true;

            for (int i = 0; i < configList.size(); i++) {
                if (areDifferent(configList.get(i), defaultList.get(i))) return true;
            }

            return false;
        }

        return !defaultValue.equals(configValue);
    }
}

@@ -140,24 +140,101 @@
 y.NMSUtils;%0A
+import com.google.common.io.ByteStreams;%0Aimport com.google.common.io.Files;%0A%0A
 import org.a
@@ -5232,24 +5232,26 @@
         
-copy
 File
+s.copy
 (configF
@@ -6226,767 +6226,8 @@
  %7D%0A%0A
-    private static void copyFile(File srcFile, File destFile) throws IOException %7B%0A        FileInputStream input = new FileInputStream(srcFile);%0A        try %7B%0A            FileOutputStream output = new FileOutputStream(destFile);%0A            try %7B%0A                copyStream(input, output);%0A            %7D finally %7B%0A                output.close();%0A            %7D%0A        %7D finally %7B%0A            input.close();%0A        %7D%0A    %7D%0A%0A    public static long copyStream(InputStream input, OutputStream output) throws IOException %7B%0A        byte%5B%5D buffer = new byte%5B4096%5D;%0A        long count = 0L;%0A%0A        int n;%0A        while (-1 != (n = input.read(buffer))) %7B%0A            output.write(buffer, 0, n);%0A            count += (long)n;%0A        %7D%0A%0A        return count;%0A    %7D%0A%0A
     

Use guava utils to copy files