//import AbsenceSchedules.AbsenceSchedulesArray;

import AbsenceSchedules.*;
import ScheduleUtil.*;
import SchedulesRecovery.*;
import jdistlib.rng.MersenneTwister;
import jdistlib.rng.RandomEngine;
import org.chocosolver.solver.Solution;
import org.chocosolver.solver.Solver;
import org.chocosolver.solver.exception.ContradictionException;

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

public class MainClass {

    public static void main(String[] args) {

        List<SchedulesArray> initialSchedulesArrayList = generateInitialSchedules();
        
        // TODO : 
        // Raffiner banques de solutions en prenant des horaires ayant de bonnes differences

        List<BankAbsenceSchedules> listBankAbsenceSchedules = GenerateAbsencesSchedules(initialSchedulesArrayList);

        List<BankRecoveredSchedules> bankRecoveredSchedulesOrderedByMeanCost = GenerateOptimalRecoveredSchedules(listBankAbsenceSchedules);

        // Afficher les solutions en ordre de cout de recouvrement et comparer avec solution optimale (nbr employes temps plein maximal).
        SchedulesWindows.ShowSolutionResultsFrame intialSchedulesViewer = new SchedulesWindows.ShowSolutionResultsFrame(bankRecoveredSchedulesOrderedByMeanCost);

        
    }

    private static List<SchedulesArray> generateInitialSchedules() {

        List<SchedulesArray> initialSchedulesArrayList = new ArrayList<>();

        InitialSchedules.ParametersInitialSchedules myScheduleParameters
                = new InitialSchedules.ParametersInitialSchedules();

        InitialSchedules.ModelInitialSchedules myModelInitialSchedules
                = new InitialSchedules.ModelInitialSchedules(myScheduleParameters);

        Solver solverInitialSchedules = myModelInitialSchedules.chocoModelInitialSchedules.getSolver();

        try {
            solverInitialSchedules.propagate();
        } catch (ContradictionException e) {
            System.err.println("Caught ContradictionException: " + e.getMessage());
            solverInitialSchedules.getEngine().flush();
        } catch (Exception e) {
            System.err.println("Caught Exception: " + e.getMessage());
        }

        solverInitialSchedules.showDashboard();
        
        /*solverInitialSchedules.setSearch(Search.domOverWDegSearch(myModelInitialSchedules.allWorkPeriods));*/
//        solverInitialSchedules.setRestartOnSolutions(); /*Sans restart, toutes les horaires sont identiques*/
        solverInitialSchedules.limitSolution(5);
        //solverInitialSchedules.limitTime(10000);
        List<Solution> myInitialSchedulesSolutions;
//        myInitialSchedulesSolutions = solverInitialSchedules.findAllOptimalSolutions
//          (myModelInitialSchedules.totalEmployeesCost, myModelInitialSchedules.chocoModelInitialSchedules.MINIMIZE);

//        solverInitialSchedules.findSolution();
        myInitialSchedulesSolutions = solverInitialSchedules.findAllSolutions();
        solverInitialSchedules.printStatistics();

        // Création de l'objet banque d'horaire, qui est une liste d'objets de type InitialSchedulesArray, qui contiennent chacun deux tableaux.
        int simulationNumber = 1;
        for (Solution CurrentSolution : myInitialSchedulesSolutions) {
            SchedulesArray isa = new SchedulesArray(myModelInitialSchedules, CurrentSolution, simulationNumber);
            initialSchedulesArrayList.add(isa);
            simulationNumber++;
        }
        
        // Pour afficher les horaires initiales
        SchedulesWindows.ShowSchedulesFrame intialSchedulesViewer = new SchedulesWindows.ShowSchedulesFrame(initialSchedulesArrayList, "Initial Schedules"); // to be removed

        return initialSchedulesArrayList;

    }

    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);
                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;
    }
}
