package resource_exchange_arena;

import java.util.ArrayList;

class SocialLearning {
    /**
     * To facilitate social learning, for the number of the agents who are able to consider changing their strategy,
     * an Agent is selected at random, and then a second agent is selected to be observed. The first agent selected
     * checks whether their performance was weaker than the agent observed, if so they have a chance to copy the
     * strategy used by the observed agent in the previous day, with the likelihood of copying their strategy
     * proportional to the difference between their individual satisfactions.
     *
     * @param agents Array List of all the agents that exist in the current 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 may change at the
     *                               end of each day.
     */
    SocialLearning(ArrayList<Agent> agents, int slotsPerAgent, int numberOfAgentsToEvolve) {
        // Copy agents to store previous results, this needs to be a deep copy and so a new cloned agent is made.
        int totalAgents = agents.size();
        double[][] previousPerformances = new double[totalAgents][3];
        for(Agent a: agents) {
            double type = (double) a.getAgentType();
            double sat = a.calculateSatisfaction(null);

            previousPerformances[a.agentID-1][0] = type;
            previousPerformances[a.agentID-1][1] = sat;
        }

        // Copy agents to store all agents that haven't yet been selected for social learning.
        ArrayList<Agent> unselectedAgents = new ArrayList<>(agents);

        // Agents who mutated can't do social learning.
        int learningSize = numberOfAgentsToEvolve;
        if (unselectedAgents.size() < learningSize) {
            learningSize = unselectedAgents.size();
        }
        for (int i = 0; i < learningSize; i++) {
            // Assign the selected agent another agents performance to 'retrospectively' observe.
            int observedPerformance = ResourceExchangeArena.random.nextInt(totalAgents);

            // Select an agent to learn.
            Agent learningAgent = unselectedAgents.get(ResourceExchangeArena.random.nextInt(unselectedAgents.size()));

            // Ensure the agent altering its strategy doesnt copy itself.
            while (learningAgent.agentID == observedPerformance) {
                observedPerformance = ResourceExchangeArena.random.nextInt(totalAgents);
            }

            // Copy the observed agents strategy if it is better than its own, with likelihood dependent on the
            // difference between the agents satisfaction and the observed satisfaction.
            double learningAgentSatisfaction = learningAgent.calculateSatisfaction(null);
            double observedAgentSatisfaction = previousPerformances[observedPerformance][1];
            if (Math.round(learningAgentSatisfaction * slotsPerAgent) < Math.round(observedAgentSatisfaction * slotsPerAgent)) {
                double difference = observedAgentSatisfaction - learningAgentSatisfaction;
                if (difference >= 0) {
                    double beta = 1;
                    double learningChance = 1 / (1 + (Math.exp(-beta * difference)));
                    double normalisedLearningChance = (learningChance * 2) - 1;

                    double threshold = ResourceExchangeArena.random.nextDouble();

                    if (normalisedLearningChance > threshold) {
                        int newType = (int) Math.round(previousPerformances[observedPerformance][0]);
                        learningAgent.setType(newType);
                    }
                }
            }
            unselectedAgents.remove(learningAgent);
        }
    }
}
