package resource_exchange_arena;

import resource_exchange_arena.parameters.UserParameters;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Random;

public class ResourceExchangeArena extends UserParameters {

    // Create a single Random object for generating random numerical data for the simulation, a single object exists to
    // allow for result replication given a specific user seed.
    static Random random = new Random();

    /**
     * This is the main method which runs the entire ResourceExchangeArena simulation.
     *
     * @param args Unused.
     * @exception IOException On input error.
     * @see IOException
     */
    public static void main(String[] args) throws IOException {
        switch (COMPARISON_LEVEL) {
            case 1:
                // Test user parameters with and without social capital for comparison.
                USE_SOCIAL_CAPITAL = false;
                runSimulationSet();
                System.out.println("********** 1 / 2 ENVIRONMENT VERSIONS COMPLETE **********");

                USE_SOCIAL_CAPITAL = true;
                runSimulationSet();
                System.out.println("********** 2 / 2 ENVIRONMENT VERSIONS COMPLETE **********");
                break;
            case 2:
                // As above but also test single agent type populations for reference.
                USE_SOCIAL_CAPITAL = false;
                SINGLE_AGENT_TYPE = true;
                SELECTED_SINGLE_AGENT_TYPE = SELFISH;
                runSimulationSet();
                System.out.println("********** 1 / 5 ENVIRONMENT VERSIONS COMPLETE **********");

                USE_SOCIAL_CAPITAL = false;
                SINGLE_AGENT_TYPE = true;
                SELECTED_SINGLE_AGENT_TYPE = SOCIAL;
                runSimulationSet();
                System.out.println("********** 2 / 5 ENVIRONMENT VERSIONS COMPLETE **********");

                USE_SOCIAL_CAPITAL = true;
                SINGLE_AGENT_TYPE = true;
                SELECTED_SINGLE_AGENT_TYPE = SOCIAL;
                runSimulationSet();
                System.out.println("********** 3 / 5 ENVIRONMENT VERSIONS COMPLETE **********");

                USE_SOCIAL_CAPITAL = false;
                SINGLE_AGENT_TYPE = false;
                runSimulationSet();
                System.out.println("********** 4 / 5 ENVIRONMENT VERSIONS COMPLETE **********");

                USE_SOCIAL_CAPITAL = true;
                SINGLE_AGENT_TYPE = false;
                runSimulationSet();
                System.out.println("********** 5 / 5 ENVIRONMENT VERSIONS COMPLETE **********");
                break;
            default:
                // Run only the set of parameters defined by the user.
                runSimulationSet();
        }

        // String version of starting ratios for file names.
        ArrayList<String> startingRatiosArray = new ArrayList<String>();

        for (int[] AGENT_TYPES : AGENT_TYPES_ARRAY) {
            String ratio = "";
            int typesListed = 0;
            for (int type : AGENT_TYPES) {
                if (typesListed != 0) {
                    ratio += "_";
                }
                typesListed++;
                ratio += Inflect.getHumanReadableAgentType(type);
            }
            startingRatiosArray.add(ratio);          
        }
    }
    
    private static void runSimulationSet() throws IOException {
        // Set the simulations initial random seed.
        random.setSeed(seed);

        // Create a directory to store the data output by all simulations being run.
        String dataOutputFolder = FOLDER_NAME + "/useSC_" + USE_SOCIAL_CAPITAL + "_AType_";
        if (!SINGLE_AGENT_TYPE) {
            dataOutputFolder += "mixed";
        } else {
            dataOutputFolder += Inflect.getHumanReadableAgentType(SELECTED_SINGLE_AGENT_TYPE);
        }

        Path dataOutputPath = Paths.get(dataOutputFolder);
        Files.createDirectories(dataOutputPath);

        // Stores the key data about all simulations for organisational purposes.
        File allSimulationsData = new File(dataOutputFolder, "allSimulationsData.txt");

        FileWriter allSimulationsDataWriter = new FileWriter(allSimulationsData);

        allSimulationsDataWriter.append("Simulation Information (all runs): \n\n");
        allSimulationsDataWriter.append("Single agent type: ").append(String.valueOf(SINGLE_AGENT_TYPE)).append("\n");
        if (SINGLE_AGENT_TYPE) {
            allSimulationsDataWriter.append("Agent type: ")
                    .append(String.valueOf(SELECTED_SINGLE_AGENT_TYPE)).append("\n");
        }
        allSimulationsDataWriter.append("Use social capital: ").append(String.valueOf(USE_SOCIAL_CAPITAL)).append("\n");
        allSimulationsDataWriter.append("Simulation runs: ").append(String.valueOf(SIMULATION_RUNS)).append("\n");
        allSimulationsDataWriter.append("Additional Days: ").append(String.valueOf(DAYS)).append("\n");
        allSimulationsDataWriter.append("Population size: ").append(String.valueOf(POPULATION_SIZE)).append("\n");
        allSimulationsDataWriter.append("Unique time slots: ").append(String.valueOf(UNIQUE_TIME_SLOTS)).append("\n");
        allSimulationsDataWriter.append("Slots per agent: ").append(String.valueOf(SLOTS_PER_AGENT)).append("\n");
        allSimulationsDataWriter.append("Simulation Information (specific run details): \n\n");


        // Percentage of learning agents converted to actual number of agents that can learn each day.
        int[] numberOfLearningAgents = new int[PERCENTAGE_OF_AGENTS_TO_EVOLVE_ARRAY.length];
        float onePercent = POPULATION_SIZE / 100.0f;
        for (int i = 0; i < PERCENTAGE_OF_AGENTS_TO_EVOLVE_ARRAY.length; i++) {
            int learningAgents = Math.round(onePercent * PERCENTAGE_OF_AGENTS_TO_EVOLVE_ARRAY[i]);
            numberOfLearningAgents[i] = learningAgents;
        }

        // Perform a parameter sweep for the key parameters being tested.
        int simVersionsCompleted = 0;
        parameterSweep:
        for (int[] AGENT_TYPES : AGENT_TYPES_ARRAY) {
            for (int i = 0; i < numberOfLearningAgents.length; i++) {

                String fileName;

                if (!SINGLE_AGENT_TYPE) {
                    fileName = "AE_" + PERCENTAGE_OF_AGENTS_TO_EVOLVE_ARRAY[i];

                    StringBuilder typeRatio = new StringBuilder("_SR_");
                    int typesListed = 0;
                    for (int type : AGENT_TYPES) {
                        if (typesListed != 0) {
                            typeRatio.append("_");
                        }
                        typesListed++;
                        typeRatio.append(Inflect.getHumanReadableAgentType(type));
                    }
                    fileName += typeRatio;
                } else {
                    fileName = "SR_" + Inflect.getHumanReadableAgentType(SELECTED_SINGLE_AGENT_TYPE);
                }

                String initialSeed = seed + "L";

                // The parameters about to be tested are stored so that it is clear what they were when looking at
                // the results.
                allSimulationsDataWriter.append("Seed: ").append(initialSeed).append("\n");
                allSimulationsDataWriter.append("Number of agents to evolve: ")
                        .append(String.valueOf(numberOfLearningAgents[i]))
                        .append("\n");
                if (!SINGLE_AGENT_TYPE) {
                    allSimulationsDataWriter.append("Starting ratio of agent types: ");
                    int typesListed = 0;
                    for (int type : AGENT_TYPES) {
                        if (typesListed != 0) {
                            allSimulationsDataWriter.append(" : ");
                        }
                        typesListed++;
                        allSimulationsDataWriter.append(Inflect.getHumanReadableAgentType(type));
                    }
                }
                allSimulationsDataWriter.append("\n\n");

                /*
                 * The arena is the environment in which all simulations take place.
                 *
                 * @param folderName String representing the output destination folder, used to organise output
                 *                   data.
                 * @param environmentTag String detailing specifics about the simulation environment.
                 * @param demandCurves Double arrays of demand used by the agents, when multiple curves are used
                 *                     the agents are split equally between the curves.
                 * @param availabilityCurve Integer array of energy availability used by the simulation.
                 * @param socialCapital Boolean value that determines whether or not social agents will utilise
                 *                      social capital.
                 * @param simulationRuns Integer value representing the number of simulations to be ran and
                 *                       averaged.
                 * @param days Integer value representing the number of days to be simulated.
                 * @param populationSize Integer value representing the size of the initial agent population.
                 * @param uniqueTimeSlots Integer value representing the number of unique time slots available in
                 *                        the simulation.
                 * @param slotsPerAgent Integer value representing the number of time slots each agent requires.
                 * @param numberOfAgentsToEvolve Integer value representing the number of Agents who's strategy
                 *                               will change at the end of each day.
                 * @param agentTypes Integer array containing the agent types that the simulation will begin with.
                 *                   The same type can exist multiple times in the array where more agents of one
                 *                   type are required.
                 * @param singleAgentType Boolean value specifying whether only a single agent type should exist,
                 *                        used for establishing baseline results.
                 * @param selectedSingleAgentType Integer value representing the single agent type to be modelled
                 *                                when singleAgentType is true.
                 * @param pythonExe String representing the system path to python environment executable.
                 * @param pythonPath String representing the system path to the python data visualiser.
                 * @exception IOException On input error.
                 * @see IOException
                 */
                new ArenaEnvironment(
                        dataOutputFolder,
                        fileName,
                        DEMAND_CURVES,
                        AVAILABILITY_CURVE,
                        USE_SOCIAL_CAPITAL,
                        SIMULATION_RUNS,
                        DAYS,
                        POPULATION_SIZE,
                        UNIQUE_TIME_SLOTS,
                        SLOTS_PER_AGENT,
                        numberOfLearningAgents[i],
                        AGENT_TYPES,
                        SINGLE_AGENT_TYPE,
                        SELECTED_SINGLE_AGENT_TYPE,
                        PYTHON_EXE,
                        PYTHON_PATH
                );

                simVersionsCompleted++;
                System.out.println("Simulation versions completed: " + simVersionsCompleted);

                if (SINGLE_AGENT_TYPE) {
                    break parameterSweep;
                }
            }
        }
        allSimulationsDataWriter.close();
    }
}
