/*   
 
*/

#ifndef CORO_DEFINED
#define CORO_DEFINED 1

#include "Common.h"
#include "List.h"
#include "CStack.h"

#if defined (HAS_UCONTEXT)
#include <ucontext.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif

typedef int (CoroUnyieldCallback)(void *);

typedef struct Coro Coro;

struct Coro
{
    void *scheduler;
    Coro *next;
    Coro *prev;
    unsigned int shouldPause : 1;
    unsigned int shouldKill : 1;
    unsigned int unused : 6;
    char *label;
    void *object; // for external use in host program 
    CoroUnyieldCallback *unyieldCallback;
#if defined(HAS_FIBERS)
    void *fiber;
#elif defined(HAS_UCONTEXT)
    ucontext_t env;
#else
    jmp_buf env;
#endif

#ifndef HAS_FIBERS
    // for getjmp and swapcontext we save the stack into a buffer
    void *stack;
    size_t stackSize;
#endif
};

Coro *Coro_new(void);
void Coro_free(Coro *self);
void Coro_freeCoroChain(Coro *self);

void Coro_scheduler_(Coro *self, void *scheduler);
void *Coro_scheduler(Coro *self);

void *Coro_stack(Coro *self);
size_t Coro_stackSize(Coro *self);

void Coro_label_(Coro *self, const char *label);
const char *Coro_label(Coro *self);

void Coro_loop(Coro *self);
void Coro_remove(Coro *self);
void Coro_insertAfter_(Coro *self, Coro *other);
void Coro_insertBefore_(Coro *self, Coro *other);
unsigned int Coro_hasCoro_(Coro *self, Coro *other);

void Coro_shouldPause_(Coro *self,  unsigned char b);
unsigned int Coro_shouldPause(Coro *self);

void Coro_shouldKill_(Coro *self, unsigned char b);
unsigned int Coro_shouldKill(Coro *self);

size_t Coro_bytesLeftOnStack(Coro *self);

void Coro_object_(Coro *self, void *object);
void *Coro_object(Coro *self);

// callback 

void Coro_unyieldCallback_(Coro *self, CoroUnyieldCallback *callback);
CoroUnyieldCallback *Coro_unyieldCallback(Coro *self);

// Scheduler will call this right before switching to the coro 
void Coro_doUnyieldCallback(Coro *self);

// actions 

void Coro_check(Coro *self);

void Coro_yield(Coro *self);
void Coro_prepareToPause(Coro *self);
void Coro_pause(Coro *self);
void Coro_resume(Coro *self);
void Coro_show(Coro *self);

// status 

void Coro_kill(Coro *self);
int Coro_isCurrent(Coro *self);
int Coro_isPaused(Coro *self);

#ifdef __cplusplus
}
#endif
#endif
