use crate::structs::{Status, GUID};
use crate::types::char16;

#[repr(C)]
pub struct SimpleFileSystemProtocol {
    pub revision: u64,
    pub open_volume: OpenVolume,
}

impl SimpleFileSystemProtocol {
    pub const GUID: GUID = GUID {
        data1: 0x0964e5b22,
        data2: 0x6459,
        data3: 0x11d2,
        data4: [0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b],
    };
}

/// The OpenVolume() function opens a volume, and returns a file handle to the volume’s root directory. This handle is used to perform all other file I/O operations. The volume remains open until all the file handles to it are closed.  If the medium is changed while there are open file handles to the volume, all file handles to the volume will return EFI_MEDIA_CHANGED. To access the files on the new medium, the volume must be reopened with OpenVol- ume(). If the new medium is a different file system than the one supplied in the EFI_HANDLE’s DevicePath for the EFI_SIMPLE_SYSTEM_PROTOCOL, OpenVolume() will return EFI_UNSUPPORTED.
/// # Arguments
/// * `this` - A pointer to the volume to open the root directory of.
/// * `root` - A pointer to the location to return the opened file handle for the root directory.
pub type OpenVolume =
    extern "efiapi" fn(this: *mut SimpleFileSystemProtocol, root: *mut *mut FileProtocol) -> Status;

#[repr(u64)]
pub enum OpenMode {
    FileModeRead = 0x0000000000000001,
    FileModeWrite = 0x0000000000000002,
    FileModeCreate = 0x8000000000000000,
}

#[repr(u64)]
pub enum FileAttribute {
    FileReadOnly = 0x0000000000000001,
    FileHidden = 0x0000000000000002,
    FileSystem = 0x0000000000000004,
    FileReserved = 0x0000000000000008,
    FileDirectory = 0x0000000000000010,
    FileArchive = 0x0000000000000020,
    FileValidAttr = 0x0000000000000037,
}

#[repr(C)]
pub struct FileProtocol {
    pub revision: u64,
    pub open: FileOpen,
    pub close: FileClose,
    pub delete: FileDelete,
    pub read: FileRead,
    pub write: FileWrite,
    pub get_position: FileGetPosition,
    pub set_position: FileSetPosition,
    pub get_info: FileGetInfo,
    pub set_info: FileSetInfo,
    pub flush: FileFlush,
    pub open_ex: FileOpenEx,
    pub read_ex: FileReadEx,
    pub write_ex: FileWriteEx,
    pub flush_ex: FileFlushEx,
}

/// The Open() function opens the file or directory referred to by FileName relative to the location of This and returns a NewHandle. The FileName may include the following path modifiers: “\” If the filename starts with a “\” the relative location is the root directory that This resides on; otherwise “" separates name components. Each name component is opened in turn, and the handle to the last file opened is returned.  “.” Opens the current location.  “..” Opens the parent directory for the current location. If the location is the root directory the request will return an error, as there is no parent directory for the root directory.  If EFI_FILE_MODE_CREATE is set, then the file is created in the directory. If the final location of FileName does not refer to a directory, then the operation fails. If the file does not exist in the directory, then a new file is created. If the file already exists in the directory, then the existing file is opened.  If the medium of the device changes, all accesses (including
/// # Arguments
/// * `This` - A pointer to the EFI_FILE_PROTOCOL instance that is the file handle to the source location. This would typically be an open handle to a directory. See the type EFI_FILE_PROTOCOL description.
/// * `NewHandle` - A pointer to the location to return the opened handle for the new file.
/// * `FileName` - The Null-terminated string of the name of the file to be opened. The file name may contain the following path modifiers: “", “.”, and “..”.
/// * `OpenMode` - The mode to open the file. The only valid combinations that the file may be opened with are: Read, Read/Write, or Create/Read/Write. See “Related Definitions” below.
/// * `Attributes` - Only valid for EFI_FILE_MODE_CREATE, in which case these are the attribute bits for the newly created file. See “Related Definitions” below.
pub type FileOpen = extern "efiapi" fn(
    this: *mut FileProtocol,
    new_handle: *mut *mut FileProtocol,
    file_name: *const char16,
    open_mode: OpenMode,
    attributes: FileAttribute,
) -> Status;
/// The Close() function closes a specified file handle. All “dirty” cached file data is flushed to the device, and the file is closed. In all cases the handle is closed. The operation will wait for all pending asynchronous I/O requests to complete before completing.
/// # Arguments
/// * `This` - A pointer to the EFI_FILE_PROTOCOL instance that is the file handle to close.
pub type FileClose = extern "efiapi" fn(this: &FileProtocol) -> Status;
pub type FileDelete = extern "efiapi" fn() -> Status;
pub type FileRead = extern "efiapi" fn() -> Status;
pub type FileWrite = extern "efiapi" fn() -> Status;
pub type FileGetPosition = extern "efiapi" fn() -> Status;
pub type FileSetPosition = extern "efiapi" fn() -> Status;
pub type FileGetInfo = extern "efiapi" fn() -> Status;
pub type FileSetInfo = extern "efiapi" fn() -> Status;
pub type FileFlush = extern "efiapi" fn() -> Status;
pub type FileOpenEx = extern "efiapi" fn() -> Status;
pub type FileReadEx = extern "efiapi" fn() -> Status;
pub type FileWriteEx = extern "efiapi" fn() -> Status;
pub type FileFlushEx = extern "efiapi" fn() -> Status;
