#ifndef HALIDE_HEXAGON_REMOTE_PIPELINE_CONTEXT_H
#define HALIDE_HEXAGON_REMOTE_PIPELINE_CONTEXT_H
#include <HalideRuntime.h>
#include <qurt.h>
typedef int (*pipeline_argv_t)(void **);
class PipelineContext {
    void *stack;
    qurt_thread_t thread;
    qurt_cond_t wakeup_thread;
    qurt_cond_t wakeup_caller;
    qurt_mutex_t work_mutex;
    
    pipeline_argv_t function;
    void **args;
    int result;
    bool running;
    void thread_main() {
        qurt_mutex_lock(&work_mutex);
        while (running) {
            qurt_cond_wait(&wakeup_thread, &work_mutex);
            if (function) {
                result = function(args);
                function = NULL;
                qurt_cond_signal(&wakeup_caller);
            }
        }
        qurt_mutex_unlock(&work_mutex);
    }
    static void redirect_main(void *data) {
        static_cast<PipelineContext *>(data)->thread_main();
    }
public:
    PipelineContext(int stack_alignment, int stack_size)
        : stack(NULL), function(NULL), args(NULL), running(true) {
        qurt_mutex_init(&work_mutex);
        qurt_cond_init(&wakeup_thread);
        qurt_cond_init(&wakeup_caller);
        
        stack = memalign(stack_alignment, stack_size);
        qurt_thread_attr_t thread_attr;
        qurt_thread_attr_init(&thread_attr);
        qurt_thread_attr_set_stack_addr(&thread_attr, stack);
        qurt_thread_attr_set_stack_size(&thread_attr, stack_size);
        qurt_thread_attr_set_priority(&thread_attr, 100);
        qurt_thread_create(&thread, &thread_attr, redirect_main, this);
    }
    ~PipelineContext() {
        
        qurt_mutex_lock(&work_mutex);
        running = false;
        qurt_cond_signal(&wakeup_thread);
        qurt_mutex_unlock(&work_mutex);
        int status;
        qurt_thread_join(thread, &status);
        qurt_cond_destroy(&wakeup_thread);
        qurt_cond_destroy(&wakeup_caller);
        qurt_mutex_destroy(&work_mutex);
        free(stack);
    }
    int run(pipeline_argv_t function, void **args) {
        
        qurt_mutex_lock(&work_mutex);
        this->function = function;
        this->args = args;
        
        qurt_cond_signal(&wakeup_thread);
        
        while (this->function != NULL) {
            qurt_cond_wait(&wakeup_caller, &work_mutex);
        }
        int result = this->result;
        qurt_mutex_unlock(&work_mutex);
        return result;
    }
};
#endif