use crate::simulator::translationtable::*;
use crate::simulator::machine::*;

pub struct MMU <'a>{
    /* Un MMU possède une seule référence vers une table des pages à un instant donné
    * Cette table est associée au processus courant
    * Cette référence peut etre mise a jour par exemple lors d'un switchTo
     */
    translationTable : Option<&'a mut TranslationTable>
}

impl <'a>MMU <'_>{

    fn create() -> MMU <'a>{

        MMU{
            translationTable : None
        }
    }

    pub fn mmu_read_mem(mmu : &mut MMU, machine : &Machine, virt_addr : u64, value : &mut u64) -> bool {
        //Pour plus tard eventuellement considerer le boutisme de notre mémoire
        //Peut etre pas si on fixe cela en BIG ENDIAN (octects poids fort au debut)
        let mut phy_addr : u64 = 0;
        let mut phy_addr_double_check : u64 = 0;
        let exc = MMU::translate(mmu, virt_addr, &mut phy_addr, false);
        MMU::translate(mmu, virt_addr, &mut phy_addr_double_check, false);

        //PB ICI car double appel a translate et on doit a chaque appel passer mmu en param
        true
    }

    pub fn mmu_write_mem(&mut self, machine : &mut Machine, virt_addr : u64, value : &mut u64) -> bool {
         //Pour plus tard eventuellement considerer le boutisme de notre mémoire
        //Peut etre pas si on fixe cela en BIG ENDIAN (octects poids fort au debut)



        true
    }

    pub fn translate(mmu : &mut MMU, virtAddr : u64, physAddr : &mut u64, writing : bool) -> ExceptionType {

        let vpn : u64 = virtAddr/PAGE_SIZE; //virtual page index
        let offset : u64 = virtAddr%PAGE_SIZE; //adresse intra page

        
            
            match &mut mmu.translationTable {
                None => {
                    println!("Error from translate : MMU refers to None (No page Table)");
                    return ExceptionType::ADDRESSERROR_EXCEPTION;
                }

                Some(table_ref) => {

                    //On verifie que notre index est valide
                    if vpn >= table_ref.get_max_num_pages(){
                        println!("Error from translate :: index is out of bound");
                        return ExceptionType::ADDRESSERROR_EXCEPTION;
                    }

                    /*Doc nachos dit que ce test sert a savoir si la page est mappée
                    *On teste les droit de lecture ecriture sur cette page
                    *A confirmer avc isabelle
                     */
                    if !table_ref.get_bit_read(vpn) && !table_ref.get_bit_write(vpn) {
                        println!("Error from translate :: virtual page # {} not mapped",vpn);
                        return ExceptionType::ADDRESSERROR_EXCEPTION;
                    }

                    //si on souhaite effectuer un acces lecture, on verifie que l'on dispose du droit d'acces sur cette page
                    if writing && !table_ref.get_bit_write(vpn) {
                        println!("Error from translate :: write access on a read only virtual page # {}",vpn);
                        return ExceptionType::READONLY_EXCEPTION;
                    }

                    //if the page is not yet in main memory, run the page fault manager
                    //Page manager not implemented yet
                    if !table_ref.get_bit_valid(vpn){
                        println!("Error from translate :: no valid correspondance");
                        println!("We need to update the page table by raising an exception -> not implemented");
                    
                        //Ici il faudra reverifier le bit valid apres intervention du page fault manager
                        return ExceptionType::ADDRESSERROR_EXCEPTION;
                    }

                    //Make sure that the physical adress is correct
                   if table_ref.get_physical_page(vpn) < 0 || table_ref.get_physical_page(vpn) >= (NUM_PHY_PAGE as i32) {
                        println!("Error from translate :: no valid correspondance");
                        return ExceptionType::BUSERROR_EXCEPTION;
                   }

                   //Set U/M bits to 1
                   if writing {
                        table_ref.set_bit_M(vpn);
                   }

                   table_ref.set_bit_U(vpn);

                   //on se permet ici la conversion du champs physical_page de i32 vers u64
                   //si cette valeur avait été signée, cela aurait été detecté juste au dessus, renvoyant une BUSERROR_EXCEPTION
                   *physAddr = (table_ref.get_physical_page(vpn) as u64)*PAGE_SIZE + offset;
                }
            }

        ExceptionType::NO_EXCEPTION
    }
 }