/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 26.08.2018
 *
 * Pointer check table
 *
 */

// TOP

internal Ptr_Table
make_Ptr_table(void *mem, umem size){
    Ptr_Table table = {};
    i32 max = (i32)(size/8);
    if (max > 0){
        table.mem = mem;
        u8 *cursor = (u8*)mem;
        table.hashes = (u64*)cursor;
        cursor += 8*max;
        table.count = 0;
        table.max = max;
        block_fill_ones(table.hashes, sizeof(*table.hashes)*max);
    }
    return(table);
}

internal i32
max_to_memsize_Ptr_table(i32 max){
    return(max*8);
}

internal b32
at_max_Ptr_table(Ptr_Table *table){
    if (table->max > 0 && (table->count + 1)*8 <= table->max*7){
        return(false);
    }
    return(true);
}

internal b32
insert_Ptr_table(Ptr_Table *table, void**key){
    i32 max = table->max;
    if (max > 0){
        i32 count = table->count;
        if ((count + 1)*8 <= max*7){
            u64 hash = 0;
            block_copy(&hash, key, 8);
            if (hash >= 18446744073709551614ULL){ hash += 2; }
            i32 first_index = hash%max;
            i32 index = first_index;
            u64 *hashes = table->hashes;
            for (;;){
                if (hashes[index] == 18446744073709551615ULL){
                    table->dirty_slot_count += 1;
                }
                if (hashes[index] == 18446744073709551615ULL || hashes[index] == 18446744073709551614ULL){
                    hashes[index] = hash;
                    table->count += 1;
                    return(true);
                }
                if (hashes[index] == hash) return(false);
                index = (index + 1)%max;
                if (index == first_index) return(false);
            }
        }
    }
    return(false);
}

internal b32
lookup_Ptr_table(Ptr_Table *table, void**key){
    i32 max = table->max;
    if (max > 0){
        u64 hash = 0;
        block_copy(&hash, key, 8);
        if (hash >= 18446744073709551614ULL){ hash += 2; }
        i32 first_index = hash%max;
        i32 index = first_index;
        u64 *hashes = table->hashes;
        for (;;){
            if (hashes[index] == 18446744073709551615ULL) break;
            if (hashes[index] == hash){
                return(true);
            }
            index = (index + 1)%max;
            if (index == first_index) break;
        }
    }
    return(false);
}

internal b32
erase_Ptr_table(Ptr_Table *table, void**key){
    i32 max = table->max;
    if (max > 0 && table->count > 0){
        u64 hash = 0;
        block_copy(&hash, key, 8);
        if (hash >= 18446744073709551614ULL){ hash += 2; }
        i32 first_index = hash%max;
        i32 index = first_index;
        u64 *hashes = table->hashes;
        for (;;){
            if (hashes[index] == 18446744073709551615ULL) break;
            if (hashes[index] == hash){
                hashes[index] = 18446744073709551614ULL;
                table->count -= 1;
                return(true);
            }
            index = (index + 1)%max;
            if (index == first_index) break;
        }
    }
    return(false);
}

internal b32
move_Ptr_table(Ptr_Table *dst_table, Ptr_Table *src_table){
    if ((src_table->count + dst_table->count)*8 <= dst_table->max*7){
        i32 max = src_table->max;
        u64 *hashes = src_table->hashes;
        for (i32 index = 0; index < max; index += 1){
            if (hashes[index] != 18446744073709551615ULL && hashes[index] != 18446744073709551614ULL){
                void* key_;
                void**key = &key_;
                block_copy(key, &hashes[index], 8);
                insert_Ptr_table(dst_table, key);
            }
        }
        return(true);
    }
    return(false);
}

internal b32
insert_Ptr_table(Ptr_Table *table, void* key){
    return(insert_Ptr_table(table, &key));
}

internal b32
lookup_Ptr_table(Ptr_Table *table, void* key){
    return(lookup_Ptr_table(table, &key));
}

internal b32
erase_Ptr_table(Ptr_Table *table, void* key){
    return(erase_Ptr_table(table, &key));
}

////////////////////////////////

internal void
insert_Ptr_table(Heap *heap, Ptr_Table *table, void* key){
    if (at_max_Ptr_table(table)){
        i32 new_max = (table->max + 1)*2;
        i32 new_mem_size = max_to_memsize_Ptr_table(new_max);
        void *new_mem = heap_allocate(heap, new_mem_size);
        Ptr_Table new_table = make_Ptr_table(new_mem, new_mem_size);
        if (table->mem != 0){
            b32 result = move_Ptr_table(&new_table, table);
            Assert(result);
            AllowLocal(result);
            heap_free(heap, table->mem);
        }
        *table = new_table;
    }
    b32 result = insert_Ptr_table(table, &key);
    Assert(result);
    AllowLocal(result);
}

////////////////////////////////

internal u32_Ptr_Table
make_u32_Ptr_table(void *mem, umem size){
    u32_Ptr_Table table = {};
    i32 max = (i32)(size/16);
    if (max > 0){
        table.mem = mem;
        u8 *cursor = (u8*)mem;
        table.hashes = (u64*)cursor;
        cursor += 8*max;
        table.vals = (void**)cursor;
        table.count = 0;
        table.max = max;
        block_fill_ones(table.hashes, sizeof(*table.hashes)*max);
    }
    return(table);
}

internal i32
max_to_memsize_u32_Ptr_table(i32 max){
    return(max*16);
}

internal b32
at_max_u32_Ptr_table(u32_Ptr_Table *table){
    if (table->max > 0 && (table->count + 1)*8 <= table->max*7){
        return(false);
    }
    return(true);
}

internal b32
insert_u32_Ptr_table(u32_Ptr_Table *table, u32*key, void**val){
    i32 max = table->max;
    if (max > 0){
        i32 count = table->count;
        if ((count + 1)*8 <= max*7){
            u64 hash = 0;
            block_copy(&hash, key, 4);
            if (hash >= 18446744073709551614ULL){ hash += 2; }
            i32 first_index = hash%max;
            i32 index = first_index;
            u64 *hashes = table->hashes;
            for (;;){
                if (hashes[index] == 18446744073709551615ULL){
                    table->dirty_slot_count += 1;
                }
                if (hashes[index] == 18446744073709551615ULL || hashes[index] == 18446744073709551614ULL){
                    hashes[index] = hash;
                    table->vals[index] = *val;
                    table->count += 1;
                    return(true);
                }
                if (hashes[index] == hash) return(false);
                index = (index + 1)%max;
                if (index == first_index) return(false);
            }
        }
    }
    return(false);
}

internal u32_Ptr_Lookup_Result
lookup_u32_Ptr_table(u32_Ptr_Table *table, u32*key){
    u32_Ptr_Lookup_Result result = {};
    i32 max = table->max;
    if (max > 0){
        u64 hash = 0;
        block_copy(&hash, key, 4);
        if (hash >= 18446744073709551614ULL){ hash += 2; }
        i32 first_index = hash%max;
        i32 index = first_index;
        u64 *hashes = table->hashes;
        for (;;){
            if (hashes[index] == 18446744073709551615ULL) break;
            if (hashes[index] == hash){
                result.success = true;
                result.val = &table->vals[index];
                return(result);
            }
            index = (index + 1)%max;
            if (index == first_index) break;
        }
    }
    return(result);
}

internal b32
erase_u32_Ptr_table(u32_Ptr_Table *table, u32*key){
    i32 max = table->max;
    if (max > 0 && table->count > 0){
        u64 hash = 0;
        block_copy(&hash, key, 4);
        if (hash >= 18446744073709551614ULL){ hash += 2; }
        i32 first_index = hash%max;
        i32 index = first_index;
        u64 *hashes = table->hashes;
        for (;;){
            if (hashes[index] == 18446744073709551615ULL) break;
            if (hashes[index] == hash){
                hashes[index] = 18446744073709551614ULL;
                table->count -= 1;
                return(true);
            }
            index = (index + 1)%max;
            if (index == first_index) break;
        }
    }
    return(false);
}

internal b32
move_u32_Ptr_table(u32_Ptr_Table *dst_table, u32_Ptr_Table *src_table){
    if ((src_table->count + dst_table->count)*8 <= dst_table->max*7){
        i32 max = src_table->max;
        u64 *hashes = src_table->hashes;
        for (i32 index = 0; index < max; index += 1){
            if (hashes[index] != 18446744073709551615ULL && hashes[index] != 18446744073709551614ULL){
                u32 key_;
                u32*key = &key_;
                block_copy(key, &hashes[index], 4);
                void**val = &src_table->vals[index];
                insert_u32_Ptr_table(dst_table, key, val);
            }
        }
        return(true);
    }
    return(false);
}

internal b32
lookup_u32_Ptr_table(u32_Ptr_Table *table, u32 *key, void* *val_out){
    u32_Ptr_Lookup_Result result = lookup_u32_Ptr_table(table, key);
    if (result.success){
        *val_out = *result.val;
    }
    return(result.success);
}

internal b32
insert_u32_Ptr_table(u32_Ptr_Table *table, u32*key, void* val){
    return(insert_u32_Ptr_table(table, key, &val));
}

internal b32
insert_u32_Ptr_table(u32_Ptr_Table *table, u32 key, void**val){
    return(insert_u32_Ptr_table(table, &key, val));
}

internal b32
insert_u32_Ptr_table(u32_Ptr_Table *table, u32 key, void* val){
    return(insert_u32_Ptr_table(table, &key, &val));
}

internal u32_Ptr_Lookup_Result
lookup_u32_Ptr_table(u32_Ptr_Table *table, u32 key){
    return(lookup_u32_Ptr_table(table, &key));
}

internal b32
lookup_u32_Ptr_table(u32_Ptr_Table *table, u32 key, void* *val_out){
    return(lookup_u32_Ptr_table(table, &key, val_out));
}

internal b32
erase_u32_Ptr_table(u32_Ptr_Table *table, u32 key){
    return(erase_u32_Ptr_table(table, &key));
}

////////////////////////////////

internal void
insert_u32_Ptr_table(Heap *heap, u32_Ptr_Table *table, u32 key, void* val){
    if (at_max_u32_Ptr_table(table)){
        i32 new_max = (table->max + 1)*2;
        i32 new_mem_size = max_to_memsize_u32_Ptr_table(new_max);
        void *new_mem = heap_allocate(heap, new_mem_size);
        u32_Ptr_Table new_table = make_u32_Ptr_table(new_mem, new_mem_size);
        if (table->mem != 0){
            b32 result = move_u32_Ptr_table(&new_table, table);
            Assert(result);
            AllowLocal(result);
            heap_free(heap, table->mem);
        }
        *table = new_table;
    }
    b32 result = insert_u32_Ptr_table(table, &key, &val);
    Assert(result);
    AllowLocal(result);
}

// BOTTOM