package resource_exchange_arena;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;

class Exchange {
    /**
     * With each exchange all agents form pairwise exchanges and are able to consider a trade with their partner for
     * one time slot.
     *
     * @param run Integer value identifying the current simulation run.
     * @param day Integer value representing the current day being simulated.
     * @param exchange Integer value representing the current exchange being simulated.
     * @param uniqueAgentTypes Integer ArrayList containing each unique agent type that exists when the
     *                         simulation begins.
     * @param agents Array List of all the agents that exist in the current simulation.
     * @param eachRoundDataCSVWriter Used to store data ragarding the state of the system at the end of each round.
     * @exception IOException On input error.
     * @see IOException
     */

    Boolean noExchanges = false;
    
    Exchange(
        int run,
            int day,
            int exchange,
            ArrayList<Integer> uniqueAgentTypes,
            ArrayList<Agent> agents,
            FileWriter eachRoundDataCSVWriter
    ) throws IOException {

        ArrayList<ArrayList<Integer>> advertisingBoard = new ArrayList<>();

        // Reset the check for whether each Agent has made an interaction this round.
        for (Agent a : agents) {
            a.setMadeInteraction(false);
        }

        // Exchanges start by Agents advertising time slots they may be willing to exchange.
        Collections.shuffle(agents, ResourceExchangeArena.random);
        for (Agent a : agents) {
            ArrayList<Integer> unlockedTimeSlots = a.publishUnlockedTimeSlots();
            if (!unlockedTimeSlots.isEmpty()) {
                ArrayList<Integer> advert = new ArrayList<>();
                advert.add(a.agentID);
                advert.addAll(unlockedTimeSlots);
                advertisingBoard.add(advert);
            }
        }

        // Each Agent has the opportunity to make exchange requests for advertised time slots.
        Collections.shuffle(agents, ResourceExchangeArena.random);
        for (Agent a : agents) {
            if (!a.madeInteraction()) {
                ArrayList<Integer> chosenAdvert = a.requestExchange(advertisingBoard);
                a.setMadeInteraction(true);
                if (!chosenAdvert.isEmpty()) {
                    // Select an unwanted time slot to offer in the exchange.
                    ArrayList<Integer> unwantedTimeSlots = a.publishUnlockedTimeSlots();
                    int selector = ResourceExchangeArena.random.nextInt(unwantedTimeSlots.size());
                    int unwantedTimeSlot = unwantedTimeSlots.get(selector);

                    ArrayList<Integer> request = new ArrayList<>();
                    request.add(a.agentID);
                    request.add(chosenAdvert.get(1));
                    request.add(unwantedTimeSlot);

                    // The agent who offered the requested time slot receives the exchange request.
                    for (Agent b : agents) {
                        if (b.agentID == chosenAdvert.get(0)) {
                            if (b.madeInteraction() == false) {
                                b.receiveExchangeRequest(request, a.getAgentType());
                                b.setMadeInteraction(true);
                                break;
                            }
                        }
                    }
                }
            }
        }

        // Agents who have received a request consider it.
        Collections.shuffle(agents, ResourceExchangeArena.random);
        for (Agent a : agents) {
            if (!a.getExchangeRequestReceived().isEmpty()) {
                boolean accepted = a.considerRequest();
                if (!accepted) {
                    for (Agent b : agents) {
                        if (b.agentID == a.getExchangeRequest().get(0)) {
                            b.requestRejected();
                            break;
                        }
                    }
                }
            }
        }


        int successfullExchanges = 0;

        // Agents confirm and complete approved requests if they are able to do so, and update their relations with
        // other Agents accordingly.
        Collections.shuffle(agents, ResourceExchangeArena.random);
        for (Agent a : agents) {
            if (a.getExchangeRequestApproved()) {
                ArrayList<Integer> offer = a.getExchangeRequestReceived();
                if (a.finalCheck(offer.get(1))) {
                    for (Agent b : agents) {
                        if (b.agentID == offer.get(0)) {
                            if (b.finalCheck(offer.get(2))) {
                                boolean scgain = b.completeRequestedExchange(offer, a.agentID, a.getAgentType());
                                boolean scloss = a.completeReceivedExchange(offer, b.getAgentType());
                                if (scgain) {
                                    a.gainedSocialCapital();
                                }
                                if (scloss) {
                                    b.lostSocialCapital();
                                }
                                successfullExchanges++;
                            }
                            break;
                        }
                    }
                }
                a.setExchangeRequestApproved(false);
            }
            // Clear the agents accepted offers list before the next exchange round.
            if (!a.getExchangeRequestReceived().isEmpty()) {
                a.setExchangeRequestReceived();
            }
        }

        if (successfullExchanges == 0) {
            noExchanges = true;
        }

        // The average end of round satisfaction is stored for each Agent type.
        // This data can later be averaged over simulation runs and added to the individual data file.
        for (int uniqueAgentType : uniqueAgentTypes) {
            double averageSatisfactionForType = CalculateSatisfaction.averageAgentSatisfaction(agents, uniqueAgentType);

            eachRoundDataCSVWriter.append(String.valueOf(run));
            eachRoundDataCSVWriter.append(",");
            
            eachRoundDataCSVWriter.append(String.valueOf(day));
            eachRoundDataCSVWriter.append(",");
            
            eachRoundDataCSVWriter.append(String.valueOf(exchange));
            eachRoundDataCSVWriter.append(",");
            
            eachRoundDataCSVWriter.append(String.valueOf(uniqueAgentType));
            eachRoundDataCSVWriter.append(",");

            eachRoundDataCSVWriter.append(String.valueOf(averageSatisfactionForType));
            eachRoundDataCSVWriter.append("\n");
        }

    }
}
