/* This is a Claire header. Do not include it in more that one place.
 * This Claire requires vulkan api:
 *
 *
 * typedef integer VkResult
 *
 * const VkResult VK_SUCCESS
 *
 * typedef integer VkStructureType
 *
 * const VkStructureType VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO
 * const VkStructureType VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO
 * const VkStructureType VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
 *
 * typedef integer VkBufferCreateFlags
 * typedef integer VkDeviceSize
 * typedef integer VkBufferUsageFlags
 * typedef integer VkSharingMode
 *
 * const VkSharingMode VK_SHARING_MODE_EXCLUSIVE
 *
 *
 * typedef handler VkPhysicalDevice
 * typedef handler VkDevice
 * typedef handler VkBuffer
 * typedef handler VkImage
 * typedef handler VkDeviceMemory
 * typedef handler VkCommandBuffer
 *
 * typedef struct {
 *     VkStructureType        sType;
 *     const void*            pNext;
 *     VkBufferCreateFlags    flags;
 *     VkDeviceSize           size;
 *     VkBufferUsageFlags     usage;
 *     VkSharingMode          sharingMode;
 *     uint32_t               queueFamilyIndexCount;
 *     const uint32_t*        pQueueFamilyIndices;
 * } VkBufferCreateInfo
 *
 * typedef integer VkMemoryPropertyFlags
 *
 * const VkMemoryPropertyFlags VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
 *
 *  typedef struct {
 *     VkMemoryPropertyFlags    propertyFlags;
 *     ...
 * } VkMemoryType
 *
 * #define VK_MAX_MEMORY_TYPES 32
 *
 * typedef struct {
 *     uint32_t        memoryTypeCount;
 *     VkMemoryType    memoryTypes[VK_MAX_MEMORY_TYPES];
 *     ...
 * } VkPhysicalDeviceMemoryProperties
 *
 * void vkGetPhysicalDeviceMemoryProperties(
 *     VkPhysicalDevice                      physicalDevice,
 *     VkPhysicalDeviceMemoryProperties*     pMemoryProperties)
 *
 * typedef void VkAllocationCallbacks
 * (seriously, this type is only used as a pointer in an exposed api function, and it always takes NULL value)
 *
 * VkResult vkCreateBuffer(
 *     VkDevice       device,
 *     const          VkBufferCreateInfo* pCreateInfo,
 *     const          VkAllocationCallbacks* pAllocator,
 *     VkBuffer*      pBuffer)
 *
 * typedef struct {
 *     VkDeviceSize    size;
 *     VkDeviceSize    alignment;
 *     uint32_t        memoryTypeBits;
 * } VkMemoryRequirements
 *
 * void vkGetBufferMemoryRequirements(
 *     VkDevice                                    device,
 *     VkBuffer                                    buffer,
 *     VkMemoryRequirements*                       pMemoryRequirements)
 *
 * typedef integer VkImageCreateFlags
 * typedef integer VkImageType
 *
 * const VkImageType VK_IMAGE_TYPE_2D
 *
 * typedef integer VkFormat
 *
 * typedef struct {
 *     uint32_t    width;
 *     uint32_t    height;
 *     uint32_t    depth;
 * } VkExtent3D
 *
 * typedef integer VkSampleCountFlagBits
 *
 * const VkSampleCountFlagBits VK_SAMPLE_COUNT_1_BIT
 *
 * typedef integer VkImageTiling
 *
 * const VkImageTiling VK_IMAGE_TILING_LINEAR
 *
 * const VkImageTiling VK_IMAGE_TILING_OPTIMAL
 *
 * typedef integer VkImageUsageFlags
 * typedef integer VkImageLayout
 *
 * const VkImageLayout VK_IMAGE_LAYOUT_UNDEFINED
 *
 * typedef struct {
 *     VkStructureType          sType;
 *     const void*              pNext;
 *     VkImageCreateFlags       flags;
 *     VkImageType              imageType;
 *     VkFormat                 format;
 *     VkExtent3D               extent;
 *     uint32_t                 mipLevels;
 *     uint32_t                 arrayLayers;
 *     VkSampleCountFlagBits    samples;
 *     VkImageTiling            tiling;
 *     VkImageUsageFlags        usage;
 *     VkSharingMode            sharingMode;
 *     uint32_t                 queueFamilyIndexCount;
 *     const uint32_t*          pQueueFamilyIndices;
 *     VkImageLayout            initialLayout;
 * } VkImageCreateInfo
 *
 * VkResult vkCreateImage(
 *     VkDevice                                    device,
 *     const VkImageCreateInfo*                    pCreateInfo,
 *     const VkAllocationCallbacks*                pAllocator,
 *     VkImage*                                    pImage)
 *
 * void vkGetImageMemoryRequirements(
 *     VkDevice                                    device,
 *     VkImage                                     image,
 *     VkMemoryRequirements*                       pMemoryRequirements)
 *
 * typedef struct {
 *     VkStructureType    sType;
 *     const void*        pNext;
 *     VkDeviceSize       allocationSize;
 *     uint32_t           memoryTypeIndex;
 * } VkMemoryAllocateInfo
 *
 * VkResult vkAllocateMemory(
 *     VkDevice                                    device,
 *     const VkMemoryAllocateInfo*                 pAllocateInfo,
 *     const VkAllocationCallbacks*                pAllocator,
 *     VkDeviceMemory*                             pMemory)
 *
 * VkResult vkBindBufferMemory(
 *     VkDevice                                    device,
 *     VkBuffer                                    buffer,
 *     VkDeviceMemory                              memory,
 *     VkDeviceSize                                memoryOffset)
 *
 * VkResult vkBindImageMemory(
 *     VkDevice                                    device,
 *     VkImage                                     image,
 *     VkDeviceMemory                              memory,
 *     VkDeviceSize                                memoryOffset)
 *
 * void vkDestroyBuffer(
 *     VkDevice                                    device,
 *     VkBuffer                                    buffer,
 *     const VkAllocationCallbacks*                pAllocator)
 *
 * void vkDestroyImage(
 *     VkDevice                                    device,
 *     VkImage                                     image,
 *     const VkAllocationCallbacks*                pAllocator)
 */

#include "../../l1/core/util.h"
#include "../../l1_5/core/buff_rb_tree_node.h"
#include "../../../gen/l1_5/BufRBTree_MapU64ToU64.h"

typedef struct{
    U64 block;
    U64 start;
    U64 len;
} MargaretIAFreeSegment;

#include "../../l1/core/uint_segments.h"

U64 margaret_get_length_resp_alignment(U64 start, U64 full_len, U8 alignment_exp) {
    if (start & ((1ull << alignment_exp) - 1)) {
        U64 pad_left = (1ull << alignment_exp) - (start & ((1ull << alignment_exp) - 1));
        return full_len >= pad_left ? full_len - pad_left : 0;
    }
    return full_len;
}

bool MargaretIAFreeSegment_less_resp_align(const MargaretIAFreeSegment* A, const MargaretIAFreeSegment* B, U8 alignment_exp){
    U64 A_len = margaret_get_length_resp_alignment(A->start, A->len, alignment_exp);
    U64 B_len = margaret_get_length_resp_alignment(B->start, B->len, alignment_exp);
    if (A_len == B_len) {
        if (A->block == B->block) {
            return A->start < B->start;
        }
        return A->block < B->block;
    }
    return A_len < B_len;
}


/* Does not include all parameters needed for relocation. Because relocation is needed only
 * during controlled defragmentation */
typedef struct {
    U64 block;
    VkImage image;
    U64 start;
} MargaretImgAllocation;

/* Not primitive */
typedef struct {
    BufRBTree_MapU64ToU64 images;
    U64 capacity;
    U64 occupation_counter;
    VkDeviceMemory mem_hand;
    void* mapped_memory;
} MargaretImgAllocatorOneBlock;

void MargaretImgAllocatorOneBlock_drop(MargaretImgAllocatorOneBlock self){
    BufRBTree_MapU64ToU64_drop(self.images);
}

#include "../../../gen/l1/eve/margaret/VecMargaretImgAllocatorOneBlock.h"

#include "../../../gen/l1/VecAndSpan_U8.h"
#include "../../../gen/l1/eve/margaret/VecMargaretIAFreeSegment.h"
#include "../../../gen/l1/eve/margaret/OptionMargaretIAFreeSegment.h"
#include "../../../gen/l1_5/eve/margaret/BufRBTreeByLenRespAlign_SetMargaretIAFreeSegment.h"
#include "../../../gen/l1/eve/margaret/OptionBufRBTreeByLenRespAlign_SetMargaretIAFreeSegment.h"

#define MARGARET_ALLOC_LIMIT_ALIGNMENT_EXP 28

/* Superstructure for managing free segments of memory of some type in ALL BLOCKS */
typedef struct {
    OptionBufRBTreeByLenRespAlign_SetMargaretIAFreeSegment free_space_in_memory[MARGARET_ALLOC_LIMIT_ALIGNMENT_EXP];
    VecU8 set_present;
} MargaretMemFreeSpaceManager;

MargaretMemFreeSpaceManager MargaretMemFreeSpaceManager_new(){
    MargaretMemFreeSpaceManager res = {.set_present = VecU8_new_zeroinit(1)};
    res.set_present.buf[0] = 3;
    for (U8 algn = 0; algn < MARGARET_ALLOC_LIMIT_ALIGNMENT_EXP; algn++)
        res.free_space_in_memory[algn] = None_BufRBTreeByLenRespAlign_SetMargaretIAFreeSegment();
    res.free_space_in_memory[3] = Some_BufRBTreeByLenRespAlign_SetMargaretIAFreeSegment(
        BufRBTreeByLenRespAlign_SetMargaretIAFreeSegment_new_reserved(3, 1));
    return res;
}

void MargaretMemFreeSpaceManager_drop(MargaretMemFreeSpaceManager self){
    for (U8 alignment_exp = 0; alignment_exp < MARGARET_ALLOC_LIMIT_ALIGNMENT_EXP; alignment_exp++)
        OptionBufRBTreeByLenRespAlign_SetMargaretIAFreeSegment_drop(self.free_space_in_memory[alignment_exp]);
    VecU8_drop(self.set_present);
}

void MargaretMemFreeSpaceManager_erase(MargaretMemFreeSpaceManager* man, U64 block, U64 start, U64 len){
    if (len == 0)
        return;
    assert(man->set_present.len > 0);
    for (size_t aj = 0; aj < man->set_present.len; aj++) {
        U8 alignment = man->set_present.buf[aj];
        assert(alignment < MARGARET_ALLOC_LIMIT_ALIGNMENT_EXP);
        assert(man->free_space_in_memory[alignment].variant == Option_Some);
        bool eret = BufRBTreeByLenRespAlign_SetMargaretIAFreeSegment_erase(&
            man->free_space_in_memory[alignment].some,
            &(MargaretIAFreeSegment){.block = block, .start = start, .len = len});
        assert(eret);
    }
}

void MargaretMemFreeSpaceManager_insert(MargaretMemFreeSpaceManager* man, U64 block, U64 start, U64 len){
    if (len == 0)
        return;
    assert(man->set_present.len > 0);  /* MargaretMemFreeSpaceManager will do that for us with 2^3 */
    for (size_t aj = 0; aj < man->set_present.len; aj++) {
        U8 alignment = man->set_present.buf[aj];
        assert(alignment < MARGARET_ALLOC_LIMIT_ALIGNMENT_EXP);
        assert(man->free_space_in_memory[alignment].variant == Option_Some);
        bool iret = BufRBTreeByLenRespAlign_SetMargaretIAFreeSegment_insert(&
            man->free_space_in_memory[alignment].some, (MargaretIAFreeSegment){.block = block, .start = start, .len = len});
        assert(iret);
    }
}

OptionMargaretIAFreeSegment MargaretMemFreeSpaceManager_search(
    MargaretMemFreeSpaceManager* man, U8 alignment_exp, U64 req_size) {
    check(alignment_exp < MARGARET_ALLOC_LIMIT_ALIGNMENT_EXP);
    if (man->free_space_in_memory[alignment_exp].variant == Option_None) {
        assert(man->set_present.len > 0);
        assert(man->free_space_in_memory[man->set_present.buf[0]].variant == Option_Some);
        assert(man->free_space_in_memory[alignment_exp].variant == Option_None);
        BufRBTreeByLenRespAlign_SetMargaretIAFreeSegment* have = &man->free_space_in_memory[man->set_present.buf[0]].some;
        man->free_space_in_memory[alignment_exp] = Some_BufRBTreeByLenRespAlign_SetMargaretIAFreeSegment(
            BufRBTreeByLenRespAlign_SetMargaretIAFreeSegment_new_reserved(alignment_exp, have->el.len));
        for (size_t i = 0; i < have->el.len; i++) {
            BufRBTreeByLenRespAlign_SetMargaretIAFreeSegment_insert(
                &man->free_space_in_memory[alignment_exp].some, *VecMargaretIAFreeSegment_at(&have->el, i));
        }
        VecU8_append(&man->set_present, alignment_exp);
    }
    assert(man->free_space_in_memory[alignment_exp].variant == Option_Some);
    U64 sit = BufRBTreeByLenRespAlign_SetMargaretIAFreeSegment_find_min_grtr_or_eq(
        &man->free_space_in_memory[alignment_exp].some, &(MargaretIAFreeSegment){.len = req_size,});
    if (sit == 0)
        return None_MargaretIAFreeSegment();
    return Some_MargaretIAFreeSegment(*BufRBTreeByLenRespAlign_SetMargaretIAFreeSegment_at_iter(
        &man->free_space_in_memory[alignment_exp].some, sit));
}

/* VkDevice and VkPhysicalDevice stay remembered here. Don't forget that, please */
typedef struct {
    VecMargaretImgAllocatorOneBlock blocks;
    MargaretMemFreeSpaceManager mem_free_space;
    VkDevice device;
    VkPhysicalDevice physical_device;
    U8 memory_type_id;
} MargaretImgAllocator;

void MargaretImgAllocator__erase_gap(MargaretImgAllocator* self, U64 block_id, U64 start, U64 len){
    MargaretMemFreeSpaceManager_erase(&self->mem_free_space, block_id, start, len);
    MargaretImgAllocatorOneBlock* BLOCK = VecMargaretImgAllocatorOneBlock_mat(&self->blocks, block_id);
    BLOCK->occupation_counter += len;
    assert(BLOCK->occupation_counter <= BLOCK->capacity);
}

void MargaretImgAllocator__insert_gap(MargaretImgAllocator* self, U64 block_id, U64 start, U64 len){
    MargaretMemFreeSpaceManager_insert(&self->mem_free_space, block_id, start, len);
    MargaretImgAllocatorOneBlock* BLOCK = VecMargaretImgAllocatorOneBlock_mat(&self->blocks, block_id);
    assert(len <= BLOCK->occupation_counter);
    BLOCK->occupation_counter -= len;
}

void MargaretImgAllocator__add_block(MargaretImgAllocator* self, U64 capacity){
    VkDeviceMemory memory;
    printf("DEBUG MargaretImgAllocator: allocating block of size %lu\n", capacity);
    check(vkAllocateMemory(self->device, &(VkMemoryAllocateInfo){
        .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
        .allocationSize = capacity, .memoryTypeIndex = self->memory_type_id
    }, NULL, &memory) == VK_SUCCESS);
    VecMargaretImgAllocatorOneBlock_append(&self->blocks, (MargaretImgAllocatorOneBlock){
        .images = BufRBTree_MapU64ToU64_new_reserved(1),
        .capacity = capacity,
        .occupation_counter = capacity, // sounds sus
        .mem_hand = memory,
        .mapped_memory = NULL /* not supported */});
}

/* Idk where to put it */
void MargaretImgAllocator__debug(const MargaretImgAllocator* self){
    printf("=============================== MargaretImgAllocator ============\n"
        "All blocks: { ");
    for (size_t i = 0; i < self->blocks.len; i++) {
        printf(" %lu/%lu ", self->blocks.buf[i].occupation_counter, self->blocks.buf[i].capacity);
    }
    printf("}\n");
    for (size_t ai = 0; ai < self->mem_free_space.set_present.len; ai++) {
        U8 alignment_exp = self->mem_free_space.set_present.buf[ai];
        printf("Free spaces at alignment_exp = %d:\n", (int)alignment_exp);
        assert(self->mem_free_space.free_space_in_memory[alignment_exp].variant == Option_Some);
        const BufRBTreeByLenRespAlign_SetMargaretIAFreeSegment* set =
            &self->mem_free_space.free_space_in_memory[alignment_exp].some;
        assert(set->guest == alignment_exp);
        for (size_t i = 0; i < set->el.len; i++) {
            const MargaretIAFreeSegment *free_seg = &set->el.buf[i];
            printf(" Block %lu, start %lu, len %lu\n", free_seg->block, free_seg->start, free_seg->len);
        }
    }
}

MargaretImgAllocator MargaretImgAllocator_new(
    VkDevice device, VkPhysicalDevice physical_device, U8 memory_type_id, U64 initial_block_size
    ){
    MargaretImgAllocator self = {
        .blocks = VecMargaretImgAllocatorOneBlock_new(),
        .mem_free_space = MargaretMemFreeSpaceManager_new(),
        .device = device,
        .physical_device = physical_device,
        .memory_type_id = memory_type_id,
    };
    MargaretImgAllocator__add_block(&self, initial_block_size);
    MargaretImgAllocator__insert_gap(&self, 0, 0, initial_block_size);
    // MargaretImgAllocator__debug(&self);
    return self;
}

U64 margaret_get_alignment_left_padding(U64 unaligned_start, U8 alignment_exp){
    U64 hit = unaligned_start & (1ull << alignment_exp) - 1;
    return (hit ? (1ull << alignment_exp) - hit : 0);
}

U64 MargaretImgAllocator__add_img_given_gap(
    MargaretImgAllocator* self, MargaretIAFreeSegment segment, U64 required_size, U8 alignment_exp
    ){
    U64 gap_start = segment.start;
    U64 gap_len = segment.len;

    U64 af = margaret_get_alignment_left_padding(gap_start, alignment_exp);
    U64 aligned_start = gap_start + af;
    assert(aligned_start + required_size <= gap_start + gap_len);
    MargaretImgAllocator__erase_gap(self, segment.block, gap_start, gap_len);
    MargaretImgAllocator__insert_gap(self, segment.block, gap_start, af);
    MargaretImgAllocator__insert_gap(self, segment.block, aligned_start + required_size,
        gap_start + gap_len - (aligned_start + required_size));

    BufRBTree_MapU64ToU64* images = &VecMargaretImgAllocatorOneBlock_mat(&self->blocks, segment.block)->images;
    bool iret = BufRBTree_MapU64ToU64_insert(images, aligned_start, required_size);
    assert(iret);
    return aligned_start;
}

U64Segment MargaretImgAllocator__get_left_free_space(
    const MargaretImgAllocator* self, MargaretImgAllocation allocation){
    const MargaretImgAllocatorOneBlock* block = VecMargaretImgAllocatorOneBlock_at(&self->blocks, allocation.block);
    U64 occ_start = allocation.start;

    U64 prev_occ_it = BufRBTree_MapU64ToU64_find_max_less(&block->images, allocation.start);
    if (prev_occ_it != 0) {
        U64 prev_occ_start;
        U64 prev_occ_taken_size;
        BufRBTree_MapU64ToU64_at_iter(&block->images, prev_occ_it, &prev_occ_start, &prev_occ_taken_size);

        assert(prev_occ_start + prev_occ_taken_size <= occ_start);
        return (U64Segment){
            .start = prev_occ_start + prev_occ_taken_size,
            .len = occ_start - (prev_occ_start + prev_occ_taken_size)};
    }
    return (U64Segment){.start = 0, .len = occ_start};
}

U64Segment MargaretImgAllocator__get_right_free_space(
    const MargaretImgAllocator* self, MargaretImgAllocation allocation){
    const MargaretImgAllocatorOneBlock* block = VecMargaretImgAllocatorOneBlock_at(&self->blocks, allocation.block);
    U64 occ_start = allocation.start;
    VkMemoryRequirements occ_memory_requirements;
    vkGetImageMemoryRequirements(self->device, allocation.image, &occ_memory_requirements);
    U64 occ_taken_size = occ_memory_requirements.size;

    U64 next_occ_it = BufRBTree_MapU64ToU64_find_min_grtr(&block->images, allocation.start);
    if (next_occ_it != 0) {
        U64 next_occ_start;
        U64 next_occ_taken_size;
        BufRBTree_MapU64ToU64_at_iter(&block->images, next_occ_it, &next_occ_start, &next_occ_taken_size);
        assert(occ_start + occ_taken_size <= next_occ_start);
        return (U64Segment){.start = occ_start + occ_taken_size, .len = next_occ_start - (occ_start + occ_taken_size)};
    }
    return (U64Segment){.start = occ_start + occ_taken_size, .len = block->capacity - (occ_start + occ_taken_size)};
}

void MargaretImgAllocator_drop(MargaretImgAllocator self){
    for (size_t bi = 0; bi < self.blocks.len; bi++) {
        vkFreeMemory(self.device, self.blocks.buf[bi].mem_hand, NULL);
    }
    VecMargaretImgAllocatorOneBlock_drop(self.blocks);
    MargaretMemFreeSpaceManager_drop(self.mem_free_space);
}

void MargaretImgAllocator_free(MargaretImgAllocator* self, MargaretImgAllocation allocation){
    U64Segment left_free_space = MargaretImgAllocator__get_left_free_space(self, allocation);
    U64Segment right_free_space = MargaretImgAllocator__get_right_free_space(self, allocation);

    vkDestroyImage(self->device, allocation.image, NULL);

    MargaretImgAllocator__erase_gap(self, allocation.block, left_free_space.start, left_free_space.len);
    MargaretImgAllocator__erase_gap(self, allocation.block, right_free_space.start, right_free_space.len);
    MargaretImgAllocator__insert_gap(self, allocation.block,
        left_free_space.start,
        right_free_space.start + right_free_space.len - left_free_space.start);

    MargaretImgAllocatorOneBlock* block = VecMargaretImgAllocatorOneBlock_mat(&self->blocks, allocation.block);
    bool eret = BufRBTree_MapU64ToU64_erase(&block->images, allocation.start);
    assert(eret);
}

NODISCARD MargaretImgAllocation MargaretImgAllocator__alloc(
    MargaretImgAllocator* self, U64 width, U64 height, VkFormat format,
    VkImageUsageFlags usage_flags
    ){
    VkPhysicalDeviceMaintenance3Properties maintenance3_properties = {
        .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES,
    };
    VkPhysicalDeviceProperties2 properties = {
        .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
        .pNext = &maintenance3_properties,
    };
    vkGetPhysicalDeviceProperties2(self->physical_device, &properties);


    VkImage fresh_img;
    check(vkCreateImage(self->device, &(VkImageCreateInfo){
        .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
        .imageType = VK_IMAGE_TYPE_2D,
        .format = format,
        .extent = (VkExtent3D){.width = width, .height = height,.depth = 1,},
        .mipLevels = 1,
        .arrayLayers = 1,
        .samples = VK_SAMPLE_COUNT_1_BIT,
        .tiling = VK_IMAGE_TILING_OPTIMAL,
        .usage = usage_flags,
        .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
        .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
    }, NULL, &fresh_img) == VK_SUCCESS);
    VkMemoryRequirements mem_requirements;
    vkGetImageMemoryRequirements(self->device, fresh_img, &mem_requirements);

    check(U64_is_2pow(mem_requirements.alignment));
    U8 alignment_exp = U64_2pow_log(mem_requirements.alignment);
    check(mem_requirements.size <= maintenance3_properties.maxMemoryAllocationSize);
    OptionMargaretIAFreeSegment free_gap =
    MargaretMemFreeSpaceManager_search(&self->mem_free_space, alignment_exp, mem_requirements.size);

    if (free_gap.variant == Option_None) {
        assert(self->blocks.len > 0);
        U64 pitch = self->blocks.buf[self->blocks.len - 1].capacity;
        // Old blocks remain intact
        U64 new_capacity = MAX_U64(mem_requirements.size, MIN_U64(2 * pitch, maintenance3_properties.maxMemoryAllocationSize));
        // U64 new_capacity = MAX_U64(mem_requirements.size, MIN_U64(pitch, maintenance3_properties.maxMemoryAllocationSize));
        MargaretImgAllocator__add_block(self, new_capacity);
        U64 bid = self->blocks.len - 1;
        MargaretImgAllocator__insert_gap(self, bid, mem_requirements.size, new_capacity - mem_requirements.size);
        MargaretImgAllocatorOneBlock* block = VecMargaretImgAllocatorOneBlock_mat(&self->blocks, bid);
        block->occupation_counter = mem_requirements.size;
        bool iret = BufRBTree_MapU64ToU64_insert(&block->images, 0, mem_requirements.size);
        assert(iret);
        check(vkBindImageMemory(self->device, fresh_img, block->mem_hand, 0) == VK_SUCCESS);
        // MargaretImgAllocator__debug(self);
        return (MargaretImgAllocation){.block = bid, fresh_img, 0};
    }
    U64 aligned_pos = MargaretImgAllocator__add_img_given_gap(self, free_gap.some, mem_requirements.size, alignment_exp);
    VkDeviceMemory memory = VecMargaretImgAllocatorOneBlock_at(&self->blocks, free_gap.some.block)->mem_hand;
    check(vkBindImageMemory(self->device, fresh_img, memory, aligned_pos) == VK_SUCCESS);
    // MargaretImgAllocator__debug(self);
    return (MargaretImgAllocation){.block = free_gap.some.block, .image = fresh_img, .start = aligned_pos};
}

typedef struct{
    MargaretImgAllocation a;
    U64 width;
    U64 height;
    VkFormat format;
    VkImageUsageFlags usage_flags;
    VkImageLayout current_layout;
} MargaretImg;

NODISCARD MargaretImg MargaretImgAllocator_alloc(
    MargaretImgAllocator* self, U64 width, U64 height, VkFormat format,
    VkImageUsageFlags usage_flags
    ){
    return (MargaretImg){.a = MargaretImgAllocator__alloc(self, width, height, format, usage_flags),
    .width = width, .height = height, .format = format, .usage_flags = usage_flags,
        .current_layout = VK_IMAGE_LAYOUT_UNDEFINED};
}
