//! Environment call handling routines

use core::borrow::Borrow;

use crate::{
    allocator,
    holeybytes::kernel_services::{
        block_read,
        service_definition_service::{sds_msg_handler, SERVICES},
    },
};

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 ecall_number = vm.registers[2].cast::<u64>();

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

    match ecall_number {
        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[3].cast::<u64>() {
                0 => false,
                1 => true,
                _ => {
                    panic!("Bad");
                }
            };

            let length = vm.registers[4].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 buffer_id = vm.registers[3].cast::<u64>();
            let mem_addr = vm.registers[4].cast::<u64>();
            let length = vm.registers[5].cast::<u64>() as usize;
            trace!("IPC address: {:?}", mem_addr);
            use alloc::vec::Vec;

            match buffer_id {
                0 => match sds_msg_handler(vm, mem_addr, length) {
                    Ok(()) => {}
                    Err(err) => log::error!("Improper sds format"),
                },
                1 => match log_msg_handler(vm, mem_addr, length) {
                    Ok(()) => {}
                    Err(err) => log::error!("Improper log format"),
                },
                2 => {
                    use crate::holeybytes::kernel_services::mem_serve::memory_msg_handler;
                    match memory_msg_handler(vm, mem_addr, length) {
                        Ok(_) => {}
                        Err(_) => {}
                    }
                    //
                }
                buffer_id => {
                    let mut buffs = IPC_BUFFERS.lock();

                    match buffs.get_mut(&buffer_id) {
                        Some(buff) => {
                            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
                            );
                        }
                        None => {
                            log::error!("Access of non-existent buffer {}", buffer_id)
                        }
                    }
                    drop(buffs);
                }
            }
        }
        4 => {
            let buffer_id = vm.registers[3].cast::<u64>();

            let mut buffs = IPC_BUFFERS.lock();
            let mut buff = buffs.get_mut(&buffer_id).unwrap();
            let msg = buff.pop();
            info!("Recieve {:?} from Buffer({})", msg, buffer_id);
        }
        5 => {
            #[cfg(target_arch = "x86_64")]
            {
                let r2 = vm.registers[2].cast::<u64>();
                unsafe fn x86_in(address: u16) -> u32 {
                    x86_64::instructions::port::Port::new(address).read()
                }
                unsafe fn x86_out(address: u16, value: u32) {
                    x86_64::instructions::port::Port::new(address).write(value);
                }
                vm.registers[3] = hbvm::value::Value(unsafe { x86_in(r2 as u16) } as u64);
            }
        }
        // 5
        _ => {
            log::error!("Syscall unknown {:?}{:?}", ecall_number, 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 = block_read(mem_addr, length);

    let log_level = msg_vec.pop().unwrap();
    match String::from_utf8(msg_vec) {
        Ok(strr) => {
            // 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);
                }
            };
        }
        Err(e) => {
            error!("{:?}", e);
        }
    }

    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(())
// }