//
// Generated by dtk
// Translation Unit: JKRAramHeap.cpp
//

#include "JSystem/JKernel/JKRAramHeap.h"
#include "JSystem/JKernel/JKRHeap.h"
#include "dolphin/os/OS.h"
#include "global.h"

JSUList<JKRAramBlock> JKRAramHeap::sAramList;

/* 802B53E8-802B54B8       .text __ct__11JKRAramHeapFUlUl */
JKRAramHeap::JKRAramHeap(u32 startAddress, u32 size) {
    OSInitMutex(&mMutex);

    mHeap = JKRHeap::findFromRoot(this);
    mSize = ALIGN_PREV(size, 0x20);
    mHeadAddress = ALIGN_NEXT(startAddress, 0x20);
    mTailAddress = mHeadAddress + mSize;
    mGroupId = -1;

    JKRAramBlock* block = new (mHeap, 0) JKRAramBlock(mHeadAddress, 0, mSize, -1, false);
    sAramList.append(&block->mBlockLink);
}

/* 802B54B8-802B5560       .text __dt__11JKRAramHeapFv */
JKRAramHeap::~JKRAramHeap() {
    JSUListIterator<JKRAramBlock> iterator(sAramList.getFirst());
    while (iterator != sAramList.getEnd()) {
        delete (iterator++).getObject();
    }
}

/* 802B5560-802B55D8       .text alloc__11JKRAramHeapFUlQ211JKRAramHeap10EAllocMode */
JKRAramBlock* JKRAramHeap::alloc(u32 size, JKRAramHeap::EAllocMode allocationMode) {
    lock();

    JKRAramBlock* block;
    if (allocationMode == JKRAramHeap::HEAD) {
        block = allocFromHead(size);
    } else {
        block = allocFromTail(size);
    }

    unlock();
    return block;
}

/* 802B55D8-802B5660       .text allocFromHead__11JKRAramHeapFUl */
JKRAramBlock* JKRAramHeap::allocFromHead(u32 size) {
    u32 alignedSize = ALIGN_NEXT(size, 0x20);
    u32 bestFreeSize = UINT32_MAX;
    JKRAramBlock* bestBlock = NULL;

    JSUList<JKRAramBlock>* list = &sAramList;
    for (JSUListIterator<JKRAramBlock> iterator = list; iterator != list->getEnd(); ++iterator) {
        JKRAramBlock* block = iterator.getObject();
        if (block->mFreeSize < alignedSize)
            continue;
        if (bestFreeSize <= block->mFreeSize)
            continue;

        bestFreeSize = block->mFreeSize;
        bestBlock = block;

        if (block->mFreeSize == alignedSize) {
            break;
        }
    }

    if (bestBlock) {
        return bestBlock->allocHead(alignedSize, mGroupId, this);
    }

    return NULL;
}

/* 802B5660-802B56D8       .text allocFromTail__11JKRAramHeapFUl */
JKRAramBlock* JKRAramHeap::allocFromTail(u32 size) {
    u32 alignedSize = ALIGN_NEXT(size, 0x20);
    JKRAramBlock* tailBlock = NULL;

    JSUList<JKRAramBlock>* list = &sAramList;
    JSUListIterator<JKRAramBlock> iterator = list->getLast();
    for (; iterator != list->getEnd(); --iterator) {
        JKRAramBlock* block = iterator.getObject();
        if (block->mFreeSize >= alignedSize) {
            tailBlock = block;
            break;
        }
    }

    if (tailBlock) {
        return tailBlock->allocTail(alignedSize, mGroupId, this);
    }

    return NULL;
}

/* 802B56D8-802B5750       .text getFreeSize__11JKRAramHeapFv */
u32 JKRAramHeap::getFreeSize() {
    u32 maxFreeSize = 0;

    lock();

    JSUList<JKRAramBlock>* list = &sAramList;
    JSUListIterator<JKRAramBlock> iterator = list;
    for (; iterator != list->getEnd(); ++iterator) {
        if (iterator->mFreeSize > maxFreeSize) {
            maxFreeSize = iterator->mFreeSize;
        }
    }

    unlock();
    return maxFreeSize;
}

/* 802B5750-802B57BC       .text getTotalFreeSize__11JKRAramHeapFv */
u32 JKRAramHeap::getTotalFreeSize() {
    u32 totalFreeSize = 0;

    lock();

    JSUList<JKRAramBlock>* list = &sAramList;
    JSUListIterator<JKRAramBlock> iterator = list;
    for (; iterator != list->getEnd(); ++iterator) {
        totalFreeSize += iterator->mFreeSize;
    }

    unlock();
    return totalFreeSize;
}

/* 802B57BC-802B590C       .text dump__11JKRAramHeapFv */
void JKRAramHeap::dump() {
    lock();

    OSReport("\nJKRAramHeap dump\n");
    OSReport(" attr  address:   size    gid\n");
    u32 bytesUsed = 0;
    for (JSUListIterator<JKRAramBlock> iterator = sAramList.getFirst() ; iterator != sAramList.getEnd(); ++iterator) {
        if (iterator->mSize) {
            const char* type;
            OSReport("%s %08x: %08x  %3d\n", iterator->isTempMemory() ? " temp" : "alloc", iterator->mAddress, iterator->mSize, iterator->mGroupId);
        }
        if (iterator->mFreeSize) {
            OSReport(" free %08x: %08x    0\n", iterator->mAddress + iterator->mSize, iterator->mFreeSize);
        }
        bytesUsed += iterator->mSize;
    }
    OSReport("%d / %d bytes (%6.2f%%) used\n", bytesUsed, mSize, (f32)bytesUsed / (f32)mSize * 100.0f);

    unlock();
}
