/*
 * Creator: Naman Dixit
 * Notice: © Copyright 2020 Naman Dixit
 */

typedef struct Thread {
    pthread_t thread;
    Char *entity_id;
    Char *resource_id;
} Thread;

typedef struct Thread_Tracker {
    Thread *threads;
    Size *free_list;
    Hash_Table map;
} Thread_Tracker;

internal_function
void threadTrackBegin (Thread_Tracker *t, Thread th)
{
    if (t->threads == NULL) {
        t->map = htCreate(0);
        sbufAdd(t->threads, (Thread){0}); // SInce 0 index out of hash table will be invalid
    }

    Size insertion_index = 0;

    if (sbufElemin(t->free_list) > 0) {
        t->threads[t->free_list[0]] = th;
        insertion_index = t->free_list[0];
        sbufUnsortedRemove(t->free_list, 0);
    } else {
        sbufAdd(t->threads, th);
        insertion_index = sbufElemin(t->threads) - 1;
    }

    htInsert(&t->map, hashString(th.resource_id), insertion_index);
}

internal_function
void threadTrackEnd (Thread_Tracker *t, Char *thread_id)
{
    Size index = htLookup(&t->map, hashString(thread_id));
    sbufAdd(t->free_list, index);
    free(t->threads[index].resource_id);
    free(t->threads[index].entity_id);
    t->threads[index] = (Thread){0};
    htRemove(&t->map, index);
}


internal_function
void* tmProcessLoop (void *arg)
{
    unused_variable(arg);

    Thread_Tracker tt = {0};

    while (true) {
        Thread_Manager_Command command = {0};

        tmCommandDequeue(&command, NULL);
        switch (command.kind) {
            case Thread_Manager_Command_DOCKER_CREATE: {
                pthread_t thread;
                pthread_create(&thread, NULL, &dockerProcessLoop, command.entity_id);
                threadTrackBegin(&tt, (Thread){.entity_id = command.entity_id,
                            .resource_id = command.resource_id,
                            .thread = thread});
            } break;

            case Thread_Manager_Command_DOCKER_DESTROY: {
                Size index = htLookup(&tt.map, hashString(command.resource_id));
                pthread_t thread = tt.threads[index].thread;
                pthread_cancel(thread);
                pthread_join(thread, NULL);
                threadTrackEnd(&tt, command.resource_id);
            } break;

            case Thread_Manager_Command_NONE: {
            } break;
        }
    }
}
