#include <stdint.h>
#include <stddef.h>

#define HEAP_SIZE 0x10000

extern uint8_t __malloc_start;

static uint8_t* heap = &__malloc_start;

typedef struct block {
    size_t size;
    int free;
    struct block* next;
} block;

static block* free_list;

static intptr_t next_virtual = 0x10000000;

intptr_t compute_virtual_base() {
    intptr_t v = next_virtual;
    next_virtual += 0x1000; // or size
    return v;
}

void mem_init() {
    free_list = (block*)heap;

    free_list->size = HEAP_SIZE - sizeof(block);
    free_list->free = 1;
    free_list->next = NULL;
}

void* my_malloc(size_t size) {
    block* curr = free_list;

    while (curr) {
        if (curr->free && curr->size >= size) {

            if (curr->size > size + sizeof(block)) {
                block* newb =
                    (block*)((uint8_t*)curr + sizeof(block) + size);

                newb->size = curr->size - size - sizeof(block);
                newb->free = 1;
                newb->next = curr->next;

                curr->next = newb;
                curr->size = size;
            }

            curr->free = 0;

            // TODO: lift off delta to a earlier stage

            void* phys = (uint8_t*)curr + sizeof(block);

            // compute delta for this allocation
            intptr_t delta = compute_virtual_base() - (intptr_t)phys;

            // apply hardware instruction assumption:
            asm volatile("add_delta %0, %0, %1"
                         : "+r"(phys)
                         : "r"(delta));

            return phys;
        }

        curr = curr->next;
    }

    return NULL;
}

void my_free(void* ptr) {
    if (!ptr) return;

    // delta is guaranteed 0 → treat pointer as physical
    block* b = (block*)((uint8_t*)ptr - sizeof(block));

    b->free = 1;

    // coalesce adjacent free blocks (pure physical heap logic)
    block* curr = free_list;

    while (curr && curr->next) {
        if (curr->free && curr->next->free) {
            curr->size += sizeof(block) + curr->next->size;
            curr->next = curr->next->next;
        } else {
            curr = curr->next;
        }
    }
}



