package se;

import java.util.ArrayList;
import java.util.Collections;

public class Scheduler {

    // liste  des processus a executer
    public ArrayList<Processus> list_pro_Exe = new ArrayList<>();
    // temps exe d'un processus
    private int time_Exe_proc;
    // temp de la file commence par 0
    private int time;
    // variable stocker valeur temps exe min d'un  processus afin determiner processus ayant min temps exe 
    int min;
    // variable stocker valeur priority max d'un  processus afin determiner processus ayant max priority 
    int max;
    //quantum
    int rr;
    // tab  pour sauvgarder  valeur exe processus pour roundrobin
    int tab_pro_time_exe[];
    Execution fenetre_execute;

    public Scheduler(ArrayList<Processus> list, String algo, Execution exe) {

        fenetre_execute = exe;
        switch (algo) {

            case "FCFS":
                exe_Algo(list, "FCFS");
                break;
            case "SJF":

                exe_Algo(list, "SJF");
                break;
            case "Priority":
                exe_Algo(list, "Priority");
                break;

            case "RoundRobin":

                tab_pro_time_exe = new int[list.size()];
                // sauvgarder temp fin
                for (int i = 0; i < list.size(); i++) {
                    list = trier(list);
                    tab_pro_time_exe[i] = list.get(i).getTempExe();
                    System.out.println(tab_pro_time_exe[i]);
                }
                // initilsaliser RR
                rr = list.get(0).getQuantum();
                exe_Algo(list, "RoundRobin");
                break;
        }
        float somme_temps_attent = 0;
        float somme_temps_round = 0;
// Calcule Temps Round and Temps Attent
        for (int i = 0; i < list.size(); i++) {

            list.get(i).setTemp_Round(list.get(i).getTempFin() - list.get(i).getTemp_arriv());
            list.get(i).setTemp_Att(list.get(i).getTemp_Round() - list.get(i).getTempExe());
            fenetre_execute.add_calcul_Table(list.get(i).getNomPre(), list.get(i).getTemp_Att(), list.get(i).getTemp_Round());
            somme_temps_attent += list.get(i).getTemp_Att();
            somme_temps_round += list.get(i).getTemp_Round();
        }

        fenetre_execute.calcul_Table_Moy((somme_temps_attent / list.size()), (somme_temps_round / list.size()));

        // Affichage Console Processus apre avoir termiiner exe
        System.out.println("*****************Fin Execution Etat processus*******************");
        for (int i = 0; i < list.size(); i++) {

            System.out.println("================" + list.get(i).getNomPre() + "=========================");
            System.out.println("|         Processus :         " + list.get(i).getNomPre() + "           |");
            System.out.println("|         ID :                " + list.get(i).getIdPrco() + "            |");
            System.out.println("|         Temps Arriver :     " + list.get(i).getTemp_arriv() + "            |");
            System.out.println("|         Temps Fin :         " + list.get(i).getTempFin() + "           |");
            System.out.println("|         Temps Attent        " + list.get(i).getTemp_Att() + "           |");
            System.out.println("|         Temps Round         " + list.get(i).getTemp_Round() + "           |");
            System.out.println("===========================================");
            System.out.println();
        }
    }

    // Methode execute algotime
    public void exe_Algo(ArrayList<Processus> list, String algo) {

        list = trier(list); // Trier List Processus

        ArrayList<Processus> fifo_pro_list = new ArrayList<>(); // LIste intermediaire pour ne pas vider list car p va etre vider dans la boucle si dessous

        for (int i = 0; i < list.size(); i++) {
            fifo_pro_list.add(list.get(i));

        }

        while (!fifo_pro_list.isEmpty() || !list_pro_Exe.isEmpty()) { // list p vide et file execution vide

            if (time >= fifo_pro_list.get(0).getTemp_arriv()) {
                // Ajouter processus  a la list
                list_pro_Exe = ajouterNouvArriv(fifo_pro_list, list_pro_Exe);
                // Exe processus 
                list_pro_Exe = Exe_Pro(list_pro_Exe, fifo_pro_list, algo);

            } else {
                // Temps Mort P1 terminer P2 n'est pas arriver 
                while (time < fifo_pro_list.get(0).getTemp_arriv()) {

                   
                    
                    time++;

                }
              
                for (int i = 0; i <= time; i++) {
                
                    fenetre_execute.add_diagramme("  ", 1, i);
            
                    
                }
                
            
            }
        }
        // initialiser valeur initale temps exe des processus apre avoir utiliser dans exe
        if (algo.equals("RoundRobin")) {
            for (int i = 0; i < tab_pro_time_exe.length; i++) {
                list = trier(list);
                list.get(i).setTempExe(tab_pro_time_exe[i]);
            }
        }
    }

    // Methode qui ajoute processus à list fifo_proc_pret d'apre time et temp arriver
    public ArrayList<Processus> ajouterNouvArriv(ArrayList<Processus> fifo_pro_list, ArrayList<Processus> list_pro_Exe) {

        for (int i = 0; i <= fifo_pro_list.size(); i++) {
            // Temps fille > temps arriver processus
            if (time >= fifo_pro_list.get(0).getTemp_arriv()) {
                list_pro_Exe.add(fifo_pro_list.get(0));
                fifo_pro_list.remove(0);
            } else {
                break;
            }
        }

        return list_pro_Exe;

    }

    // Execute Processus
    public ArrayList<Processus> Exe_Pro(ArrayList<Processus> list_pro_Exe, ArrayList<Processus> fifo_pro_list, String algo) {

        if (algo.equals("RoundRobin")) {

            list_pro_Exe.get(0).setTempDeb_exe(time);
            // Ajouter processus au tableau fenetre execute
            fenetre_execute.add_Exe_Table(list_pro_Exe.get(0).getNomPre(), String.valueOf(list_pro_Exe.get(0).getTempDeb_exe()), String.valueOf(list_pro_Exe.get(0).getTempFin()), "En cour");

            while (!list_pro_Exe.isEmpty()) {

                list_pro_Exe.get(0).setTempDeb_exe(time);
                list_pro_Exe.get(0).setTempFin(time);
                if (list_pro_Exe.get(0).getTempExe() != 0) {
                    if (rr != 0) {
                        list_pro_Exe.get(0).setTempExe(list_pro_Exe.get(0).getTempExe() - 1);

                        rr--;
                        time++;
                        fenetre_execute.add_diagramme(list_pro_Exe.get(0).getNomPre(), list_pro_Exe.get(0).getTempExe(), time);

// Ajouter processus exemple P1 en execution et P2 arrive  alors en ajoute P2
                        if (!fifo_pro_list.isEmpty()) {
                            list_pro_Exe = ajouterNouvArriv(fifo_pro_list, list_pro_Exe);
                        }
                    } else {

                        list_pro_Exe.get(0).setTempFin(time);

                        // reinitaliser quantum
                        rr = list_pro_Exe.get(0).getQuantum();

                        if (list_pro_Exe.size() != 1) {
                            // Ajouter processus en tete a la queue de la list
                            list_pro_Exe.add(list_pro_Exe.get(0));
                            // Enlever Element de la tete de la list
                            list_pro_Exe.remove(0);
                            // initialiser temp debut execution processsus 
                            list_pro_Exe.get(0).setTempDeb_exe(time);
                            // Ajoue de processus au tableau de interface execution
                            fenetre_execute.add_Exe_Table(list_pro_Exe.get(0).getNomPre(), String.valueOf(list_pro_Exe.get(0).getTempDeb_exe()), String.valueOf(list_pro_Exe.get(0).getTempFin()), "En cour");

                            for (int i = 1; i < list_pro_Exe.size(); i++) {
                                if (list_pro_Exe.get(list_pro_Exe.size() - 1).getIdPrco() != list_pro_Exe.get(i).getIdPrco()) {
                                    list_pro_Exe.get(i).setTempDeb_exe(time);
                                }
                                list_pro_Exe.get(i).setTempDeb_exe(time);
                                // Ajoue de processus au tableau de interface execution
                                fenetre_execute.add_Exe_Table(list_pro_Exe.get(i).getNomPre(), "", String.valueOf(list_pro_Exe.get(i).getTempFin()), "En Attent");

                            }

                        } else {
                            list_pro_Exe.add(list_pro_Exe.get(0));
                            list_pro_Exe.remove(0);
                            // Ajoue de processus au tableau de interface execution
                            fenetre_execute.add_Exe_Table(list_pro_Exe.get(0).getNomPre(), String.valueOf(list_pro_Exe.get(0).getTempDeb_exe()), String.valueOf(list_pro_Exe.get(0).getTempFin()), "En cour");

                        }

                    }
                } else {
                    //Réinitialiser  RR 
                    rr = list_pro_Exe.get(0).getQuantum();

                    // Ajouter tableau interface Exécution
                    fenetre_execute.add_Exe_Table(list_pro_Exe.get(0).getNomPre(), "", String.valueOf(list_pro_Exe.get(0).getTempFin()), "Terminer");
                    // Enlever Element de la file
                    list_pro_Exe.remove(0);
                    if (!list_pro_Exe.isEmpty()) {
                        list_pro_Exe.get(0).setTempDeb_exe(time);

                        fenetre_execute.add_Exe_Table(list_pro_Exe.get(0).getNomPre(), String.valueOf(list_pro_Exe.get(0).getTempDeb_exe()), String.valueOf(list_pro_Exe.get(0).getTempFin()), "En cour");

                        for (int i = 1; i < list_pro_Exe.size(); i++) {
                            if (list_pro_Exe.get(list_pro_Exe.size() - 1).getIdPrco() != list_pro_Exe.get(i).getIdPrco()) {
                                list_pro_Exe.get(i).setTempDeb_exe(time);
                            }
                            fenetre_execute.add_Exe_Table(list_pro_Exe.get(i).getNomPre(), "", String.valueOf(list_pro_Exe.get(i).getTempFin()), "En Attent");

                        }
                    }
                }
            }

        } else {
            while (!list_pro_Exe.isEmpty()) {
                if ("SJF".equals(algo)) {

                    min = process_min_tempExe(list_pro_Exe);
                    list_pro_Exe.get(min).setTempDeb_exe(time);


                    time_Exe_proc = list_pro_Exe.get(min).getTempExe();
                    fenetre_execute.add_Exe_Table(list_pro_Exe.get(min).getNomPre(), String.valueOf(list_pro_Exe.get(min).getTempDeb_exe()), String.valueOf(list_pro_Exe.get(min).getTempFin()), "En cour");

                } else {
                    if ("Priority".equals(algo)) {
                        max = process_max_Priority(list_pro_Exe);
                        list_pro_Exe.get(max).setTempDeb_exe(time);
                        time_Exe_proc = list_pro_Exe.get(max).getTempExe();
                        fenetre_execute.add_Exe_Table(list_pro_Exe.get(max).getNomPre(), String.valueOf(list_pro_Exe.get(max).getTempDeb_exe()), String.valueOf(list_pro_Exe.get(max).getTempFin()), "En cour");

                    } else {
                        list_pro_Exe.get(0).setTempDeb_exe(time);
                        time_Exe_proc = list_pro_Exe.get(0).getTempExe();
                        fenetre_execute.add_Exe_Table(list_pro_Exe.get(0).getNomPre(), String.valueOf(list_pro_Exe.get(0).getTempDeb_exe()), String.valueOf(list_pro_Exe.get(0).getTempFin()), "En cour");
                    }

                }
                while (time_Exe_proc > 0) {

                    time++;
                    time_Exe_proc--;
                    list_pro_Exe = Fin_Exe_proc(list_pro_Exe, algo, time); // NORMALEMENT METHODE AJOUTER APRES BOUCLE TERMINER MAIS A CAUSE DE LA SIMULATION
                    // CAR QUAND P1 TERMINE EST SUBITEMENT P2 ARRIVE ALORS SA CAUSE PROBLEM EXE CAUSE algo na  intelligence 
                    if (!fifo_pro_list.isEmpty()) {

                        if (time >= fifo_pro_list.get(0).getTemp_arriv()) {
                            list_pro_Exe = ajouterNouvArriv(fifo_pro_list, list_pro_Exe);
                        }
                    }

                }

            }

        }

        return list_pro_Exe;
    }

    // exe processus selon critire algo exemple fcfs indice de la fille c'est
    // processus ayant temps mine
    public ArrayList<Processus> Fin_Exe_proc(ArrayList<Processus> list_pro_Exe, String algo, int time) {
        if (time_Exe_proc == 0) {  // condiiton ajouter du a un problem de simulation car programme il faut que exe il n'a pas 
            //intelligence car quand P1 sort terminer  est subtiment P2 arrive cette condition regle se problem dans la boucle  while au Fin  Exe pro est appeller 
            switch (algo) {
                case "FCFS":
                    list_pro_Exe.get(0).setTempFin(time);

                    fenetre_execute.add_Exe_Table(list_pro_Exe.get(0).getNomPre(), "", String.valueOf(list_pro_Exe.get(0).getTempFin()), "Terminer");
                    fenetre_execute.add_diagramme(list_pro_Exe.get(max).getNomPre(), 0, time);
                    list_pro_Exe.remove(0);

                    break;

                case "SJF":

                    list_pro_Exe.get(min).setTempFin(time);
                    fenetre_execute.add_Exe_Table(list_pro_Exe.get(min).getNomPre(), "", String.valueOf(list_pro_Exe.get(min).getTempFin()), "Terminer");
                    fenetre_execute.add_diagramme(list_pro_Exe.get(max).getNomPre(), 0, time);
                    list_pro_Exe.remove(min);
                    break;

                case "Priority":
                    list_pro_Exe.get(max).setTempFin(time); // initialiser temp fin de processus terminer
                    fenetre_execute.add_Exe_Table(list_pro_Exe.get(max).getNomPre(), "", String.valueOf(list_pro_Exe.get(max).getTempFin()), "Terminer");
                    fenetre_execute.add_diagramme(list_pro_Exe.get(max).getNomPre(), 0, time);
                    list_pro_Exe.remove(max); // elever processus de la liste

                    break;

            }
        }

        return list_pro_Exe;
    }

    // Methode qui trie liste d'apre date debut
    public ArrayList<Processus> trier(ArrayList<Processus> list) {

        for (int i = 0; i < list.size(); i++) {

            for (int j = (i + 1); j < list.size(); j++) {
                if (list.get(i).getTemp_arriv() > list.get(j).getTemp_arriv()) {
                    Collections.swap(list, i, j);
                } else {
                    if (list.get(i).getTemp_arriv() == list.get(j).getTemp_arriv()) {
                        if (list.get(i).getIdPrco() > list.get(j).getIdPrco()) {
                            Collections.swap(list, i, j);
                        }
                    }
                }
            }
        }

        return list;
    }

    // Methode retourne adresse ddu indice minimal Temps Execution
    public int process_min_tempExe(ArrayList<Processus> list) {

        min = list.get(0).getTempExe();
        int indice = 0;

        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).getTempExe() < min) {
                min = list.get(i).getTempExe();
                indice = i;

            }
        }

        return indice;
    }

    // Methode qui retourne indice processus ayant plus grand Priority
    public int process_max_Priority(ArrayList<Processus> list) {
        int indice = 0;
        max = list.get(0).getPriority();

        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).getPriority() > max) {
                max = list.get(i).getPriority();
                indice = i;
            }
        }

        return indice;
    }

   
}