#![allow(unused)]

use crate::{
    cpu::CPU, instruction_set::{Instruction, InstructionStatus, Instructions, isa::functor_to_name}, memory_manager::type_traits::TryToUsize, send_recv::{SendRecv, handle_send_recv}, tracing::TRACER
};
pub mod cpu;
pub mod instruction_set;
pub mod memory_manager;
pub mod send_recv;
pub mod utility;
pub mod json_to_instruction;
pub mod tracing;



#[derive(Debug, Clone)]
pub struct CoreInstructionsBuilder {
    core_instructions : Vec<CoreInstruction>
}

impl CoreInstructionsBuilder {
    pub fn new(size:usize) -> Self {
        let mut core_instructions = Vec::with_capacity(size);
        for _ in 0..=size {
            core_instructions.push(CoreInstruction::empty());
        }
        Self { core_instructions }
    }

    pub fn build(self) -> Vec<CoreInstruction> {
        self.core_instructions
    }

    pub fn set_core(&mut self, core : impl TryToUsize, core_instruction : Instructions) -> &mut Self{
        self.core_instructions[core.try_into().expect("Set core with not valid size")] = core_instruction.into();
         self
    }
}


#[derive(Debug, Clone)]
pub struct CoreInstruction {
    instructions: Instructions,
    program_counter: usize,
}

impl CoreInstruction {
    fn new(instructions: Instructions, program_counter: usize) -> Self {
        Self {
            instructions,
            program_counter,
        }
    }

    fn empty() -> Self {
        Self { instructions: Vec::new(), program_counter: 0 }
    }
}

impl From<Instructions> for CoreInstruction {
    fn from(value: Instructions) -> Self {
        CoreInstruction {
            instructions: value,
            program_counter: 0,
        }
    }
}

#[derive(Debug, Clone)]
pub struct Executable {
    cpu: CPU,
    core_instructions: Vec<CoreInstruction>,
    send_recv : SendRecv,
}

impl Executable {
    pub fn new(cpu: CPU, core_instructions: Vec<CoreInstruction>) -> Self {
        let num_core = cpu.num_core();
        let send_recv = SendRecv::new(num_core);
        assert_eq!(num_core, core_instructions.len(), "Some core doesn't have is list of istruction (required even if empty)");
        Self {
            cpu,
            core_instructions,
            send_recv
        }
    }

    pub fn execute(&mut self) {
        let Self {
            cpu,
            core_instructions,
            send_recv
        } = self;
        let mut cpu_progressed = 0;
        let max_core = cpu.num_core();
        let mut index_unit = 0;

        while (cpu_progressed > -2) {
            let mut core_result = InstructionStatus::Completed;
            while core_result.is_completed() && let Some(core_instruction) = core_instructions.get_mut(index_unit){
                core_result = InstructionStatus::NotExecuted;
                let CoreInstruction {
                    instructions,
                    program_counter,
                } = core_instruction;
                core_result = instructions
                    .get(*program_counter)
                    .map_or(InstructionStatus::default(), |inst: &Instruction| {
                        inst.execute(cpu)
                    });
                if core_result.is_completed() {
                    cpu_progressed = 0;
                    *program_counter += 1;
                }
            }
            if handle_send_recv(cpu, core_instructions, send_recv, core_result) { cpu_progressed = 0; }
            handle_wait_sync(cpu, core_instructions, core_result);
            index_unit = if index_unit + 1 >= max_core {
            cpu_progressed-=1;
                0
            } else {
                index_unit + 1
            };
        }
    }

    pub fn cpu(&self) -> &CPU {
        &self.cpu
    }

    pub fn cpu_mut(&mut self) -> &mut CPU {
        &mut self.cpu
    }

    pub fn dump(&self)  {
        let core_instructions = &self.core_instructions;
        for  (i, core_instruction) in  core_instructions.iter().enumerate() {
            eprintln!("INST OF CORE {}:", i);
            for inst in &core_instruction.instructions {
                inst.dump();
            }
        }
    }
}

fn handle_wait_sync(cpu: &mut CPU, core_instructions: &mut [CoreInstruction], core_result: InstructionStatus) {
}


#[cfg(test)]
mod test {
    use super::*;
    use crate::instruction_set::instruction_data::InstructionDataBuilder;
    use crate::instruction_set::{InstructionsBuilder, isa::*};

    #[test]
    fn test_only_host() {
        let mut cpu = CPU::new(0);
        cpu.host()
            .execute_store(0, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
        let mut inst_builder = InstructionsBuilder::new();
        let mut idata_build = InstructionDataBuilder::new();
        idata_build.set_core_indx(0).fix_core_indx();
        inst_builder.make_inst(sldi, idata_build.set_rdimm(1, 0).build());
        inst_builder.make_inst(sld, idata_build.set_rdr1(1, 1).build());
        inst_builder.make_inst(sldi, idata_build.set_rdimm(2, 8).build());
        inst_builder.make_inst(sld, idata_build.set_rdr1(2, 2).build());
        inst_builder.make_inst(sadd, idata_build.set_rdr1r2(2, 1, 2).build());
        let mut core_instruction = vec![inst_builder.build().into()];
        let mut executable = Executable::new(cpu, core_instruction);
        executable.execute();
        assert_eq!(executable.cpu_mut().host().register(2), 4, "Not sum to 4");
    }

    #[test]
    fn test_10_core_same_code() {
        let setup_core = |index: usize, cpu: &mut CPU| -> Instructions {
            cpu.core(index)
                .execute_store(0, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
            let mut inst_builder = InstructionsBuilder::new();
            let mut idata_build = InstructionDataBuilder::new();
            idata_build.set_core_indx(index as i32).fix_core_indx();
            inst_builder.make_inst(sldi, idata_build.set_rdimm(1, 0).build());
            inst_builder.make_inst(sld, idata_build.set_rdr1(1, 1).build());
            inst_builder.make_inst(sldi, idata_build.set_rdimm(2, 8).build());
            inst_builder.make_inst(sld, idata_build.set_rdr1(2, 2).build());
            inst_builder.make_inst(sadd, idata_build.set_rdr1r2(2, 1, 2).build());
            inst_builder.build()
        };

        let mut cpu = CPU::new(10);
        let mut core_instruction = Vec::new();
        for i in 0..cpu.num_core() {
            core_instruction.push(setup_core(i, &mut cpu).into())
        }

        let mut executable = Executable::new(cpu, core_instruction);
        executable.execute();
        for i in 0.. executable.cpu.num_core() {
            assert_eq!(executable.cpu_mut().core(i).register(2), 4, "Core {} not sum to 4", i);
        }
    }

    

}
