#include "gpuMemory.hpp"

namespace render::kernel
{

void *gpuAllocator(SceKernelMemBlockType type, SceGxmMemoryAttribFlags attribs, size_t size, SceUID *uid)
{
    if (type == SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW) {
        size = ALIGN(size, 256 * 1024);
    } else {
        size = ALIGN(size, 4 * 1024);
    }

    SceUID newUid = sceKernelAllocMemBlock("gpuRegular", type, size, nullptr);

    if (newUid < 0) {
        return nullptr;
    }

    void *baseAddr = nullptr;

    if (sceKernelGetMemBlockBase(newUid, &baseAddr) < 0) {
        return nullptr;
    }

    if (sceGxmMapMemory(baseAddr, size, attribs) < 0) {
        return nullptr;
    }

    *uid = newUid;

    return baseAddr;
}

void gpuFree(SceUID uid)
{
    void *addr;

    if (sceKernelGetMemBlockBase(uid, &addr) < 0) {
        return;
    }

    sceGxmUnmapMemory(addr);
    sceKernelFreeMemBlock(uid);
}

void *gpuFragmentUsseAllocator(size_t size, SceUID *uid, unsigned int *offset)
{
    size = ALIGN(size, 4 * 1024);

    SceUID newUid = sceKernelAllocMemBlock("gpuFragmentUsse", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, size, nullptr);
    if (newUid < 0) {
        return nullptr;
    }

    void *baseAddr = nullptr;

    if (sceKernelGetMemBlockBase(newUid, &baseAddr) < 0) {
        return nullptr;
    }

    if (sceGxmMapFragmentUsseMemory(baseAddr, size, offset) < 0) {
        return nullptr;
    }

    *uid = newUid;

    return baseAddr;
}

void gpuFragmentUsseFree(SceUID uid)
{
    void *addr;

    if (sceKernelGetMemBlockBase(uid, &addr) < 0) {
        return;
    }

    sceGxmUnmapFragmentUsseMemory(addr);
    sceKernelFreeMemBlock(uid);
}

void *gpuVertexUsseAllocator(size_t size, SceUID *uid, unsigned int *offset)
{
    size = ALIGN(size, 4 * 1024);

    SceUID newUid = sceKernelAllocMemBlock("gpuVertexUsse", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, size, nullptr);
    if (newUid < 0) {
        return nullptr;
    }

    void *baseAddr = nullptr;

    if (sceKernelGetMemBlockBase(newUid, &baseAddr) < 0) {
        return nullptr;
    }

    if (sceGxmMapVertexUsseMemory(baseAddr, size, offset) < 0) {
        return nullptr;
    }

    *uid = newUid;

    return baseAddr;
}


void gpuVertexUsseFree(SceUID uid)
{
    void *addr;

    if (sceKernelGetMemBlockBase(uid, &addr) < 0) {
        return;
    }

    sceGxmUnmapVertexUsseMemory(addr);
    sceKernelFreeMemBlock(uid);
}


} // namespace render::kernel