/*
 *
 * Setup.java
 *
 * Setup is a singleton object that runs on the initial mod setup to run critical checks to make sure the mod
 * is allowed to do things on the users filesystem (reading conf files, writing conf files, etc). It's quite
 * important for the mod to know this!
 *
*/

package jesse.keeblarcraft.Utils;

import java.util.List;

import org.apache.commons.io.FileUtils;
import org.spongepowered.include.com.google.common.io.Files;
import java.util.List;
import java.util.ArrayList;

import jesse.keeblarcraft.ConfigMgr.ConfigManager;
import jesse.keeblarcraft.Utils.ChatUtil.CONSOLE_COLOR;
import jesse.keeblarcraft.Utils.CustomExceptions.DIRECTORY_CREATE_EXCEPTION;
import jesse.keeblarcraft.Utils.CustomExceptions.DIRECTORY_DELETE_EXCEPTION;
import jesse.keeblarcraft.Utils.CustomExceptions.SETUP_FAILED_EXCEPTION;

// Singleton class is designed to help the mod set itself up and create all the important things. It does two things:
//
// Thing 1: If on initial setup of mod for first time; creates all necessary directories and files.
//          Doing this helps the mod know if it has permissions to do things, or if it shouldn't even bother!
//          Some functionality is disabled if the mod can't access the hard drive in various ways.
//
// Thing 2: On any sequential startup; it checks to make sure all the directories exist & replaces missing ones.
//          It will also do a state-check of the system to make sure we will have permissions to read & write.
//          If we do not have these, or only partial - then functionality may be disabled for runtime performance
public final class Setup {
    private static Setup static_inst;

    public Setup() {
        System.out.println(ChatUtil.ColoredString("Running system setup and checks...", CONSOLE_COLOR.BLUE));
    }

    // Returns the singleton object
    public static Setup GetInstance() {
        if (static_inst == null) {
            static_inst = new Setup();
        }

        return static_inst;
    }

    /// Unit testing functions below
    public static Boolean has_read = false;
    public static Boolean has_write = false;

    // First time setup variables
    private static final List<String> DIRECTORY_LIST = new ArrayList<String>() {{
        add("notes");       // Expect 1 file per player!
        add("factions");    // Expect 1 file per faction!
        add("story");       // Expect 1 file per story chapter!
        add("commands");    // Expect 1 file per command that's configurable!
        add("events");      // Expect 1 file per event that is configurable!
    }};

    // These will be top-level config files above the directories this mod creates
    private static final List<String> FILE_LIST = new ArrayList<String>() {{
        add("story/story.json");      // Big config file, determines when players can do certain things at different story levels
        add("factions/factions.json");   // General configuration file for factions stuff
        add("events/1events.json");     // General configuration file for story events!
        add("general.json");    // The super general configuration file! (May be removed)
    }};

    // RunChecks()
    //
    // Checks if we are able to create necessary directories and run reading over the file system for the current
    // directory this mod is maintained in. This will return false if any checks fail; but individual checks can
    // be accessed in class variables (above)
    public Boolean RunChecks() {
        ConfigManager conf = new ConfigManager();

        // Create directory check
        try {
            has_write = conf.CreateDirectory("test_dir");
            System.out.println(ChatUtil.ColoredString("test_dir created! has write: ", CONSOLE_COLOR.GREEN) + (has_write ? ChatUtil.ColoredString("YES", CONSOLE_COLOR.YELLOW) : ChatUtil.ColoredString("NO", CONSOLE_COLOR.YELLOW)));
        } catch (DIRECTORY_CREATE_EXCEPTION e) {
            System.out.println(ChatUtil.ColoredString("Failed to create test directory or it already exists", CONSOLE_COLOR.MAGENTA));
            has_write = false;
        }

        // Write to disk then read that data back
        if (has_write) {
            try {
                has_write = conf.CreateFile("test_dir/test_note.txt");
                has_write = conf.WriteToFile("test_dir/test_note.txt", "test_write_read", "w");

                List<String> lines = conf.GetFile("test_dir/test_note.txt");
                if (lines.size() == 0) {
                    has_read = false;
                } else {
                    has_read = true;
                }
            } catch (Exception e) {
                System.out.println(ChatUtil.ColoredString("Failed to create or write to test dir file ", CONSOLE_COLOR.RED));
                has_read = false;
            }
        }

        // Delete directory if created (it's a temporary dir)
        if (has_write) {
            try {
                has_write = conf.DeleteFile("test_dir/test_note.txt");
                has_write = conf.DeleteDirectory("test_dir");
            } catch (Exception e) {
                System.out.println(ChatUtil.ColoredString("Lost access to writing mid-way", CONSOLE_COLOR.RED));
                has_write = false;
            }
        }
        //need to be able to take in raw booleans for coloredstrings functions
        System.out.println(ChatUtil.ColoredString("CHECKS DEBUG: Value of has_write: ", CONSOLE_COLOR.BLUE) + ChatUtil.ColoredString(has_write.toString(), CONSOLE_COLOR.YELLOW) + ChatUtil.ColoredString(". Value of has_read: ", CONSOLE_COLOR.BLUE) + ChatUtil.ColoredString(has_read.toString(), CONSOLE_COLOR.YELLOW));
        return has_write && has_read;
    }

    // RunSetup
    //
    // Primary function call that will execute when mod starts up
    public Boolean RunSetup() throws SETUP_FAILED_EXCEPTION {
        Boolean ret = false;

        // Setup can only complete if it has guarenteed we have read & write permissions
        if (ret = RunChecks()) {
            try {
                // Create necessary directories
                ConfigManager conf = new ConfigManager();
                for (Integer i = 0; i < DIRECTORY_LIST.size(); i++) {
                    if ( ! conf.DoesDirectoryExist(DIRECTORY_LIST.get(i))) {
                        conf.CreateDirectory(DIRECTORY_LIST.get(i));
                        System.out.println(ChatUtil.ColoredString("Creating directory ", CONSOLE_COLOR.GREEN) +  ChatUtil.ColoredString(DIRECTORY_LIST.get(i), CONSOLE_COLOR.YELLOW) + ChatUtil.ColoredString("...", CONSOLE_COLOR.GREEN));
                    } else {
                        System.out.println(ChatUtil.ColoredString("Directory ", CONSOLE_COLOR.BLUE) + conf.DoesDirectoryExist(DIRECTORY_LIST.get(i)) + ChatUtil.ColoredString(" already exists. Skipping... ", CONSOLE_COLOR.BLUE));
                    }
                }

                // Create necessary files
                for (Integer i = 0; i < FILE_LIST.size(); i++) {
                    if ( ! conf.DoesFileExist(FILE_LIST.get(i))) {
                        conf.CreateFile(FILE_LIST.get(i));
                        System.out.println(ChatUtil.ColoredString("Creating file ", CONSOLE_COLOR.GREEN) + ChatUtil.ColoredString(FILE_LIST.get(i), CONSOLE_COLOR.YELLOW) +  ChatUtil.ColoredString("...", CONSOLE_COLOR.GREEN));
                    } else {
                        System.out.println(ChatUtil.ColoredString("File ", CONSOLE_COLOR.BLUE) + conf.DoesDirectoryExist(FILE_LIST.get(i)) + ChatUtil.ColoredString(" already exists. Skipping...", CONSOLE_COLOR.BLUE));
                    }
                }
            } catch (Exception e) {
                throw new SETUP_FAILED_EXCEPTION();
            }
        } else {
            System.out.println(ChatUtil.ColoredString("RunChecks() failed in its process. This mod has deemed it does not have read or write privileges in its hosted area and will now exit.", CONSOLE_COLOR.RED));
            throw new SETUP_FAILED_EXCEPTION();
        }

        System.out.println(ChatUtil.ColoredString("DID SETUP COMPLETE SUCCESSFULLY? ", CONSOLE_COLOR.YELLOW) + (ret ? ChatUtil.ColoredString("YES", CONSOLE_COLOR.YELLOW) : ChatUtil.ColoredString("NO", CONSOLE_COLOR.YELLOW)));

        return ret;
    }
}