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

#include "JSystem/JUtility/JUTCacheFont.h"
#include "JSystem/JKernel/JKRAram.h"
#include "JSystem/JUtility/JUTAssert.h"
#include "JSystem/JUtility/JUTConsole.h"
#include "dolphin/gx/GX.h"

/* 802C03D4-802C0454       .text __ct__12JUTCacheFontFPC7ResFONTUlP7JKRHeap */
JUTCacheFont::JUTCacheFont(const ResFONT* p_fontRes, u32 cacheSize, JKRHeap* p_heap) {
    initialize_state();
    JUTResFont::initialize_state();
    JUTFont::initialize_state();
    initiate(p_fontRes, NULL, cacheSize, p_heap);
}

/* 802C0454-802C04E8       .text __dt__12JUTCacheFontFv */
JUTCacheFont::~JUTCacheFont() {
    if (isValid()) {
        deleteMemBlocks_CacheFont();
        initialize_state();

        deleteMemBlocks_ResFont();
        JUTResFont::initialize_state();

        JUTFont::initialize_state();
    }
}

/* 802C04E8-802C056C       .text deleteMemBlocks_CacheFont__12JUTCacheFontFv */
void JUTCacheFont::deleteMemBlocks_CacheFont() {
    if (field_0xb0 != 0) {
        delete[] mCacheBuffer;
    }

    delete field_0xac;
    delete mInfoBlock;
    delete mpMemBlocks;
    delete field_0x7c;
    delete field_0x80;
    delete field_0x84;
}

/* 802C056C-802C05A8       .text initialize_state__12JUTCacheFontFv */
void JUTCacheFont::initialize_state() {
    field_0xb0 = 0;
    mCacheBuffer = NULL;

    field_0xac = NULL;
    mInfoBlock = NULL;
    field_0x7c = NULL;
    field_0x80 = NULL;
    field_0x84 = NULL;
    mpMemBlocks = NULL;

    mPagingType = PAGE_TYPE_0;
    mMaxSheetSize = 0;

    mCacheBuffer = NULL;
    field_0x9c = NULL;
    field_0xa0 = NULL;
}

/* 802C05A8-802C0738       .text getMemorySize__12JUTCacheFontFPC7ResFONTPUsPUlPUsPUlPUsPUlPUl */
int JUTCacheFont::getMemorySize(const ResFONT* p_font, u16* o_widCount, u32* o_widSize, u16* o_glyCount, u32* o_glySize, u16* o_mapCount, u32* o_mapSize, u32* o_glyTexSize) {
    if (p_font == NULL) {
        return 0;
    }

    u16 widBlockCount = 0;
    u16 glyBlockCount = 0;
    u16 mapBlockCount = 0;
    u32 totalWidSize = 0;
    u32 totalGlySize = 0;
    u32 totalMapSize = 0;
    u32 maxGlyTexSize = 0;
    u32 glyTexSize;

    u8* fontInf = (u8*)p_font->data;
    for (int i = 0; i < p_font->numBlocks; i++) {
        switch (((BlockHeader*)fontInf)->magic) {
        case 'INF1':
            break;
        case 'WID1':
            totalWidSize += ((BlockHeader*)fontInf)->size;
            widBlockCount++;
            break;
        case 'GLY1':
            totalGlySize += ((BlockHeader*)fontInf)->size;
            glyTexSize = ((ResFONT::GLY1*)fontInf)->textureSize;
            glyBlockCount++;
            if (glyTexSize > maxGlyTexSize) {
                maxGlyTexSize = glyTexSize;
            }
            break;
        case 'MAP1':
            totalMapSize += ((BlockHeader*)fontInf)->size;
            mapBlockCount++;
            break;
        default:
            JUTReportConsole("JUTCacheFont: Unknown data block\n");
            break;
        }

        fontInf += ((BlockHeader*)fontInf)->size;
    }

    if (o_widCount != NULL) {
        *o_widCount = widBlockCount;
    }

    if (o_glyCount != NULL) {
        *o_glyCount = glyBlockCount;
    }

    if (o_mapCount != NULL) {
        *o_mapCount = mapBlockCount;
    }

    if (o_widSize != NULL) {
        *o_widSize = totalWidSize;
    }

    if (o_glySize != NULL) {
        *o_glySize = totalGlySize;
    }

    if (o_mapSize != NULL) {
        *o_mapSize = totalMapSize;
    }

    if (o_glyTexSize != NULL) {
        *o_glyTexSize = maxGlyTexSize;
    }

    return 1;
}

/* 802C0738-802C0798       .text initiate__12JUTCacheFontFPC7ResFONTPvUlP7JKRHeap */
int JUTCacheFont::initiate(const ResFONT* p_fontRes, void* param_1, u32 param_2, JKRHeap* p_heap) {
    if (!internal_initiate(p_fontRes, param_1, param_2, p_heap)) {
        deleteMemBlocks_CacheFont();
        deleteMemBlocks_ResFont();
        JUTFont::initialize_state();
        mValid = false;
        return 0;
    }

    return 1;
}

/* 802C0798-802C089C       .text internal_initiate__12JUTCacheFontFPC7ResFONTPvUlP7JKRHeap */
bool JUTCacheFont::internal_initiate(const ResFONT* p_fontRes, void* param_1, u32 param_2, JKRHeap* param_3) {
    deleteMemBlocks_CacheFont();
    initialize_state();
    deleteMemBlocks_ResFont();
    JUTResFont::initialize_state();
    JUTFont::initialize_state();

    if (p_fontRes == NULL) {
        return false;
    }

    mResFont = p_fontRes;
    mValid = true;
    getMemorySize(p_fontRes, &mWidthBlockNum, &mTotalWidSize, &mGlyphBlockNum, &mTotalGlySize,
                  &mMapBlockNum, &mTotalMapSize, &mMaxSheetSize);

    if (!allocArea(param_1, param_2, param_3)) {
        return false;
    } else if (!allocArray(param_3)) {
        return false;
    }

    setBlock();
    return true;
}

/* 802C089C-802C0A90       .text allocArea__12JUTCacheFontFPvUlP7JKRHeap */
bool JUTCacheFont::allocArea(void* cacheBuffer, u32 param_1, JKRHeap* heap) {
    mInfoBlock = (ResFONT::INF1*)new (heap, 0) ResFONT();
    if (mInfoBlock == NULL) {
        return false;
    }

    if (mTotalWidSize != 0) {
        field_0x7c = new (heap, 0) u8[mTotalWidSize];
        if (field_0x7c == NULL) {
            return false;
        }
    }

    if (mGlyphBlockNum != 0) {
        field_0x80 = new (heap, 0) u8[mGlyphBlockNum * sizeof(ResFONT::GLY1)];
        if (field_0x80 == NULL) {
            return false;
        }

        field_0xac = JKRAram::getManager()->mAramHeap->alloc(
            mTotalGlySize - (mGlyphBlockNum * sizeof(ResFONT::GLY1)), JKRAramHeap::HEAD);
        if (field_0xac == NULL) {
            return false;
        }
    }

    if (mTotalMapSize != 0) {
        field_0x84 = new (heap, 0) u8[mTotalMapSize];
        if (field_0x84 == NULL) {
            return false;
        }
    }

    field_0x94 = mMaxSheetSize + 0x40;
    mCachePage = param_1 / field_0x94;
    u32 v1 = field_0x94 * mCachePage;
    if (mCachePage == 0) {
        return false;
    }

    if (cacheBuffer != NULL) {
        JUT_ASSERT(351, ( (u32)cacheBuffer & 0x1f ) == 0);
        mCacheBuffer = cacheBuffer;
        field_0xb0 = 0;
    } else {
        mCacheBuffer = new (heap, 0x20) u8[v1];
        if (mCacheBuffer == NULL) {
            return false;
        }
        field_0xb0 = 1;
    }

    invalidiateAllCache();
    return true;
}

/* 802C0A90-802C0B78       .text allocArray__12JUTCacheFontFP7JKRHeap */
bool JUTCacheFont::allocArray(JKRHeap* heap) {
    mpMemBlocks = (void**)new (heap, 0) u32[mWidthBlockNum + mGlyphBlockNum + mMapBlockNum];
    if (mpMemBlocks == NULL) {
        return false;
    }

    void** blocks = mpMemBlocks;
    if (mWidthBlockNum) {
        mpWidthBlocks = (ResFONT::WID1**)blocks;
        blocks = blocks + mWidthBlockNum;
    }
    if (mGlyphBlockNum) {
        mpGlyphBlocks = (ResFONT::GLY1**)blocks;
        blocks = blocks + mGlyphBlockNum;
        for (int i = 0; i < mGlyphBlockNum; i++) {
            mpGlyphBlocks[i] = (ResFONT::GLY1*)((u8*)mCacheBuffer + (field_0x94 * i));
        }
    }
    if (mMapBlockNum) {
        mpMapBlocks = (ResFONT::MAP1**)blocks;
    }
    return true;
}

/* 802C0B78-802C0DD0       .text setBlock__12JUTCacheFontFv */
void JUTCacheFont::setBlock() {
    int widthNum = 0;
    int gylphNum = 0;
    int mapNum = 0;
    u8* pWidth = (u8*)field_0x7c;
    ResFONT::GLY1* piVar5 = (ResFONT::GLY1*)field_0x80;
    ResFONT::MAP1* pMap = (ResFONT::MAP1*)field_0x84;
    u32 aramAddress = field_0xac->getAddress();
    mMaxCode = 0xffff;
    const int* pData = (int*)mResFont->data;

    for (int i = 0; i < mResFont->numBlocks; i++) {
        switch (*pData) {
        case 'INF1':
            memcpy(mInfoBlock, pData, 0x20);
            u32 u = mInfoBlock->fontType;
            JUT_ASSERT(447, u < suAboutEncoding_);
            mIsLeadByte = &JUTResFont::saoAboutEncoding_[u];
            break;
        case 'WID1':
            memcpy(pWidth, pData, pData[1]);
            mpWidthBlocks[widthNum] = (ResFONT::WID1*)pWidth;
            pWidth += pData[1];
            widthNum++;
            break;
        case 'GLY1':
            memcpy(piVar5, pData, 0x20);
            JKRAramBlock* iVar1 =
                JKRMainRamToAram((u8*)pData + 0x20, aramAddress, pData[1] - 0x20,
                                 EXPAND_SWITCH_UNKNOWN0, 0, NULL, 0xffffffff);
            if (iVar1 == NULL) {
                OSPanic(__FILE__, 476, "Cannot alloc ARAM area.");
            }
            piVar5->magic = aramAddress;
            if (piVar5->textureSize > mMaxSheetSize) {
                mMaxSheetSize = piVar5->textureSize;
            }
            mpGlyphBlocks[gylphNum] = piVar5;
            gylphNum++;
            piVar5++;
            aramAddress = pData[1] + aramAddress - 0x20;
            break;
        case 'MAP1':
            memcpy(pMap, pData, pData[1]);
            mpMapBlocks[mapNum] = pMap;
            if (mMaxCode > mpMapBlocks[mapNum]->startCode) {
                mMaxCode = mpMapBlocks[mapNum]->startCode;
            }
            mapNum++;
            pMap = (ResFONT::MAP1*)((u8*)pMap + pData[1]);
            break;
        default:
            JUTReportConsole("Unknown data block\n");
            break;
        }

        pData = (int*)((u8*)pData + pData[1]);
    }
}

/* 802C0DD0-802C0E80       .text determineBlankPage__12JUTCacheFontFv */
JUTCacheFont::TGlyphCacheInfo* JUTCacheFont::determineBlankPage() {
    TGlyphCacheInfo* pVar1 = field_0xa4;
    if (pVar1 != NULL) {
        field_0xa4 = pVar1->mNext;
        TGlyphCacheInfo* pVar2 = pVar1->mNext;
        if (pVar2 == NULL) {
            field_0xa8 = 0;
        } else {
            pVar2->mPrev = NULL;
        }
        return pVar1;
    }

    TGlyphCacheInfo* puVar1 = field_0xa0;
    while (puVar1 != NULL) {
        TGlyphCacheInfo* prev = puVar1->mPrev;
        if (puVar1->field_0x1e == 0) {
            unlink(puVar1);
            field_0xb4++;
            return puVar1;
        }
        puVar1 = prev;
    }

    return NULL;
}

/* 802C0E80-802C0FE8       .text getGlyphFromAram__12JUTCacheFontFPQ212JUTCacheFont15TGlyphCacheInfoPQ212JUTCacheFont10TCachePagePiPi */
void JUTCacheFont::getGlyphFromAram(TGlyphCacheInfo* param_0, TCachePage* pCachePage, int* param_2, int* param_3) {
    /* Nonmatching */
    TGlyphCacheInfo* pGylphCacheInfo = pCachePage;
    memcpy(pGylphCacheInfo, param_0, sizeof(TGlyphCacheInfo));
    prepend(pGylphCacheInfo);
    int iVar3 = pGylphCacheInfo->field_0x16 * pGylphCacheInfo->field_0x18;
    int iVar2 = *param_2 / iVar3;
    pGylphCacheInfo->field_0x8 += iVar2 * iVar3;
    u16 local_30 = pGylphCacheInfo->field_0x8 + iVar3 - 1;
    pGylphCacheInfo->field_0xa = pGylphCacheInfo->field_0xa < local_30 ? pGylphCacheInfo->field_0xa : local_30;
    *param_3 = iVar2;
    *param_2 -= iVar2 * iVar3;
    u8* result =
        JKRAramToMainRam((u32)param_0->mPrev + pGylphCacheInfo->field_0x10 * iVar2, (u8*)(pCachePage + 1),
                         pGylphCacheInfo->field_0x10, EXPAND_SWITCH_UNKNOWN0, 0, NULL, 0xffffffff, NULL);
    JUT_ASSERT(623, result);
    GXInitTexObj(&pCachePage->mTexObj, pCachePage + 1, pGylphCacheInfo->mWidth, pGylphCacheInfo->mHeight,
                 (GXTexFmt)pGylphCacheInfo->mTexFormat, GX_CLAMP, GX_CLAMP, GX_FALSE);
    GXInitTexObjLOD(&pCachePage->mTexObj, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE,
                    GX_ANISO_1);
}

/* 802C0FE8-802C109C       .text loadImage__12JUTCacheFontFi11_GXTexMapID */
void JUTCacheFont::loadImage(int param_0, GXTexMapID texMapId) {
    /* Nonmatching */
    TCachePage* cachePage = loadCache_char_subroutine(&param_0, false);
    if (cachePage != NULL) {
        mWidth = cachePage->field_0xc * (param_0 % int(cachePage->field_0x16));
        mHeight = cachePage->field_0xe * (param_0 / cachePage->field_0x16);
        GXLoadTexObj(getTexObj(cachePage), texMapId);
        if (mPagingType == PAGE_TYPE_1) {
            unlink(cachePage);
            prepend(cachePage);
        }
    }
}

/* 802C109C-802C11E4       .text loadCache_char_subroutine__12JUTCacheFontFPib */
JUTCacheFont::TCachePage* JUTCacheFont::loadCache_char_subroutine(int* param_0, bool param_1) {
    /* Nonmatching */
    TCachePage* rv = NULL;
    for (TCachePage* pCachePage = (TCachePage*)field_0x9c; pCachePage != NULL;
         pCachePage = (TCachePage*)pCachePage->mNext)
    {
        if (pCachePage->field_0x8 <= *param_0 && *param_0 <= pCachePage->field_0xa) {
            rv = pCachePage;
            *param_0 -= pCachePage->field_0x8;
            break;
        }
    }

    if (rv == NULL) {
        int i = 0;
        for (; i < mGlyphBlockNum; i++) {
            if (mpGlyphBlocks[i]->startCode <= *param_0 && *param_0 <= mpGlyphBlocks[i]->endCode) {
                *param_0 -= mpGlyphBlocks[i]->startCode;
                break;
            }
        }
        if (i < mGlyphBlockNum) {
            TCachePage* pBlankPage = (TCachePage*)determineBlankPage();
            if (pBlankPage == NULL) {
                return NULL;
            }
            int texPageIdx;
            getGlyphFromAram((JUTCacheFont::TGlyphCacheInfo*)mpGlyphBlocks[i], pBlankPage, param_0,
                             &texPageIdx);
            mTexPageIdx = texPageIdx;
            field_0x66 = i;
            rv = pBlankPage;
        } else {
            return NULL;
        }
    }
    if (param_1) {
        rv->field_0x1e = 1;
    }
    return rv;
}

/* 802C11E4-802C126C       .text invalidiateAllCache__12JUTCacheFontFv */
void JUTCacheFont::invalidiateAllCache() {
    int* piVar3 = (int*)mCacheBuffer;
    for (int uVar2 = 0; uVar2 < mCachePage; uVar2++) {
        int iVar1;
        if (uVar2 == 0) {
            iVar1 = 0;
        } else {
            iVar1 = (int)piVar3 - field_0x94;
        }
        *piVar3 = iVar1;
        if (uVar2 == mCachePage - 1) {
            iVar1 = 0;
        } else {
            iVar1 = (int)piVar3 + field_0x94;
        }
        piVar3[1] = iVar1;
        piVar3 = (int*)((int)piVar3 + field_0x94);
    }
    field_0xa8 = (int)piVar3 - field_0x94;
    field_0xa4 = (TGlyphCacheInfo*)mCacheBuffer;
    field_0x9c = NULL;
    field_0xa0 = NULL;
}

/* 802C126C-802C12B0       .text unlink__12JUTCacheFontFPQ212JUTCacheFont15TGlyphCacheInfo */
void JUTCacheFont::unlink(JUTCacheFont::TGlyphCacheInfo* cacheInfo) {
    if (cacheInfo->mPrev == NULL) {
        field_0x9c = cacheInfo->mNext;
    } else {
        cacheInfo->mPrev->mNext = cacheInfo->mNext;
    }

    if (cacheInfo->mNext == NULL) {
        field_0xa0 = cacheInfo->mPrev;
    } else {
        cacheInfo->mNext->mPrev = cacheInfo->mPrev;
    }
}

/* 802C12B0-802C12DC       .text prepend__12JUTCacheFontFPQ212JUTCacheFont15TGlyphCacheInfo */
void JUTCacheFont::prepend(JUTCacheFont::TGlyphCacheInfo* cacheInfo) {
    TGlyphCacheInfo* oldHead = field_0x9c;
    field_0x9c = cacheInfo;
    cacheInfo->mPrev = NULL;
    cacheInfo->mNext = oldHead;

    if (oldHead == NULL) {
        field_0xa0 = cacheInfo;
    } else {
        oldHead->mPrev = cacheInfo;
    }
}
