import AbsenceSchedules.*;
import InitialSchedules.*;
import ScheduleUtil.*;
import SchedulesRecovery.*;
import VariableDemandSchedules.BankVariableDemandSchedules;
import VariableDemandSchedules.ParametersVariableDemandSimulator;
import VariableDemandSchedules.VariableDemandSchedulesArray;
import jdistlib.rng.MersenneTwister;
import jdistlib.rng.RandomEngine;
import org.chocosolver.solver.Solution;

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;

@SuppressWarnings("unused")
public class MainClass {

    public static void main(String[] args) {
        
//        for (int i = 0 ; i <= 10 ; i++) {
            
//            ParametersAbsencesSimulator.probPresence = (double) i * 0.01 + 0.9; // Pour de 0.9 a 1.0 par increment de 0.01
            
//            System.out.print("Probabilité absence = " + (1-ParametersAbsencesSimulator.probPresence));

            // Placer une sous-boucle pour faire varier la probabilite d'augmentation de la demande.
            
                List<SchedulesArray> initialSchedulesArrayList = generateInitialSchedules();

                //List<BankVariableDemandSchedules> listBankVariableDemandSchedules = GenerateModifiedDemandSchedules(initialSchedulesArrayList);

                List<BankAbsenceSchedules> listBankAbsenceSchedules = GenerateAbsencesSchedules(initialSchedulesArrayList);

                List<BankRecoveredSchedules> bankRecoveredSchedulesOrderedByMeanCost = GenerateOptimalRecoveredSchedules(listBankAbsenceSchedules);

            @SuppressWarnings("unused") SchedulesWindows.ShowSolutionResultsFrame initialSchedulesViewer = new SchedulesWindows.ShowSolutionResultsFrame(bankRecoveredSchedulesOrderedByMeanCost);
            
//            int optimalFullTimeEmployee = bankRecoveredSchedulesOrderedByMeanCost.get(0).initialSchedulesArray.getNumberFullTimeEmployee();
//            System.out.println(optimalFullTimeEmployee + " ");
//        }
    }
    
    @SuppressWarnings("unused")
    private static List<SchedulesArray> generateInitialSchedules() {

        // Trouver les horaires des employes a temps plein possibles pour different ratio templein/temps partiel
        List<SchedulesArray> initialSchedulesArrayList = new ArrayList<>();
        
        InitialSchedules.ParametersInitialSchedules myScheduleParameters = new InitialSchedules.ParametersInitialSchedules();
        
        int minFullTimeEmployee = 2; //La solution avec 1 employes est trop lente
        
        int maxFullTimeEmployee = 9; //9 est le max d'employes a temps plein pour la presente requiredWorkforce
        
        int simulationNumber = 1;
        
        for (int numberFullTimeEmployees = minFullTimeEmployee ; numberFullTimeEmployees <= maxFullTimeEmployee ; numberFullTimeEmployees++) {
            
            System.out.println("numberFullTimeEmployees: " + numberFullTimeEmployees);
            
            InitialSchedules.ModelFullTimeEmployeesInitialSchedules myModelFullTimeEmployeesInitialSchedules
                    = new InitialSchedules.ModelFullTimeEmployeesInitialSchedules(myScheduleParameters, numberFullTimeEmployees);

            SolverInitialSchedules mySolverFullTimeEmployeesInitialSchedules = new SolverInitialSchedules(myModelFullTimeEmployeesInitialSchedules);
            List<Solution> myInitialFullTimeEmployeesSchedulesSolutions = mySolverFullTimeEmployeesInitialSchedules.findAllSolution();

            // Création de l'objet banque d'horaire, qui est une liste d'objets de type InitialSchedulesArray, qui contiennent chacun deux tableaux.
            for (Solution CurrentFullTimeEmployeesSolution : myInitialFullTimeEmployeesSchedulesSolutions) {
                FullTimeEmployeeScheduleArray fullTimeSchedulesArray = new FullTimeEmployeeScheduleArray(myModelFullTimeEmployeesInitialSchedules, CurrentFullTimeEmployeesSolution);

                InitialSchedules.ParametersInitialSchedules myPartTimeScheduleParameters = new InitialSchedules.ParametersInitialSchedules();
                myPartTimeScheduleParameters.adjustRequiredWorkforce(fullTimeSchedulesArray);

                InitialSchedules.ModelPartTimeEmployeesInitialSchedules myModelPartTimeEmployeesInitialSchedules
                    = new InitialSchedules.ModelPartTimeEmployeesInitialSchedules(myPartTimeScheduleParameters);

                SolverInitialSchedules mySolverPartTimeEmployeesInitialSchedules = new SolverInitialSchedules(myModelPartTimeEmployeesInitialSchedules);
                List<Solution> myInitialPartTimeEmployeesSchedulesSolutions = mySolverPartTimeEmployeesInitialSchedules.findAllSolution();
//                mySolverPartTimeEmployeesInitialSchedules.printStatistics();

                for (Solution CurrentPartTimeEmployeesSolution : myInitialPartTimeEmployeesSchedulesSolutions) {
                    PartTimeEmployeeScheduleArray partTimeSchedulesArray = new PartTimeEmployeeScheduleArray(myModelPartTimeEmployeesInitialSchedules, CurrentPartTimeEmployeesSolution);
                    SchedulesArray initialSchedulesArray = new SchedulesArray(fullTimeSchedulesArray, partTimeSchedulesArray, simulationNumber);
                    initialSchedulesArrayList.add(initialSchedulesArray);
                    simulationNumber++;
                }
                
            }
            
        }
        
        // Pour afficher les horaires initiales
        SchedulesWindows.ShowSchedulesFrame initialSchedulesViewer = new SchedulesWindows.ShowSchedulesFrame(initialSchedulesArrayList, "Initial Schedules"); // to be removed

        return initialSchedulesArrayList;

    }

    private static List<BankVariableDemandSchedules> GenerateModifiedDemandSchedules(List<SchedulesArray> initialSchedulesArrayList) {
        RandomEngine r = new MersenneTwister(987654321);
        List<BankVariableDemandSchedules> listBankVariableDemandSchedules = new ArrayList<>();
        List<SchedulesArray> listVariableDemandSchedules = new ArrayList<>(); // to be removed
        for (SchedulesArray initialSchedule : initialSchedulesArrayList) {
            BankVariableDemandSchedules bankVariableDemandSimulations = new BankVariableDemandSchedules(initialSchedule);
            for (int simNum = 0; simNum < ParametersVariableDemandSimulator.numberVariableDemandSimulation ; simNum++){
                VariableDemandSchedulesArray variableDemandSchedule = new VariableDemandSchedulesArray(initialSchedule,r);
                listVariableDemandSchedules.add(variableDemandSchedule);
                bankVariableDemandSimulations.addVariableDemandSchedules(variableDemandSchedule);
            }
            listBankVariableDemandSchedules.add(bankVariableDemandSimulations);
        }
        // Pour afficher les horaires avec demande modifiée
        SchedulesWindows.ShowSchedulesFrame variableDemandSchedulesViewer = new SchedulesWindows.ShowSchedulesFrame(listVariableDemandSchedules,"Variable Demand Schedules");

        return listBankVariableDemandSchedules;
    }

    private static List<BankAbsenceSchedules> GenerateAbsencesSchedules(List<SchedulesArray> initialSchedulesArrayList) {
        
        RandomEngine r = new MersenneTwister(123456789);
        List<BankAbsenceSchedules> listBankAbsenceSchedules = new ArrayList<>();
        List<SchedulesArray> listAbsenceSchedules = new ArrayList<>(); // to be removed
        for (SchedulesArray initialSchedule : initialSchedulesArrayList) {
            BankAbsenceSchedules bankAbsenceSimulations = new BankAbsenceSchedules(initialSchedule);
            for (int simNum = 0 ; simNum < ParametersAbsencesSimulator.numberAbsenceSimulations ; simNum++) {
                AbsenceSchedulesArray absenceSchedule = new AbsenceSchedulesArray(initialSchedule, r);
                listAbsenceSchedules.add(absenceSchedule); // to be removed
                bankAbsenceSimulations.addAbsenceSchedules(absenceSchedule);
            }
            listBankAbsenceSchedules.add(bankAbsenceSimulations);
        }
        
        // Pour afficher les horaires avec absence
        SchedulesWindows.ShowSchedulesFrame absenceSchedulesViewer = new SchedulesWindows.ShowSchedulesFrame(listAbsenceSchedules, "Absence Schedules"); // to be removed
        
        return listBankAbsenceSchedules;
    }
    
    private static List<BankRecoveredSchedules> GenerateOptimalRecoveredSchedules(List<BankAbsenceSchedules> listBankAbsenceSchedules) {
        
        BankRecoveredSchedulesComparator meanCostComparator = new BankRecoveredSchedulesComparator();
        PriorityQueue< BankRecoveredSchedules > bankRecoveredSchedulesOrderedByMeanCost = new PriorityQueue<>(meanCostComparator);
        List<SchedulesArray> listRecoveredSchedules = new ArrayList<>();  // to be removed
        for (BankAbsenceSchedules bankAbsenceSchedule : listBankAbsenceSchedules) {
            SchedulesArray initialSchedulesArray = bankAbsenceSchedule.initialSchedulesArray;
            BankRecoveredSchedules bankRecoveredSimulations = new BankRecoveredSchedules(initialSchedulesArray, bankAbsenceSchedule.bankAbsenceSimulation);
            int totalCostRecoveredSimulations = 0;
            for (AbsenceSchedulesArray absenceSchedule : bankAbsenceSchedule.bankAbsenceSimulation) {
                RecoveredSchedulesArray recoveredSchedule = new RecoveredSchedulesArray(absenceSchedule);
                listRecoveredSchedules.add(recoveredSchedule); // to be removed
                bankRecoveredSimulations.addRecoveredSchedules(recoveredSchedule);
                if (recoveredSchedule.totalScheduleCost != Integer.MAX_VALUE){
                    totalCostRecoveredSimulations += recoveredSchedule.totalScheduleCost;
                }
                
            }
            bankRecoveredSimulations.meanCostRecoveredSimulations = totalCostRecoveredSimulations / bankRecoveredSimulations.bankRecoveredSimulation.size();
            bankRecoveredSchedulesOrderedByMeanCost.add(bankRecoveredSimulations);
        }
        // Pour afficher les horaires de recouvrement
        SchedulesWindows.ShowSchedulesFrame recoveredSchedulesViewer = new SchedulesWindows.ShowSchedulesFrame(listRecoveredSchedules, "Recovered Schedules"); // to be removed
        
        List<BankRecoveredSchedules> listbankRecoveredSchedulesOrderedByMeanCost = new ArrayList<>();
        while(!bankRecoveredSchedulesOrderedByMeanCost.isEmpty()){
            BankRecoveredSchedules bankRecoveredSchedules = bankRecoveredSchedulesOrderedByMeanCost.poll();
            listbankRecoveredSchedulesOrderedByMeanCost.add(bankRecoveredSchedules);
        }
        
        return listbankRecoveredSchedulesOrderedByMeanCost;
    }
}
