use core::{fmt::Debug, ptr::null};

use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec};
use system_error::SystemError;

use crate::{
    driver::base::block::SeekFrom,
    filesystem::vfs::{
        file::{File, FileMode},
        ROOT_INODE,
    },
    libs::elf::ELF_LOADER,
    mm::{
        ucontext::{AddressSpace, UserStack},
        VirtAddr,
    },
};

/// 系统支持的所有二进制文件加载器的列表
const BINARY_LOADERS: [&'static dyn BinaryLoader; 1] = [&ELF_LOADER];

pub trait BinaryLoader: 'static + Debug {
    /// 检查二进制文件是否为当前加载器支持的格式
    fn probe(self: &'static Self, param: &ExecParam, buf: &[u8]) -> Result<(), ExecError>;

    fn load(
        self: &'static Self,
        param: &mut ExecParam,
        head_buf: &[u8],
    ) -> Result<BinaryLoaderResult, ExecError>;
}

/// 二进制文件加载结果
#[derive(Debug)]
pub struct BinaryLoaderResult {
    /// 程序入口地址
    entry_point: VirtAddr,
}

impl BinaryLoaderResult {
    pub fn new(entry_point: VirtAddr) -> Self {
        Self { entry_point }
    }

    pub fn entry_point(&self) -> VirtAddr {
        self.entry_point
    }
}

#[allow(dead_code)]
#[derive(Debug)]
pub enum ExecError {
    /// 二进制文件不可执行
    NotExecutable,
    /// 二进制文件不是当前架构的
    WrongArchitecture,
    /// 访问权限不足
    PermissionDenied,
    /// 不支持的操作
    NotSupported,
    /// 解析文件本身的时候出现错误（比如一些字段本身不合法）
    ParseError,
    /// 内存不足
    OutOfMemory,
    /// 参数错误
    InvalidParemeter,
    /// 无效的地址
    BadAddress(Option<VirtAddr>),
    Other(String),
}

impl Into<SystemError> for ExecError {
    fn into(self) -> SystemError {
        match self {
            ExecError::NotExecutable => SystemError::ENOEXEC,
            ExecError::WrongArchitecture => SystemError::EOPNOTSUPP_OR_ENOTSUP,
            ExecError::PermissionDenied => SystemError::EACCES,
            ExecError::NotSupported => SystemError::EOPNOTSUPP_OR_ENOTSUP,
            ExecError::ParseError => SystemError::ENOEXEC,
            ExecError::OutOfMemory => SystemError::ENOMEM,
            ExecError::InvalidParemeter => SystemError::EINVAL,
            ExecError::BadAddress(_addr) => SystemError::EFAULT,
            ExecError::Other(_msg) => SystemError::ENOEXEC,
        }
    }
}

bitflags! {
    pub struct ExecParamFlags: u32 {
        // 是否以可执行文件的形式加载
        const EXEC = 1 << 0;
    }
}

#[derive(Debug)]
pub struct ExecParam {
    file: File,
    vm: Arc<AddressSpace>,
    /// 一些标志位
    flags: ExecParamFlags,
    /// 用来初始化进程的一些信息。这些信息由二进制加载器和exec机制来共同填充
    init_info: ProcInitInfo,
}

#[derive(Debug, Eq, PartialEq)]
pub enum ExecLoadMode {
    /// 以可执行文件的形式加载
    Exec,
    /// 以动态链接库的形式加载
    DSO,
}

#[allow(dead_code)]
impl ExecParam {
    pub fn new(
        file_path: &str,
        vm: Arc<AddressSpace>,
        flags: ExecParamFlags,
    ) -> Result<Self, SystemError> {
        let inode = ROOT_INODE().lookup(file_path)?;

        // 读取文件头部，用于判断文件类型
        let file = File::new(inode, FileMode::O_RDONLY)?;

        Ok(Self {
            file,
            vm,
            flags,
            init_info: ProcInitInfo::new(),
        })
    }

    pub fn vm(&self) -> &Arc<AddressSpace> {
        &self.vm
    }

    pub fn flags(&self) -> &ExecParamFlags {
        &self.flags
    }

    pub fn init_info(&self) -> &ProcInitInfo {
        &self.init_info
    }

    pub fn init_info_mut(&mut self) -> &mut ProcInitInfo {
        &mut self.init_info
    }

    /// 获取加载模式
    pub fn load_mode(&self) -> ExecLoadMode {
        if self.flags.contains(ExecParamFlags::EXEC) {
            ExecLoadMode::Exec
        } else {
            ExecLoadMode::DSO
        }
    }

    pub fn file_mut(&mut self) -> &mut File {
        &mut self.file
    }
}

/// ## 加载二进制文件
pub fn load_binary_file(param: &mut ExecParam) -> Result<BinaryLoaderResult, SystemError> {
    // 读取文件头部，用于判断文件类型
    let mut head_buf = [0u8; 512];
    param.file_mut().lseek(SeekFrom::SeekSet(0))?;
    let _bytes = param.file_mut().read(512, &mut head_buf)?;
    // kdebug!("load_binary_file: read {} bytes", _bytes);

    let mut loader = None;
    for bl in BINARY_LOADERS.iter() {
        let probe_result = bl.probe(param, &head_buf);
        if probe_result.is_ok() {
            loader = Some(bl);
            break;
        }
    }
    // kdebug!("load_binary_file: loader: {:?}", loader);
    if loader.is_none() {
        return Err(SystemError::ENOEXEC);
    }

    let loader: &&dyn BinaryLoader = loader.unwrap();
    assert!(param.vm().is_current());
    // kdebug!("load_binary_file: to load with param: {:?}", param);

    let result: BinaryLoaderResult = loader
        .load(param, &head_buf)
        .unwrap_or_else(|e| panic!("load_binary_file failed: error: {e:?}, param: {param:?}"));

    // kdebug!("load_binary_file: load success");
    return Ok(result);
}

/// 程序初始化信息，这些信息会被压入用户栈中
#[derive(Debug)]
pub struct ProcInitInfo {
    pub proc_name: String,
    pub args: Vec<String>,
    pub envs: Vec<String>,
    pub auxv: BTreeMap<u8, usize>,
}

impl ProcInitInfo {
    pub fn new() -> Self {
        Self {
            proc_name: String::new(),
            args: Vec::new(),
            envs: Vec::new(),
            auxv: BTreeMap::new(),
        }
    }

    /// 把程序初始化信息压入用户栈中
    /// 这个函数会把参数、环境变量、auxv等信息压入用户栈中
    ///
    /// ## 返回值
    ///
    /// 返回值是一个元组，第一个元素是最终的用户栈顶地址，第二个元素是环境变量pointer数组的起始地址     
    pub unsafe fn push_at(
        &self,
        ustack: &mut UserStack,
    ) -> Result<(VirtAddr, VirtAddr), SystemError> {
        // 先把程序的名称压入栈中
        self.push_str(ustack, &self.proc_name)?;

        // 然后把环境变量压入栈中
        let envps = self
            .envs
            .iter()
            .map(|s| {
                self.push_str(ustack, s.as_str()).expect("push_str failed");
                ustack.sp()
            })
            .collect::<Vec<_>>();

        // 然后把参数压入栈中
        let argps = self
            .args
            .iter()
            .map(|s| {
                self.push_str(ustack, s.as_str()).expect("push_str failed");
                ustack.sp()
            })
            .collect::<Vec<_>>();

        // 压入auxv
        self.push_slice(ustack, &[null::<u8>(), null::<u8>()])?;
        for (&k, &v) in self.auxv.iter() {
            self.push_slice(ustack, &[k as usize, v])?;
        }

        // 把环境变量指针压入栈中
        self.push_slice(ustack, &[null::<u8>()])?;
        self.push_slice(ustack, envps.as_slice())?;

        // 把参数指针压入栈中
        self.push_slice(ustack, &[null::<u8>()])?;
        self.push_slice(ustack, argps.as_slice())?;

        let argv_ptr = ustack.sp();

        // 把argc压入栈中
        self.push_slice(ustack, &[self.args.len()])?;

        return Ok((ustack.sp(), argv_ptr));
    }

    fn push_slice<T: Copy>(&self, ustack: &mut UserStack, slice: &[T]) -> Result<(), SystemError> {
        let mut sp = ustack.sp();
        sp -= slice.len() * core::mem::size_of::<T>();
        sp -= sp.data() % core::mem::align_of::<T>();

        unsafe { core::slice::from_raw_parts_mut(sp.data() as *mut T, slice.len()) }
            .copy_from_slice(slice);
        unsafe {
            ustack.set_sp(sp);
        }

        return Ok(());
    }

    fn push_str(&self, ustack: &mut UserStack, s: &str) -> Result<(), SystemError> {
        self.push_slice(ustack, &[b'\0'])?;
        self.push_slice(ustack, s.as_bytes())?;
        return Ok(());
    }
}
