/*
 * Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
 *
 * SPDX-License-Identifier: MPL-2.0
 */

use alloc::sync::Arc;
use hashbrown::HashMap;
use spin::Lazy;

use crate::{
    filesystem::{vfs::FsNode, StorageDevice},
    handle::{Handle, HandleResource},
};

pub static KERNEL_STATE: Lazy<spin::Mutex<KernelInternalState>> =
    Lazy::new(|| spin::Mutex::new(KernelInternalState::new()));

pub struct KernelInternalState {
    pub hostname: String,
    pub storage_devices: HashMap<Handle, Box<dyn StorageDevice>>,
    // FIXME: should this be per-process?
    file_table: HashMap<Handle, FileTableEntry>,
    should_shutdown: bool,
}

impl KernelInternalState {
    pub fn new() -> Self {
        Self {
            should_shutdown: false,
            storage_devices: HashMap::new(),
            file_table: HashMap::new(),
            hostname: "".to_string(),
        }
    }

    pub fn set_hostname(&mut self, hostname: String) {
        self.hostname = hostname;
    }

    pub fn add_storage_device(&mut self, device: impl StorageDevice + Send + 'static) {
        let device = Box::new(device);
        self.storage_devices.insert(device.device_handle(), device);
    }

    pub fn storage_device(&self, handle: Handle) -> Option<&dyn StorageDevice> {
        self.storage_devices.get(&handle).map(|d| &**d)
    }

    // TODO: implement flags here
    pub fn open_file_descriptor(&mut self, fs_node: Arc<FsNode>) -> Handle {
        let handle = Handle::new(HandleResource::FileDescriptor);
        self.file_table.insert(handle, FileTableEntry::new(fs_node));
        handle
    }

    pub fn file_descriptor(&self, handle: Handle) -> Option<&FileTableEntry> {
        self.file_table.get(&handle)
    }

    pub fn close_file_descriptor(&mut self, handle: Handle) {
        self.file_table.remove(&handle);
    }

    pub fn shutdown(&mut self) {
        self.should_shutdown = true;
    }

    pub fn update_state(&mut self) {
        if self.should_shutdown {
            crate::arch::shutdown();
        }
    }
}

impl Default for KernelInternalState {
    fn default() -> Self {
        Self::new()
    }
}

pub struct FileTableEntry {
    fs_node: Arc<FsNode>,
    // TODO: permissions, flags, owner, maybe cache stuff here?
}

impl FileTableEntry {
    fn new(fs_node: Arc<FsNode>) -> Self {
        Self { fs_node }
    }

    pub fn fs_node(&self) -> Arc<FsNode> {
        self.fs_node.clone()
    }
}
