//! Environment call handling routines

use {alloc::boxed::Box, core::cell::LazyCell, hbvm::mem::Address};

use crate::{
    holeybytes::{
        kernel_services::{
            block_read, dt_msg_handler::dt_msg_handler, logging_service::log_msg_handler,
            service_definition_service::sds_msg_handler,
        },
        ExecThread, STACK_SIZE,
    },
    kmain::EXECUTOR,
    task::Executor,
};

use {
    crate::{arch, ipc::buffer::IpcBuffer, kmain::IPC_BUFFERS},
    hbvm::value::Value,
    log::{debug, error, info, trace},
};

#[cfg(target_arch = "x86_64")]
#[inline(always)]
unsafe fn x86_out<T: x86_64::instructions::port::PortWrite>(address: u16, value: T) {
    x86_64::instructions::port::Port::new(address).write(value);
}
#[cfg(target_arch = "x86_64")]
#[inline(always)]
unsafe fn x86_in<T: x86_64::instructions::port::PortRead>(address: u16) -> T {
    x86_64::instructions::port::Port::new(address).read()
}

#[inline(always)]
pub fn handler(thr: &mut ExecThread, pid: &usize) {
    let ecall_number = thr.vm.registers[2].cast::<u64>();

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

            let bounded = match thr.vm.registers[3] {
                Value(0) => false,
                Value(1) => true,
                _ => {
                    panic!("Bad");
                }
            };

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

            let mut buffs = IPC_BUFFERS.lock();

            let buff_id = arch::hardware_random_u64();
            buffs.insert(
                buff_id,
                match bounded {
                    false => IpcBuffer::new(false, 0),
                    true => IpcBuffer::new(true, length),
                },
            );
            thr.vm.registers[1] = hbvm::value::Value(buff_id);
        }
        2 => {
            log::error!("Oops, deleting buffers is not implemented.")
            // Delete buffer
        }
        3 => {
            // Send a message to a buffer
            let buffer_id = thr.vm.registers[3].cast::<u64>();
            let mem_addr = thr.vm.registers[4].cast::<u64>();
            let length = thr.vm.registers[5].cast::<u64>() as usize;
            trace!("IPC address: {:?}", mem_addr);

            unsafe { LazyCell::<Executor>::get_mut(&mut EXECUTOR) }
                .unwrap()
                .send_buffer(buffer_id as usize);

            match buffer_id {
                0 => match sds_msg_handler(&mut thr.vm, mem_addr, length) {
                    Ok(()) => {}
                    Err(err) => log::error!("Improper sds format: {err:?}"),
                },
                1 => match log_msg_handler(&mut thr.vm, mem_addr, length) {
                    Ok(()) => {}
                    Err(_) => log::error!("Improper log format"),
                },
                2 => {
                    use crate::holeybytes::kernel_services::mem_serve::memory_msg_handler;
                    match memory_msg_handler(&mut thr.vm, mem_addr, length) {
                        Ok(_) => {}
                        Err(_) => {}
                    }
                }
                #[cfg(target_arch = "x86_64")]
                3 => {
                    let msg_vec = block_read(mem_addr, length);
                    let msg_type = msg_vec[0];
                    match msg_type {
                        0 => unsafe {
                            let size = msg_vec[1];
                            let addr =
                                u16::from_le_bytes(msg_vec[2..4].try_into().unwrap_unchecked());
                            let value = match size {
                                0 => x86_in::<u8>(addr) as u64,
                                1 => x86_in::<u16>(addr) as u64,
                                2 => x86_in::<u32>(addr) as u64,
                                _ => panic!("Trying to read size other than: 8, 16, 32 from port."),
                            };
                            // info!("Read the value {} from address {}", value, addr);
                            thr.vm.registers[1] = hbvm::value::Value(value);
                        },
                        1 => unsafe {
                            let size = msg_vec[1];
                            let addr =
                                u16::from_le_bytes(msg_vec[2..4].try_into().unwrap_unchecked());
                            // info!("Setting address {}", addr);

                            match size {
                                0 => x86_out(addr, msg_vec[4]),
                                1 => x86_out(
                                    addr,
                                    u16::from_le_bytes(msg_vec[4..6].try_into().unwrap_unchecked()),
                                ),
                                2 => x86_out(
                                    addr,
                                    u32::from_le_bytes(msg_vec[4..8].try_into().unwrap_unchecked()),
                                ),
                                _ => panic!("How?"),
                            }
                        },
                        _ => {}
                    }
                }
                #[cfg(not(target_arch = "x86_64"))]
                3 => unimplemented!("TODO: implement whatever buffer 3 does for no x86_64"),
                // source of rng
                4 => {
                    let block = block_read(mem_addr, length);
                    block.chunks_mut(8.min(length)).for_each(|chunk| {
                        chunk.clone_from_slice(
                            &crate::arch::hardware_random_u64().to_le_bytes()[..chunk.len()],
                        );
                    });
                    thr.vm.registers[1] = hbvm::value::Value(mem_addr);
                }
                5 => match dt_msg_handler(&mut thr.vm, mem_addr, length) {
                    Ok(()) => {}
                    Err(_) => log::error!("Improper dt query"),
                },
                6 => unsafe {
                    let program = block_read(mem_addr, length);

                    // decode AbleOS Executable format
                    let header = &program[0..46];
                    let magic_slice = &header[0..3];
                    if magic_slice != [0x15, 0x91, 0xD2] {
                        log::error!("Invalid magic number at the start of executable.");
                        return;
                    }

                    let executable_format_version =
                        u32::from_le_bytes(header[3..7].try_into().unwrap());
                    let offset = if executable_format_version == 0 {
                        47
                    } else {
                        error!("Invalid executable format.");
                        return;
                    };

                    let code_length = u64::from_le_bytes(header[7..15].try_into().unwrap());
                    let data_length = u64::from_le_bytes(header[15..23].try_into().unwrap());
                    let end = (code_length + data_length) as usize;
                    log::debug!("{code_length} + {data_length} = {end}");

                    let thr2 = ExecThread::new(program[offset..end].into(), Address::new(0));
                    thr.vm.registers[1] = Value(
                        LazyCell::<Executor>::get_mut(&mut EXECUTOR)
                            .unwrap()
                            .spawn(Box::pin(async move {
                                if let Err(e) = thr2.await {
                                    log::error!("{e:?}");
                                }
                            })) as u64,
                    );
                    log::debug!("spawned a process");
                },
                7 => unsafe {
                    let mut thr2 = ExecThread::new(
                        thr.program,
                        thr.vm.pc.wrapping_sub(thr.program.as_ptr() as u64),
                    );
                    thr.stack_bottom
                        .copy_to_nonoverlapping(thr2.stack_bottom, STACK_SIZE);
                    thr2.vm.registers = thr.vm.registers;
                    thr2.vm.registers[1] = Value(0);

                    thr.vm.registers[1] = Value(
                        LazyCell::<Executor>::get_mut(&mut EXECUTOR)
                            .unwrap()
                            .spawn(Box::pin(async move {
                                if let Err(e) = thr2.await {
                                    log::error!("{e:?}");
                                }
                            })) as u64,
                    );
                    log::debug!("forked a process")
                },

                buffer_id => {
                    let mut buffs = IPC_BUFFERS.lock();
                    match buffs.get_mut(&buffer_id) {
                        Some(buff) => {
                            let msg_vec = block_read(mem_addr, length);
                            buff.push(msg_vec.to_vec());
                            debug!("Sent Message {:?} to Buffer({})", msg_vec, buffer_id);
                        }
                        None => {
                            log::error!("Access of non-existent buffer {}", buffer_id)
                        }
                    }
                }
            }
        }
        4 => {
            let buffer_id = thr.vm.registers[3].cast::<u64>();
            let map_ptr = thr.vm.registers[4].cast::<u64>();
            let max_length = thr.vm.registers[5].cast::<u64>();
            let mut buffs = IPC_BUFFERS.lock();
            let buff: &mut IpcBuffer = match buffs.get_mut(&buffer_id) {
                Some(buff) => buff,
                None => panic!(
                    "Failed to get buffer: id={buffer_id}, ptr={map_ptr}, length={max_length}"
                ),
            };

            let msg = match buff.pop() {
                Ok(msg) => msg,
                Err(_) => return,
            };
            if msg.len() > unsafe { max_length.try_into().unwrap_unchecked() } {
                info!("{}", max_length);
                error!("Message is too long to map in.");
            } else {
                unsafe {
                    let ptr = map_ptr as *mut u8;
                    ptr.copy_from_nonoverlapping(msg.as_ptr(), msg.len());
                }

                debug!("Recieve {:?} from Buffer({})", msg, buffer_id);
            }
        }
        5 => {
            #[cfg(target_arch = "x86_64")]
            {
                let r2 = thr.vm.registers[2].cast::<u64>();
                let x = hbvm::value::Value(unsafe { x86_in::<u8>(r2 as u16) } as u64);
                // info!("Read {:?} from Port {:?}", x, r2);
                thr.vm.registers[3] = x
            }
        }
        6 => {
            // Wait till interrupt
            use crate::kmain::EXECUTOR;
            let interrupt_type = thr.vm.registers[3].cast::<u8>();
            debug!("Interrupt subscribed: {}", interrupt_type);
            unsafe {
                LazyCell::<Executor>::get_mut(&mut EXECUTOR)
                    .unwrap()
                    .interrupt_subscribe(*pid, interrupt_type);
            }
        }
        7 => {
            // Wait till buffer
            use crate::kmain::EXECUTOR;
            let buffer_id = thr.vm.registers[3].cast::<u64>() as usize;
            debug!("Buffer subscribed: {}", buffer_id);
            unsafe {
                LazyCell::<Executor>::get_mut(&mut EXECUTOR)
                    .unwrap()
                    .buffer_subscribe(*pid, buffer_id);
            }
        }
        _ => {
            log::error!("Syscall unknown {:?}{:?}", ecall_number, thr.vm.registers);
        }
    }
}

#[derive(Debug)]
pub enum LogError {
    NoMessages,
    InvalidLogFormat,
}