use crate::memory_allocation_services::*;
use crate::structs::*;
use crate::types::Handle;
use core::ffi::c_void;
/**
   UEFI uses the EFI Boot Services Table, which contains a table header and pointers to all of the boot
   services. The definition for this table is shown in the following code fragments. Except for the table
   header, all elements in the EFI Boot Services Tables are prototypes of function pointers to functions as
   defined in Section 7. The function pointers in this table are not valid after the operating system has taken
   control of the platform with a call to EFI_BOOT_SERVICES.ExitBootServices().
**/
#[repr(C)]
pub struct BootServicesTable {
    pub efi_table_header: TableHeader,
    pub raise_tpl: RaiseTpl,
    pub restore_tpl: RestoreTpl,
    pub allocate_pages: AllocatePages,
    pub free_pages: FreePages,
    pub get_memory_map: GetMemoryMap,
    pub allocate_pool: AllocatePool,
    pub free_pool: FreePool,
    pub create_event: CreateEvent,
    pub set_timer: SetTimer,
    pub wait_for_event: WaitForEvent,
    pub signal_event: SignalEvent,
    pub close_event: CloseEvent,
    pub check_event: CheckEvent,
    pub install_protocol_interface: InstallProtocolInterface,
    pub reinstall_protocol_interface: ReinstallProtocolInterface,
    pub uninstall_protocol_interface: UninstallProtocolInterface,
    pub handle_protocol: HandleProtocol,
    pub reserved: Reserved,
    pub register_protocol_notify: RegisterProtocolNotify,
    pub locate_handle: LocateHandle,
    pub locate_device_path: LocateDevicePath,
    pub install_configuration_table: InstallConfigurationTable,
    pub load_image: LoadImage,
    pub start_image: StartImage,
    pub exit: Exit,
    pub unload_image: UnloadImage,
    pub exit_boot_services: ExitBootServices,
    pub get_next_monotonic_count: GetNextMonotonicCount,
    pub stall: Stall,
    pub set_watchdog_timer: SetWatchdogTimer,
    pub connect_controller: ConnectController,
    pub disconnect_contoller: DisconnectController,
    pub open_protocol: OpenProtocol,
    pub close_protocol: CloseProtocol,
    pub open_protocol_information: OpenProtocolInformation,
    pub protocol_per_handle: ProtocolPerHandle,
    pub locate_handle_buffer: LocateHandleBuffer,
    pub locate_protocol: LocateProtocol,
    pub install_multiple_protocol_interfaces: InstallMultipleProtocolInterfaces,
    pub uninstall_multiple_protocol_interfaces: UninstallMultipleProtocolInterfaces,
    pub calculate_crc_32: CalculateCrc32,
    pub copy_mem: CopyMem,
    pub set_mem: SetMem,
    pub create_event_ex: CreateEventEx,
}

pub type RaiseTpl = extern "efiapi" fn() -> Status;
pub type RestoreTpl = extern "efiapi" fn() -> Status;
/// The AllocatePages() function allocates the requested number of pages and returns a pointer to the base address of the page range in the location referenced by Memory. The function scans the memory map to locate free pages. When it finds a physically contiguous block of pages that is large enough and also satisfies the allocation requirements of Type, it changes the memory map to indicate that the pages are now of type MemoryType.  In general, UEFI OS loaders and UEFI applications should allocate memory (and pool) of type EfiLoaderData. UEFI boot service drivers must allocate memory (and pool) of type EfiBootServicesData. UREFI runtime drivers should allocate memory (and pool) of type EfiRuntimeServicesData (although such allocation can only be made during boot services time).  Allocation requests of Type AllocateAnyPages allocate any available range of pages that satisfies the request. On input, the address pointed to by Memory is ignored.  Allocation requests of Type AllocateMaxAddress allocate any available range of pages whose uppermost address is less than or equal to the address pointed to by Memory on input.  Allocation requests of Type AllocateAddress allocate pages at the address pointed to by Memory on input.
/// # Arguments
/// * `Type` - The type of allocation to perform.
/// * `MemoryType` - The type of memory to allocate. Normal allocations (that is, allocations by any UEFI ap plication) are of type LoaderData. MemoryType values in the range 0x70000000..0x7FFFFFFF are reserved for OEM use. MemoryType values in the range 0x80000000..0xFFFFFFFF are reserved for use by UEFI OS loaders that are provided by operating system vendors.
/// * `Pages` - The number of contiguous 4 KiB pages to allocate.
/// * `Memory` - Pointer to a physical address. On input, the way in which the address is used depends on the value of Type. On output the address is set to the base of the page range that was allocated.
pub type AllocatePages = extern "efiapi" fn(
    r#type: AllocateType,
    memory_type: MemoryType,
    pages: usize,
    &mut PhysicalAddress,
) -> Status;

/// The FreePages() function returns memory allocated by AllocatePages() to the firmware.
/// # Arguments
/// * `Memory` - The base physical address of the pages to be freed. Type EFI_PHYSICAL_ADDRESS is defined in the EFI_BOOT_SERVICES.AllocatePages() function description.
/// * `Pages` - The number of contiguous 4 KiB pages to free.
pub type FreePages = extern "efiapi" fn(memory: PhysicalAddress, pages: usize) -> Status;

/// The GetMemoryMap() function returns a copy of the current memory map. The map is an array of memory descriptors, each of which describes a contiguous block of memory. The map describes all of memory, no mat- ter how it is being used. That is, it includes blocks allocated by EFI_BOOT_SERVICES.AllocatePages() and EFI_BOOT_SERVICES.AllocatePool(), as well as blocks that the firmware is using for its own purposes. The memory map is only used to describe memory that is present in the system. The firmware does not return a range description for address space regions that are not backed by physical hardware. Regions that are backed by physical hardware, but are not supposed to be accessed by the OS, must be returned as EfiReservedMemoryType. The OS may use addresses of memory ranges that are not described in the memory map at its own discretion.  Until EFI_BOOT_SERVICES.ExitBootServices() is called, the memory map is owned by the firmware and the cur- rently executing UEFI Image should only use memory pages it has explicitly allocated.  If the MemoryMap buffer is too small, the EFI_BUFFER_TOO_SMALL error code is returned and the MemoryMap- Size value contains the size of the buffer needed to contain the current memory map. The actual size of the buffer allocated for the consequent call to GetMemoryMap() should be bigger then the value returned in MemoryMapSize, since allocation of the new buffer may potentially increase memory map size.  On success a MapKey is returned that identifies the current memory map. The firmware’s key is changed every time something in the memory map changes. In order to successfully invoke EFI_BOOT_SERVICES.ExitBootServices() the caller must provide the current memory map key.  The GetMemoryMap() function also returns the size and revision number of the EFI_MEMORY_DESCRIPTOR. The DescriptorSize represents the size in bytes of an EFI_MEMORY_DESCRIPTOR array element returned in Mem- oryMap. The size is returned to allow for future expansion of the EFI_MEMORY_DESCRIPTOR in response to hardware innovation. The structure of the EFI_MEMORY_DESCRIPTOR may be extended in the future but it will remain backwards compatible with the current definition. Thus OS software must use the DescriptorSize to find the start of each EFI_MEMORY_DESCRIPTOR in the MemoryMap array.
/// # Arguments
/// * `MemoryMapSize` - A pointer to the size, in bytes, of the MemoryMap buffer. On input, this is the size of the buffer allocated by the caller. On output, it is the size of the buffer returned by the firmware if the buffer was large enough, or the size of the buffer needed to contain the map if the buffer was too small.
/// * `MemoryMap` - A pointer to the buffer in which firmware places the current memory map. The map is an array of EFI_MEMORY_DESCRIPTORs.
/// * `MapKey` - A pointer to the location in which firmware returns the key for the current memory map.
/// * `DescriptorSize` - A pointer to the location in which firmware returns the size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
/// * `DescriptorVersion` - A pointer to the location in which firmware returns the version number associated with the EFI_MEMORY_DESCRIPTOR.
pub type GetMemoryMap = extern "efiapi" fn(
    memory_map_size: &mut usize,
    memory_map: *mut MemoryDescriptor,
    map_key: &mut usize,
    descriptor_size: &mut usize,
    descriptor_version: &mut u32,
) -> Status;

pub type AllocatePool = extern "efiapi" fn() -> Status;
pub type FreePool = extern "efiapi" fn() -> Status;
pub type CreateEvent = extern "efiapi" fn() -> Status;
pub type SetTimer = extern "efiapi" fn() -> Status;
pub type WaitForEvent = extern "efiapi" fn() -> Status;
pub type SignalEvent = extern "efiapi" fn() -> Status;
pub type CloseEvent = extern "efiapi" fn() -> Status;
pub type CheckEvent = extern "efiapi" fn() -> Status;
pub type InstallProtocolInterface = extern "efiapi" fn() -> Status;
pub type ReinstallProtocolInterface = extern "efiapi" fn() -> Status;
pub type UninstallProtocolInterface = extern "efiapi" fn() -> Status;
pub type HandleProtocol = extern "efiapi" fn() -> Status;
pub type Reserved = extern "efiapi" fn() -> Status;
pub type RegisterProtocolNotify = extern "efiapi" fn() -> Status;
pub type LocateHandle = extern "efiapi" fn() -> Status;
pub type LocateDevicePath = extern "efiapi" fn() -> Status;
pub type InstallConfigurationTable = extern "efiapi" fn() -> Status;
pub type LoadImage = extern "efiapi" fn() -> Status;
pub type StartImage = extern "efiapi" fn() -> Status;
pub type Exit = extern "efiapi" fn(
    image_handle: Handle,
    exit_status: Status,
    exit_data_size: usize,
    *const u16,
) -> Status;
pub type UnloadImage = extern "efiapi" fn() -> Status;
pub type ExitBootServices = extern "efiapi" fn() -> Status;
pub type GetNextMonotonicCount = extern "efiapi" fn() -> Status;
pub type Stall = extern "efiapi" fn() -> Status;
pub type SetWatchdogTimer = extern "efiapi" fn() -> Status;
pub type ConnectController = extern "efiapi" fn() -> Status;
pub type DisconnectController = extern "efiapi" fn() -> Status;
pub type OpenProtocol = extern "efiapi" fn() -> Status;
pub type CloseProtocol = extern "efiapi" fn() -> Status;
pub type OpenProtocolInformation = extern "efiapi" fn() -> Status;
pub type ProtocolPerHandle = extern "efiapi" fn() -> Status;
pub type LocateHandleBuffer = extern "efiapi" fn() -> Status;
/// The LocateProtocol() function finds the first device handle that support Protocol, and returns a pointer to the protocol interface from that handle in Interface. If no protocol instances are found, then Interface is set to NULL.  If Interface is NULL, then EFI_INVALID_PARAMETER is returned.  If Protocol is NULL, then EFI_INVALID_PARAMETER is returned.  If Registration is NULL, and there are no handles in the handle database that support Protocol, then EFI_NOT_FOUND is returned.  If Registration is not NULL, and there are no new handles for Registration, then EFI_NOT_FOUND is returned.
/// # Arguments
/// * `Protocol` - Provides the protocol to search for.
/// * `Registration` - Optional registration key returned from EFI_BOOT_SERVICES.RegisterProtocolNotify() . If Registration is NULL, then it is ignored.
/// * `Interface` - On return, a pointer to the first interface that matches Protocol and Registration.
pub type LocateProtocol = extern "efiapi" fn(
    protocol: &GUID,
    registration: *mut c_void,
    interface: *mut *mut c_void,
) -> Status;
pub type InstallMultipleProtocolInterfaces = extern "efiapi" fn() -> Status;
pub type UninstallMultipleProtocolInterfaces = extern "efiapi" fn() -> Status;
pub type CalculateCrc32 = extern "efiapi" fn() -> Status;
pub type CopyMem = extern "efiapi" fn() -> Status;
pub type SetMem = extern "efiapi" fn() -> Status;
pub type CreateEventEx = extern "efiapi" fn() -> Status;

impl BootServicesTable {
    pub const BOOT_SERVICES_SIGNATURE: u64 = 0x56524553544f4f42;
}
