#include <nanox/nanos.h>

#include "VSs.h"

/*nanos_submit 
nanos_current_wd
nanos_create_wd_compact
nanos_handle_error
nanos_create_wd_and_run_compact

nanos_wg_wait_completion //taskwait
nanos_wait_on //taskwait on
nanos_set_lock //critical start
nanos_unset_lock //critical end

nanos_omp_set_interface
nanos_smp_factory*/

int tasks_created = 0;

nanos_wd_t nanos_current_wd(void) {
    return (nanos_wd_t) ((VSsSemData*) currVP->semanticData)->taskStub;
}

nanos_err_t nanos_create_wd_compact(nanos_wd_t *wd, nanos_const_wd_definition_t *const_data, nanos_wd_dyn_props_t *dyn_props,
        size_t data_size, void ** data, nanos_wg_t wg, nanos_copy_data_t **copies, nanos_region_dimension_internal_t **dimensions) {

    *wd = NULL;
    return NANOS_OK;

    VSsTaskStub* ts = (VSsTaskStub*) malloc(sizeof (VSsTaskStub));
    void* alloc_data = malloc(data_size);
    *data = alloc_data;
    ts->args = alloc_data;

    VSsTaskType* taskType = (VSsTaskType*) malloc(sizeof (VSsTaskType));

    taskType->fn = *((VSsTaskFnPtr*) ((nanos_device_t*) & const_data[1])->arg);
    taskType->sizeOfArgs = data_size;

    ts->taskType = taskType;

    *wd = (nanos_wd_t) ts;

    return NANOS_OK;
}

nanos_err_t nanos_submit(nanos_wd_t wd, size_t num_data_accesses, nanos_data_access_t *data_accesses, nanos_team_t team) {

    return NANOS_UNIMPLEMENTED;

    int32* depsTypes = malloc(sizeof (int32) * num_data_accesses);
    size_t* depsSizes = malloc(sizeof (size_t) * num_data_accesses);
    void** depsAddrs = malloc(sizeof (void*)*num_data_accesses);

    int i;
    for (i = 0; i < num_data_accesses; i++) {
        depsAddrs[i] = (void*)((uintptr_t)data_accesses[i].address + data_accesses[i].offset );
        depsTypes[i] = (data_accesses[i].flags.output) ? WRITER : READER;
        depsSizes[i] = data_accesses[i].dimensions[0].size;
    }

    VSsTaskStub* ts = (VSsTaskStub*) wd;



    VSsTaskType* taskType = ts->taskType;
    taskType->numDeps = num_data_accesses;
    taskType->depsTypes = depsTypes;
    taskType->depsSizes = depsSizes;


    VSsSemReq reqData;

    reqData.reqType = submit_task;

    reqData.taskType = taskType;
    reqData.args = ts->args;
    reqData.deps = depsAddrs;
    reqData.callingSlv = currVP;

    int32* taskID;
    taskID = VSs__create_taskID_of_size(1);
    taskID[1] = tasks_created++;

    reqData.taskID = taskID;

    free(ts);

    VMS_WL__send_sem_request(&reqData, currVP);

    return NANOS_OK;
}

nanos_err_t nanos_create_wd_and_run_compact( nanos_const_wd_definition_t *const_data, nanos_wd_dyn_props_t *dyn_props,
                                             size_t data_size, void * data, size_t num_data_accesses, nanos_data_access_t *data_accesses,
                                             nanos_copy_data_t *copies, nanos_region_dimension_internal_t *dimensions, nanos_translate_args_t translate_args ) {


    int32* depsTypes = malloc(sizeof (int32) * num_data_accesses);
    size_t* depsSizes = malloc(sizeof (size_t) * num_data_accesses);
    void** depsAddrs = malloc(sizeof (void*) * num_data_accesses);

    int i;
    for (i = 0; i < num_data_accesses; i++) {
        depsAddrs[i] = (void*)((uintptr_t)data_accesses[i].address + data_accesses[i].offset );
        depsTypes[i] = (data_accesses[i].flags.output) ? WRITER : READER;
        depsSizes[i] = data_accesses[i].dimensions[0].size;
    }

    /* const_data is declared as:
     * 
     * struct nanos_const_wd_definition_local_t
                 {
                         nanos_const_wd_definition_t base;
                         nanos_device_t devices[1];
                 };
     * and devices[0].arg is the function to call
     * so ugly pointer tricks to get it from there
     */

    VSsTaskType* taskType = (VSsTaskType*) malloc(sizeof (VSsTaskType));

    taskType->fn = *((VSsTaskFnPtr*) ((nanos_device_t*) & const_data[1])->arg);
    taskType->numDeps = num_data_accesses;
    taskType->depsTypes = depsTypes;
    taskType->depsSizes = depsSizes;
    taskType->sizeOfArgs = data_size;

    VSsSemReq reqData;

    reqData.reqType = submit_task;

    reqData.taskType = taskType;
    reqData.args = data;
    reqData.deps = depsAddrs;
    reqData.callingSlv = currVP;

    int32* taskID;
    taskID = VSs__create_taskID_of_size(1);
    taskID[1] = tasks_created++;

    reqData.taskID = taskID;

    VMS_WL__send_sem_request(&reqData, currVP);

    return NANOS_OK;
}

void nanos_handle_error(nanos_err_t err) {
    exit(err);
}

nanos_err_t nanos_wg_wait_completion(nanos_wg_t wg, bool avoid_flush) {
    VSsSemReq reqData;

    reqData.reqType = taskwait;
    reqData.callingSlv = currVP;

    VMS_WL__send_sem_request(&reqData, currVP);

    return NANOS_OK;
}

nanos_err_t nanos_wait_on( size_t num_data_accesses, nanos_data_access_t *data_accesses ) {
    VSsSemReq reqData;

    reqData.reqType = taskwait_on;
    reqData.callingSlv = currVP;

    int i;
    for (i = 0; i < num_data_accesses; i++) {
        reqData.args = data_accesses[i].address;
        VMS_WL__send_sem_request(&reqData, currVP);
    }

    return NANOS_OK;
}

nanos_err_t nanos_set_lock(nanos_lock_t *lock) {
    VSsSemReq reqData;

    reqData.reqType = critical_start;
    reqData.callingSlv = currVP;

    reqData.criticalID = lock;

    VMS_WL__send_sem_request(&reqData, currVP);

    return NANOS_OK;
}

nanos_err_t nanos_unset_lock(nanos_lock_t *lock) {
    VSsSemReq reqData;

    reqData.reqType = critical_end;
    reqData.callingSlv = currVP;

    reqData.criticalID = lock;

    VMS_WL__send_sem_request(&reqData, currVP);

    return NANOS_OK;
}

nanos_err_t nanos_omp_barrier(void) {
    VSsSemReq reqData;

    reqData.reqType = barrier;
    reqData.callingSlv = currVP;

    VMS_WL__send_sem_request(&reqData, currVP);

    return NANOS_OK;
}

nanos_err_t nanos_in_final(bool *result) {
    *result = false;
    return NANOS_OK;
}

void * nanos_smp_factory(void *args) {
    return NULL;
}

void nanos_omp_set_interface(void * arg) {
    return;
}
