// SPDX-License-Identifier: MPL-2.0

use ostd::mm::VmIo;

use super::SyscallReturn;
use crate::{
    fs::{
        file::file_table::{RawFileDesc, get_file_fast},
        utils::PATH_MAX,
        vfs::{file_system::SuperBlock, path::FsPath},
    },
    prelude::*,
};

pub fn sys_statfs(path_ptr: Vaddr, statfs_buf_ptr: Vaddr, ctx: &Context) -> Result<SyscallReturn> {
    let user_space = ctx.user_space();
    let path_name = user_space.read_cstring(path_ptr, PATH_MAX)?;
    debug!(
        "path = {:?}, statfs_buf_ptr = 0x{:x}",
        path_name, statfs_buf_ptr,
    );

    let fs = {
        let path_name = path_name.to_string_lossy();
        let fs_path = FsPath::try_from(path_name.as_ref())?;
        let path = ctx
            .thread_local
            .borrow_fs()
            .resolver()
            .read()
            .lookup(&fs_path)?;
        path.fs()
    };

    let statfs = Statfs::from(fs.sb());
    user_space.write_val(statfs_buf_ptr, &statfs)?;
    Ok(SyscallReturn::Return(0))
}

pub fn sys_fstatfs(
    raw_fd: RawFileDesc,
    statfs_buf_ptr: Vaddr,
    ctx: &Context,
) -> Result<SyscallReturn> {
    debug!(
        "raw_fd = {}, statfs_buf_addr = 0x{:x}",
        raw_fd, statfs_buf_ptr
    );

    let fs = {
        let mut file_table = ctx.thread_local.borrow_file_table_mut();
        let file = get_file_fast!(&mut file_table, raw_fd.try_into()?);
        file.path().fs()
    };

    let statfs = Statfs::from(fs.sb());
    ctx.user_space().write_val(statfs_buf_ptr, &statfs)?;
    Ok(SyscallReturn::Return(0))
}

/// FS Stat
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Pod)]
struct Statfs {
    /// Type of filesystem
    f_type: u64,
    /// Optimal transfer block size
    f_bsize: usize,
    /// Total data blocks in filesystem
    f_blocks: usize,
    /// Free blocks in filesystem
    f_bfree: usize,
    /// Free blocks available to unprivileged user
    f_bavail: usize,
    /// Total inodes in filesystem
    f_files: usize,
    /// Free inodes in filesystem
    f_ffree: usize,
    /// Filesystem ID
    f_fsid: u64,
    /// Maximum length of filenames
    f_namelen: usize,
    /// Fragment size
    f_frsize: usize,
    /// Mount flags of filesystem
    f_flags: u64,
    /// Padding bytes reserved for future use
    f_spare: [u64; 4],
}

impl From<SuperBlock> for Statfs {
    fn from(sb: SuperBlock) -> Self {
        Self {
            f_type: sb.magic,
            f_bsize: sb.bsize,
            f_blocks: sb.blocks,
            f_bfree: sb.bfree,
            f_bavail: sb.bavail,
            f_files: sb.files,
            f_ffree: sb.ffree,
            f_fsid: sb.fsid,
            f_namelen: sb.namelen,
            f_frsize: sb.frsize,
            f_flags: sb.flags,
            f_spare: [0u64; 4],
        }
    }
}
