typedef void CoroutineProc(mco_coro *co);
CoData *CoCurr = NULL;

void CoDestroy(CoData *n) {
    mco_destroy(n->co);
    Release(&n->arena);
}

void CoRemove(String name) {
    IterRemove(ActiveCoroutines) {
        IterRemovePrepare(ActiveCoroutines);
        if (it.name == name) {
            mco_destroy(it.co);
            Release(&it.arena);
            remove_item = true;
        }
    }
}

#define CoAdd(x) CoAddEx(x, #x)
CoData *CoAddEx(CoroutineProc *proc, String name) {
    CoRemove(name);
    mco_desc desc = mco_desc_init(proc, 0);

    mco_coro *coro = NULL;
    mco_result ok = mco_create(&coro, &desc);
    if (ok != MCO_SUCCESS) {
        ReportWarningf("failed to create coroutine %d", ok);
        return NULL;
    }

    Add(&ActiveCoroutines, {coro, name});
    CoData *prev_curr = CoCurr;
    CoCurr = GetLast(ActiveCoroutines);
    ok = mco_resume(coro);
    Assert(ok == MCO_SUCCESS);
    CoCurr = prev_curr;
    return GetLast(ActiveCoroutines);
}

void CoUpdate(Event *event) {
    ProfileFunction();
    double start = GetTimeSeconds();
    for (;ActiveCoroutines.len;) {
        IterRemove(ActiveCoroutines) {
            IterRemovePrepare(ActiveCoroutines);
            ProfileScopeEx(it.name);

            mco_state status = mco_status(it.co);
            if (status == MCO_DEAD) {
                CoDestroy(&it);
                remove_item = true;
            } else {
                mco_push(it.co, &event, sizeof(Event *));
                CoCurr = &it;
                mco_result ok = mco_resume(it.co);
                if (ok != MCO_SUCCESS) {
                    ReportWarningf("failed to resume coroutine %d", ok);
                    CoDestroy(&it);
                    remove_item = true;
                }
            }
        }

        double took = GetTimeSeconds() - start;
        if (took > (0.016666 / 3.0)) {
            break;
        }
    }
}

Event *CoYield(mco_coro *co) {
    mco_result ok = mco_yield(co);
    Assert(ok == MCO_SUCCESS);

    Event *event = NULL;
    ok = mco_pop(co, &event, sizeof(Event *));
    Assert(ok == MCO_SUCCESS);

    return event;
}
