#include <ctype.h>
#include <string.h>

#include <aos/aos.h>
#include <spawn/spawn.h>

#include <elf/elf.h>
#include <aos/dispatcher_arch.h>
#include <aos/lmp_chan.h>
#include <aos/aos_rpc.h>
#include <barrelfish_kpi/paging_arm_v8.h>
#include <barrelfish_kpi/domain_params.h>
#include <spawn/multiboot.h>
#include <spawn/argv.h>

extern struct bootinfo *bi;
extern coreid_t my_core_id;





/**
 * \brief Set the base address of the .got (Global Offset Table) section of the ELF binary
 *
 * \param arch_load_info This must be the base address of the .got section (local to the
 * child's VSpace). Must not be NULL.
 * \param handle The handle for the new dispatcher that is to be spawned. Must not be NULL.
 * \param enabled_area The "resume enabled" register set. Must not be NULL.
 * \param disabled_area The "resume disabled" register set. Must not be NULL.
 */
__attribute__((__used__))
static void armv8_set_registers(void *arch_load_info,
                              dispatcher_handle_t handle,
                              arch_registers_state_t *enabled_area,
                              arch_registers_state_t *disabled_area)
{
    assert(arch_load_info != NULL);
    uintptr_t got_base = (uintptr_t) arch_load_info;

    struct dispatcher_shared_aarch64 * disp_arm = get_dispatcher_shared_aarch64(handle);
    disp_arm->got_base = got_base;

    enabled_area->regs[REG_OFFSET(PIC_REGISTER)] = got_base;
    disabled_area->regs[REG_OFFSET(PIC_REGISTER)] = got_base;
}









/**
 * TODO(M2): Implement this function.
 * \brief Spawn a new dispatcher called 'argv[0]' with 'argc' arguments.
 *
 * This function spawns a new dispatcher running the ELF binary called
 * 'argv[0]' with 'argc' - 1 additional arguments. It fills out 'si'
 * and 'pid'.
 *
 * \param argc The number of command line arguments. Must be > 0.
 * \param argv An array storing 'argc' command line arguments.
 * \param si A pointer to the spawninfo struct representing
 * the child. It will be filled out by this function. Must not be NULL.
 * \param pid A pointer to a domainid_t variable that will be
 * assigned to by this function. Must not be NULL.
 * \return Either SYS_ERR_OK if no error occured or an error
 * indicating what went wrong otherwise.
 */
errval_t spawn_load_argv(int argc, char *argv[], struct spawninfo *si,
                domainid_t *pid) {
    // TODO: Implement me
    // - Initialize the spawn_info struct
    // - Get the module from the multiboot image
    //   and map it (take a look at multiboot.c)
    // - Setup the child's cspace
    // - Setup the child's vspace
    // - Load the ELF binary
    // - Setup the dispatcher
    // - Setup the environment
    // - Make the new dispatcher runnable

    // afeer: script page 84/85
    cnode_create_l1(&si->cspace_l1_cnode_cap, &si->cspace_l1_cnode_info);

    cnode_create_foreign_l2(si->cspace_l1_cnode_cap, ROOTCN_SLOT_TASKCN, &si->cspace_l2_cnode_taskcn);

    // afeer: (script page 84) is this correct?
    si->cspace_cap_selfep.cnode = si->cspace_l2_cnode_taskcn;
    si->cspace_cap_selfep.slot = TASKCN_SLOT_SELFEP;

    si->cspace_cap_dispatcher.cnode = si->cspace_l2_cnode_taskcn;
    si->cspace_cap_dispatcher.slot = TASKCN_SLOT_DISPATCHER;

    si->cspace_cap_rootcn.cnode = si->cspace_l2_cnode_taskcn;
    si->cspace_cap_rootcn.slot = TASKCN_SLOT_ROOTCN;

    si->cspace_cap_dispframe.cnode = si->cspace_l2_cnode_taskcn;
    si->cspace_cap_dispframe.slot = TASKCN_SLOT_DISPFRAME;

    si->cspace_cap_argspage.cnode = si->cspace_l2_cnode_taskcn;
    si->cspace_cap_argspage.slot = TASKCN_SLOT_ARGSPAGE;

    cnode_create_foreign_l2(si->cspace_l1_cnode_cap, ROOTCN_SLOT_SLOT_ALLOC0, &si->cspace_l2_cnode_slot_alloc_0);
    cnode_create_foreign_l2(si->cspace_l1_cnode_cap, ROOTCN_SLOT_SLOT_ALLOC1, &si->cspace_l2_cnode_slot_alloc_1);
    cnode_create_foreign_l2(si->cspace_l1_cnode_cap, ROOTCN_SLOT_SLOT_ALLOC2, &si->cspace_l2_cnode_slot_alloc_2);
    cnode_create_foreign_l2(si->cspace_l1_cnode_cap, ROOTCN_SLOT_BASE_PAGE_CN, &si->cspace_l2_cnode_base_pagecn);
    cnode_create_foreign_l2(si->cspace_l1_cnode_cap, ROOTCN_SLOT_PAGECN, &si->cspace_l2_cnode_pagecn); // < afeer: TODO: script page 85: fill this with pagetable capabilities

    // afeer: populate some capabilities
    dispatcher_create(si->cspace_cap_dispatcher);
    cap_retype(si->cspace_cap_selfep, si->cspace_cap_dispatcher, 0, ObjType_EndPointLMP, 0, 1);

    return LIB_ERR_NOT_IMPLEMENTED;
}


/**
 * TODO(M2): Implement this function.
 * \brief Spawn a new dispatcher executing 'binary_name'
 *
 * \param binary_name The name of the binary.
 * \param si A pointer to a spawninfo struct that will be
 * filled out by spawn_load_by_name. Must not be NULL.
 * \param pid A pointer to a domainid_t that will be
 * filled out by spawn_load_by_name. Must not be NULL.
 *
 * \return Either SYS_ERR_OK if no error occured or an error
 * indicating what went wrong otherwise.
 */
errval_t spawn_load_by_name(char *binary_name, struct spawninfo * si,
                            domainid_t *pid) {
    errval_t err;

    // - Get the mem_region from the multiboot image
    struct mem_region *module = multiboot_find_module(bi, binary_name);
    if (module == NULL) return SPAWN_ERR_FIND_MODULE;

    struct capref child_frame = {
        .cnode = cnode_module,
        .slot = module->mrmod_slot,
    };

    struct capability c;
    err = cap_direct_identify(child_frame, &c);
    if (err_is_fail(err)) return err;
    gensize_t frame_size = get_size(&c);
    if (frame_size < module->mr_bytes) return SPAWN_ERR_MODULE_FRAME_TOO_SMALL;

    char *module_data;
    err = paging_map_frame_attr(
        get_current_paging_state(), (void **)&module_data,
        frame_size, child_frame, VREGION_FLAGS_READ
    );
    if (err_is_fail(err)) return err_push(err, SPAWN_ERR_MAP_MODULE);

    debug_printf("SPAWN: mapped %s, magic: %"PRIx8" %c%c%c\n",
        binary_name, module_data[0], module_data[1], module_data[2], module_data[3]);

    // - Fill in argc/argv from the multiboot command line
    // - Call spawn_load_argv

    return LIB_ERR_NOT_IMPLEMENTED;
}
