/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 19.05.2017
 *
 * Parse contexts allocation and locking.
 *
 */

// TOP

struct Stored_Parse_Context{
    umem memsize;
    u64 *kw_keywords;
    u64 *pp_keywords;
    u32 kw_max;
    u32 pp_max;
};

struct Stored_Parse_Context_Slot{
    union{
        Stored_Parse_Context_Slot *next;
        Stored_Parse_Context *context;
    };
    b32 freed;
};

struct Parse_Context_Memory{
    Stored_Parse_Context_Slot *parse_context_array;
    u32 parse_context_counter;
    u32 parse_context_max;
    
    Stored_Parse_Context_Slot free_sentinel;
};

internal void
parse_context_init_memory(Parse_Context_Memory *parse_mem, void *mem, umem memsize){
    sll_init_sentinel(&parse_mem->free_sentinel);
    
    parse_mem->parse_context_array = (Stored_Parse_Context_Slot*)mem;
    parse_mem->parse_context_counter = 0;
    parse_mem->parse_context_max = (u32)(memsize / sizeof(*parse_mem->parse_context_array));
}

internal Parse_Context_ID
parse_context_valid_id(Parse_Context_Memory *parse_mem, Parse_Context_ID id){
    Parse_Context_ID valid_id = 0;
    if (id > parse_mem->parse_context_max && id < parse_mem->parse_context_max*2){
        valid_id = id;
    }
    return(valid_id);
}

internal Parse_Context_ID
parse_context_add(Parse_Context_Memory *parse_mem, General_Memory *general, Parser_String_And_Type *kw_sats, u32 kw_count, Parser_String_And_Type *pp_sats, u32 pp_count){
    Stored_Parse_Context_Slot *slot = 0;
    if (parse_mem->free_sentinel.next != &parse_mem->free_sentinel){
        slot = parse_mem->free_sentinel.next;
        sll_remove(&parse_mem->free_sentinel, slot);
    }
    else if (parse_mem->parse_context_counter < parse_mem->parse_context_max){
        slot = &parse_mem->parse_context_array[parse_mem->parse_context_counter++];
    }
    
    u32 result = 0;
    if (slot != 0){
        u32 stride = sizeof(*kw_sats);
        umem kw_memsize = cpp_get_table_memory_size_string_lengths(&kw_sats->length, stride, kw_count);
        umem pp_memsize = cpp_get_table_memory_size_string_lengths(&pp_sats->length, stride, pp_count);
        
        umem memsize = kw_memsize + pp_memsize + sizeof(Stored_Parse_Context);
        void *mem = general_memory_allocate(general, (i32)memsize);
        
        Stored_Parse_Context *parse_context = (Stored_Parse_Context*)mem;
        u8 *kw_mem = (u8*)(parse_context+1);
        u8 *pp_mem = kw_mem + kw_memsize;
        
        Cpp_Keyword_Table kw_table = cpp_make_table(&kw_sats->str, stride, &kw_sats->length, stride, &kw_sats->type, stride, kw_count, kw_mem, kw_memsize);
        
        Cpp_Keyword_Table pp_table = cpp_make_table(&pp_sats->str, stride, &pp_sats->length, stride, &pp_sats->type, stride, pp_count, pp_mem, pp_memsize);
        
        parse_context->memsize = memsize;
        parse_context->kw_keywords = kw_table.keywords;
        parse_context->pp_keywords = pp_table.keywords;
        parse_context->kw_max = kw_table.max;
        parse_context->pp_max = pp_table.max;
        slot->context = parse_context;
        slot->freed = false;
        
        result = (u32)(slot - parse_mem->parse_context_array) + parse_mem->parse_context_max;
    }
    
    return(result);
}

internal u32
parse_context_add_default(Parse_Context_Memory *parse_mem, General_Memory *general){
    Stored_Parse_Context_Slot *slot = 0;
    if (parse_mem->free_sentinel.next != &parse_mem->free_sentinel){
        slot = parse_mem->free_sentinel.next;
        sll_remove(&parse_mem->free_sentinel, slot);
    }
    else if (parse_mem->parse_context_counter < parse_mem->parse_context_max){
        slot = &parse_mem->parse_context_array[parse_mem->parse_context_counter++];
    }
    
    u32 result = 0;
    if (slot != 0){
        umem kw_memsize = cpp_get_table_memory_size_default(CPP_TABLE_KEYWORDS);
        umem pp_memsize = cpp_get_table_memory_size_default(CPP_TABLE_PREPROCESSOR_DIRECTIVES);
        
        umem memsize = kw_memsize + pp_memsize + sizeof(Stored_Parse_Context);
        void *mem = general_memory_allocate(general, (i32)(memsize));
        
        Stored_Parse_Context *parse_context = (Stored_Parse_Context*)mem;
        u8 *kw_mem = (u8*)(parse_context+1);
        u8 *pp_mem = kw_mem + kw_memsize;
        
        Cpp_Keyword_Table kw_table = cpp_make_table_default(CPP_TABLE_KEYWORDS, kw_mem, kw_memsize);
        Cpp_Keyword_Table pp_table = cpp_make_table_default(CPP_TABLE_PREPROCESSOR_DIRECTIVES, pp_mem, pp_memsize);
        
        parse_context->memsize = memsize;
        parse_context->kw_keywords = kw_table.keywords;
        parse_context->pp_keywords = pp_table.keywords;
        parse_context->kw_max = kw_table.max;
        parse_context->pp_max = pp_table.max;
        slot->context = parse_context;
        slot->freed = false;
        
        result = (u32)(slot - parse_mem->parse_context_array) + parse_mem->parse_context_max;
    }
    
    return(result);
}

struct Parse_Context{
    b32 valid;
    Cpp_Keyword_Table kw_table;
    Cpp_Keyword_Table pp_table;
    umem memory_size;
};

internal Parse_Context
parse_context_get(Parse_Context_Memory *parse_mem, Parse_Context_ID id, void *mem, umem memsize){
    Parse_Context result = {0};
    
    Stored_Parse_Context_Slot *slot = 0;
    if (id == 0){
        // do nothing
    }
    else{
        id -= parse_mem->parse_context_max;
    }
    
    if (id < parse_mem->parse_context_counter){
        slot = &parse_mem->parse_context_array[id];
        if (slot->freed){
            slot = 0;
        }
    }
    
    if (slot != 0){
        Stored_Parse_Context *context = slot->context;
        if (context->memsize < memsize){
            u8 *base_ptr = (u8*)context;
            u8 *kw_keywords = (u8*)mem + (((u8*)context->kw_keywords) - base_ptr);
            u8 *pp_keywords = (u8*)mem + (((u8*)context->pp_keywords) - base_ptr);
            result.valid = true;
            result.kw_table.keywords = (u64*)kw_keywords;
            result.pp_table.keywords = (u64*)pp_keywords;
            result.kw_table.max = context->kw_max;
            result.pp_table.max = context->pp_max;
            result.memory_size = context->memsize;
            memcpy(mem, context, context->memsize);
        }
    }
    
    return(result);
}

internal void
parse_context_rebase(Parse_Context *parse_mem, void *old_base, void *new_base){
    u8 *old_base_ptr = (u8*)old_base;
    u8 *new_base_ptr = (u8*)new_base;
    
    u8 *ptr = (u8*)parse_mem->kw_table.keywords;
    parse_mem->kw_table.keywords = (u64*)(ptr + (new_base_ptr - old_base_ptr));
    
    ptr = (u8*)parse_mem->pp_table.keywords;
    parse_mem->pp_table.keywords = (u64*)(ptr + (new_base_ptr - old_base_ptr));
}

// BOTTOM