//! Environment call handling routines

use {
    super::{mem::Memory, Vm},
    crate::{arch, holeybytes::mem, ipc::buffer::IpcBuffer, kmain::IPC_BUFFERS},
    alloc::string::String,
    log::{debug, error, info, trace, warn},
};

pub fn handler(vm: &mut Vm) {
    let r1 = vm.registers[1].cast::<u64>();

    // debug!("Ecall number {:?}", r1);
    // trace!("Register dump: {:?}", vm.registers);

    match r1 {
        0 => {
            // TODO: explode computer
            // hello world ecall
            for x in 0u64..=255 {
                vm.registers[x as usize] = x.into();
            }
        }
        1 => {
            // Make buffer

            let bounded = match vm.registers[2].cast::<u64>() {
                0 => false,
                1 => true,
                _ => {
                    panic!("Bad");
                }
            };

            let length = vm.registers[3].cast::<u64>();

            let mut buffs = IPC_BUFFERS.lock();
            let abc;

            match bounded {
                false => {
                    abc = IpcBuffer::new(false, 0);
                }
                true => {
                    abc = IpcBuffer::new(true, length);
                }
            };
            let buff_id = arch::hardware_random_u64();
            buffs.insert(buff_id, abc);
            debug!("Buffer ID: {}", buff_id);
            vm.registers[1] = hbvm::value::Value(buff_id);
        }
        2 => {
            // Delete buffer
        }
        3 => {
            // Send a message to a buffer
            let r2 = vm.registers[2].cast::<u64>();
            let r3 = vm.registers[3].cast::<u64>();
            let r4 = vm.registers[4].cast::<u64>();

            let buffer_id = r2;
            let mem_addr = r3;
            let length = r4 as usize;
            trace!("IPC address: {:?}", mem_addr);
            use alloc::vec::Vec;

            match buffer_id {
                1 => match log_msg_handler(vm, mem_addr, length) {
                    Ok(()) => {}
                    Err(err) => log::error!("Improper log format"),
                },
                2 => match memory_msg_handler(vm, mem_addr, length) {
                    Ok(()) => {}
                    Err(err) => log::error!("Improper log format"),
                },
                buffer_id => {
                    let mut buffs = IPC_BUFFERS.lock();

                    let mut buff = buffs.get_mut(&buffer_id).unwrap();

                    let mut msg_vec = vec![];

                    for x in 0..(length as isize) {
                        let xyz = mem_addr as *const u8;
                        let value = unsafe { xyz.offset(x).read() };
                        msg_vec.push(value);
                    }
                    buff.push(msg_vec.clone());
                    info!(
                        "Message {:?} has been sent to Buffer({})",
                        msg_vec, buffer_id
                    );
                    drop(buffs);
                }
            }
        }
        4 => {
            let r2 = vm.registers[2].cast::<u64>();

            let mut buffs = IPC_BUFFERS.lock();
            let mut buff = buffs.get_mut(&r2).unwrap();
            let msg = buff.pop();
            info!("Recieve {:?} from Buffer({})", msg, r2);
        }
        // 5
        _ => {
            log::error!("Syscall unknown {:?}{:?}", r1, vm.registers);
        }
    }
}

fn log_msg_handler(vm: &mut Vm, mem_addr: u64, length: usize) -> Result<(), LogError> {
    // let message_length = 8 + 8 + 8;
    // log::info!("Mem Addr 0x{:x?} length {}", mem_addr, length);
    let mut msg_vec = vec![];

    for x in 0..(length as isize) {
        let xyz = mem_addr as *const u8;
        let value = unsafe { xyz.offset(x).read() };
        // info!("{} - 0x{:x?} - {}", value, value, value as char);
        msg_vec.push(value);
    }
    let log_level = msg_vec.pop().unwrap();
    let strr = String::from_utf8(msg_vec).unwrap();

    // use LogLevel::*;
    let ll = match log_level {
        0 | 48 => error!("{}", strr),
        1 | 49 => warn!("{}", strr),
        2 | 50 => info!("{}", strr),
        3 | 51 => debug!("{}", strr),
        4 | 52 => trace!("{}", strr),
        _ => {
            return Err(LogError::InvalidLogFormat);
        }
    };

    Ok(())
}

#[derive(Debug)]
pub enum LogError {
    InvalidLogFormat,
}
use {alloc::vec, log::Record};

fn memory_msg_handler(vm: &mut Vm, mem_addr: u64, length: usize) -> Result<(), LogError> {
    let mut val = alloc::vec::Vec::new();
    for _ in 0..4096 {
        val.push(0);
    }
    info!("Block address: {:?}", val.as_ptr());
    vm.registers[1] = hbvm::value::Value(val.as_ptr() as u64);
    vm.registers[2] = hbvm::value::Value(4096);
    Ok(())
}