#include "HalideRuntime.h"

extern "C" {

// Fake mutex array. We still define a pointer to halide_mutex since empty struct leads
// to compile error (empty struct has size 0 in C, size 1 in C++).
struct halide_mutex_array {
    halide_mutex *mutex;
};

WEAK int halide_default_do_task(void *user_context, halide_task_t f, int idx,
                                uint8_t *closure) {
    return f(user_context, idx, closure);
}

WEAK int halide_default_do_loop_task(void *user_context, halide_loop_task_t f,
                                     int min, int extent, uint8_t *closure,
                                     void *task_parent) {
    return f(user_context, min, extent, closure, task_parent);
}

WEAK int halide_default_do_par_for(void *user_context, halide_task_t f,
                                   int min, int size, uint8_t *closure) {
    for (int x = min; x < min + size; x++) {
        int result = halide_do_task(user_context, f, x, closure);
        if (result) {
            return result;
        }
    }
    return 0;
}

WEAK int halide_default_do_parallel_tasks(void *user_context, int num_tasks,
                                          struct halide_parallel_task_t *tasks,
                                          void *task_parent) {
    halide_error(nullptr, "halide_default_do_parallel_tasks not implemented on this platform.");
    return -1;
}

WEAK int halide_default_semaphore_init(halide_semaphore_t *s, int n) {
    halide_error(nullptr, "halide_default_semaphore_init not implemented on this platform.");
    return 0;
}

WEAK int halide_default_semaphore_release(halide_semaphore_t *s, int n) {
    halide_error(nullptr, "halide_default_semaphore_release not implemented on this platform.");
    return 0;
}

WEAK bool halide_default_semaphore_try_acquire(halide_semaphore_t *s, int n) {
    halide_error(nullptr, "halide_default_semaphore_try_acquire not implemented on this platform.");
    return false;
}

}  // extern "C"

namespace Halide {
namespace Runtime {
namespace Internal {

WEAK halide_do_task_t custom_do_task = halide_default_do_task;
WEAK halide_do_loop_task_t custom_do_loop_task = halide_default_do_loop_task;
WEAK halide_do_par_for_t custom_do_par_for = halide_default_do_par_for;
WEAK halide_do_parallel_tasks_t custom_do_parallel_tasks = halide_default_do_parallel_tasks;
WEAK halide_semaphore_init_t custom_semaphore_init = halide_default_semaphore_init;
WEAK halide_semaphore_try_acquire_t custom_semaphore_try_acquire = halide_default_semaphore_try_acquire;
WEAK halide_semaphore_release_t custom_semaphore_release = halide_default_semaphore_release;
WEAK halide_mutex_array halide_fake_mutex_array;

}  // namespace Internal
}  // namespace Runtime
}  // namespace Halide

extern "C" {

WEAK halide_thread *halide_spawn_thread(void (*f)(void *), void *closure) {
    // We can't fake spawning a thread. Emit an error.
    halide_error(nullptr, "halide_spawn_thread not implemented on this platform.");
    return nullptr;
}

WEAK void halide_join_thread(halide_thread *thread_arg) {
    halide_error(nullptr, "halide_join_thread not implemented on this platform.");
}

// Don't need to do anything with mutexes since we are in a fake thread pool.
WEAK void halide_mutex_lock(halide_mutex *mutex) {
}

WEAK void halide_mutex_unlock(halide_mutex *mutex) {
}

// Return a fake but non-null pointer here: this can be legitimately called
// from non-threaded code that uses the .atomic() schedule directive
// (e.g. correctness/multiple_scatter). Since we don't have threads, we don't
// need to mutex to do anything, but returning a null would trigger an error
// condition that would be misrepoted as out-of-memory.
WEAK halide_mutex_array *halide_mutex_array_create(int sz) {
    return &halide_fake_mutex_array;
}

WEAK void halide_mutex_array_destroy(void *user_context, void *array) {
    // Don't destroy the array! It's just halide_fake_mutex_array!
}

WEAK int halide_mutex_array_lock(halide_mutex_array *array, int entry) {
    return 0;
}

WEAK int halide_mutex_array_unlock(halide_mutex_array *array, int entry) {
    return 0;
}

WEAK void halide_shutdown_thread_pool() {
}

WEAK int halide_set_num_threads(int n) {
    if (n != 1) {
        halide_error(nullptr, "halide_set_num_threads: only supports a value of 1 on this platform.");
    }
    return 1;
}

WEAK halide_do_task_t halide_set_custom_do_task(halide_do_task_t f) {
    halide_do_task_t result = custom_do_task;
    custom_do_task = f;
    return result;
}

WEAK halide_do_par_for_t halide_set_custom_do_par_for(halide_do_par_for_t f) {
    halide_do_par_for_t result = custom_do_par_for;
    custom_do_par_for = f;
    return result;
}

WEAK int halide_do_task(void *user_context, halide_task_t f, int idx,
                        uint8_t *closure) {
    return (*custom_do_task)(user_context, f, idx, closure);
}

WEAK int halide_do_par_for(void *user_context, halide_task_t f,
                           int min, int size, uint8_t *closure) {
    return (*custom_do_par_for)(user_context, f, min, size, closure);
}

WEAK int halide_do_loop_task(void *user_context, halide_loop_task_t f,
                             int min, int size, uint8_t *closure, void *task_parent) {
    return custom_do_loop_task(user_context, f, min, size, closure, task_parent);
}

WEAK int halide_do_parallel_tasks(void *user_context, int num_tasks,
                                  struct halide_parallel_task_t *tasks,
                                  void *task_parent) {
    return custom_do_parallel_tasks(user_context, num_tasks, tasks, task_parent);
}

WEAK int halide_semaphore_init(struct halide_semaphore_t *sema, int count) {
    return custom_semaphore_init(sema, count);
}

WEAK int halide_semaphore_release(struct halide_semaphore_t *sema, int count) {
    return custom_semaphore_release(sema, count);
}

WEAK bool halide_semaphore_try_acquire(struct halide_semaphore_t *sema, int count) {
    return custom_semaphore_try_acquire(sema, count);
}

}  // extern "C"
