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

pub mod errors;
pub mod ext2;
pub mod vfs;

use ::ext2::sector::Size1024;
use alloc::sync::Arc;

use crate::{filesystem::vfs::VFS, handle::Handle, KERNEL_STATE};

use self::{
    errors::FsError,
    ext2::Ext2StorageDevice,
    vfs::{DirectoryEntry, FsNode},
};
use FsResult as Result;

pub type FsResult<T> = core::result::Result<T, FsError>;

/// The methods on this trait are to be used internally.
pub trait StorageDevice
where
    Self: Send,
{
    fn read(&self, node: &FsNode, offset: usize, size: usize, buffer: &mut Vec<u8>) -> Result<()>;
    fn write(&self, node: &FsNode, offset: usize, buffer: &[u8]) -> Result<()>;
    fn read_dir(&self, node: &FsNode, index: usize) -> Result<DirectoryEntry>;
    fn find_dir(&self, node: &FsNode, name: &str) -> Result<Handle>;
    // TODO: flush to disk

    fn root_node(&self) -> Arc<FsNode>;
    fn device_handle(&self) -> Handle;
}

pub fn init() -> Result<()> {
    let mut state = KERNEL_STATE.lock();
    let fs = load_fs()?;
    let mut vfs = VFS.lock();
    vfs.init(fs.root_node());
    state.add_storage_device(fs);
    Ok(())
}

fn load_fs() -> Result<Ext2StorageDevice<Size1024, Vec<u8>>> {
    let mut volume = Vec::new();
    volume.extend_from_slice(include_bytes!("../../../userland/root_fs/ext2.img"));

    Ext2StorageDevice::new(volume)
}

pub fn walk<S: Into<String>>(dir: Arc<FsNode>, path: S) {
    let path = path.into();
    if let Some(dir) = dir.directory() {
        for entry in dir {
            let fs_node = {
                let mut vfs = VFS.lock();
                vfs.fs_node(entry.node()).unwrap()
            };

            println!("{}/{} => {}", path, entry.name(), fs_node.inode());
            trace!("{entry:#?}");
            if entry.name() != "." && entry.name() != ".." {
                walk(fs_node, format!("{}/{}", path, entry.name()));
            }
        }
    }
}
