#include <stdio.h>
#include <stddef.h>
#include "coroutine.h"
#include "generator.h"
#include "asleep.h"
#include "task.h"

#include <dirent.h> 
#include <string.h>
#include <stdlib.h>
#include <time.h>

#define DEMO_STACK_SIZE (8192*sizeof(void *))

Coroutine *cor_main;

typedef struct {
    intptr_t headroom;
    bool canstartcoroutine;
} TestResult;


void *dotests(
    void *param
){
    (void)param;

    _Coroutine_Dump();
    Coroutine *active = Coroutine_GetActive();
    Coroutine *a = Coroutine_New(DEMO_STACK_SIZE, dotests);
    Coroutine *b = Coroutine_New(DEMO_STACK_SIZE, dotests);
    Coroutine *c = Coroutine_New(DEMO_STACK_SIZE, dotests);
    printf("[stack=aabbcc]\n");
    _Coroutine_Dump();
    printf("%ld %ld %ld (a, b, c: a < b < c)\n", (unsigned char *)active - (unsigned char *)a, (unsigned char *)active - (unsigned char *)b, (unsigned char *)active - (unsigned char *)c);
    Coroutine_Delete(b);
    printf("[stack=aaffcc]\n");
    _Coroutine_Dump();
    b = Coroutine_New(DEMO_STACK_SIZE, dotests);
    printf("[stack=aabbcc]\n");
    _Coroutine_Dump();
    printf("%ld (b again)\n", (unsigned char *)active - (unsigned char *)b);
    Coroutine_Delete(b);
    printf("[stack=aaffcc]\n");
    _Coroutine_Dump();
    Coroutine *d = Coroutine_New(DEMO_STACK_SIZE*2, dotests);
    printf("[stack=aaffccdddd]\n");
    printf("%ld (d: c < d)\n", (unsigned char *)active - (unsigned char *)d);
    _Coroutine_Dump();
    printf("[testing merge before]\n");
    Coroutine_Delete(c);
    printf("[stack=aaffffdddd]\n");
    _Coroutine_Dump();
    b = Coroutine_New(DEMO_STACK_SIZE*2, dotests);
    printf("[stack=aabbbbdddd]\n");
    printf("%ld (b again)\n", (unsigned char *)active - (unsigned char *)b);
    _Coroutine_Dump();
    printf("[testing merge after]\n");
    Coroutine_Delete(b);
    printf("[stack=aaffffdddd]\n");
    _Coroutine_Dump();
    Coroutine_Delete(a);
    printf("[stack=ffffffdddd]\n");
    _Coroutine_Dump();
    a = Coroutine_New(DEMO_STACK_SIZE*2, dotests);
    printf("[stack=aaaaffdddd]\n");
    printf("%ld (a again)\n", (unsigned char *)active - (unsigned char *)a);
    _Coroutine_Dump();
    printf("[testing merge both ways]\n");
    Coroutine_Delete(a);
    printf("[stack=ffffffdddd]\n");
    _Coroutine_Dump();
    a = Coroutine_New(DEMO_STACK_SIZE, dotests);
    printf("[stack=aaffffdddd]\n");
    _Coroutine_Dump();
    printf("%ld (a again)\n", (unsigned char *)active - (unsigned char *)a);
    b = Coroutine_New(DEMO_STACK_SIZE, dotests);
    printf("[stack=aabbffdddd]\n");
    _Coroutine_Dump();
    printf("%ld (b again)\n", (unsigned char *)active - (unsigned char *)b);
    c = Coroutine_New(DEMO_STACK_SIZE, dotests);
    printf("[stack=aabbccdddd]\n");
    printf("%ld (c again)\n", (unsigned char *)active - (unsigned char *)c);
    Coroutine_Delete(a);
    printf("[stack=ffbbccdddd]\n");
    Coroutine_Delete(c);
    printf("[stack=ffbbffdddd]\n");
    Coroutine_Delete(b);
    printf("[stack=ffffffdddd]\n");
    _Coroutine_Dump();
    a = Coroutine_New(DEMO_STACK_SIZE*3, dotests);
    printf("[stack=aaaaaadddd]\n");
    printf("%ld (a again)\n", (unsigned char *)active - (unsigned char *)a);
    _Coroutine_Dump();

    Coroutine_Delete(a);
    printf("[stack=ffffffdddd]\n");
    _Coroutine_Dump();
    Coroutine_Delete(d);
    printf("[stack=fffffffffff]\n");
    _Coroutine_Dump();
    a = Coroutine_New(COROUTINE_MINIMUM_STACK_SIZE, dotests);
    printf("[stack=affffffffff]\n");
    _Coroutine_Dump();
    b = Coroutine_New(COROUTINE_MINIMUM_STACK_SIZE, dotests);
    printf("[stack=abfffffffff]\n");
    _Coroutine_Dump();
    c = Coroutine_New(COROUTINE_MINIMUM_STACK_SIZE, dotests);
    printf("[stack=abcffffffff]\n");
    _Coroutine_Dump();
    Coroutine_Delete(a);
    printf("[stack=fbcffffffff]\n");
    _Coroutine_Dump();
    Coroutine_Delete(b);
    printf("[stack=ffcffffffff]\n");
    _Coroutine_Dump();
    a = Coroutine_New(COROUTINE_MINIMUM_STACK_SIZE, dotests);
    printf("[stack=afcffffffff]\n");
    _Coroutine_Dump();

    Coroutine_Delete(c);
    printf("[stack=affffffffff]\n");
    _Coroutine_Dump();

    Coroutine_Delete(a);
    printf("[stack=fffffffffff]\n");
    _Coroutine_Dump();

    return NULL;
}


int main(int argc, char *argv[]) {
    (void)argc;
    (void)argv;
    printf("Hello\n");

    // what stack do we get with no stack limit set
    printf("%d\n", Coroutine_Run(DEMO_STACK_SIZE, dotests, NULL, NULL));
}
