#include "egg/gfx/eggDrawPathLightMap.h"

#include "common.h"
#include "egg/gfx/eggDrawGX.h"
#include "egg/gfx/eggGlobalDrawState.h"
#include "egg/gfx/eggPostEffectBase.h"
#include "egg/gfx/eggStateGX.h"
#include "rvl/GX/GXPixel.h"
#include "rvl/GX/GXTypes.h"

namespace EGG {

DrawPathLightMap::DrawPathLightMap()
    : field_0x18(6),
      mAlpha(0xFF),
      field_0x1C(0),
      field_0x20(0),
      field_0x24(0),
      mpTextureBuffer_0x28(nullptr),
      mpTextureBuffer_0x2C(nullptr),
      mpTextureBuffer_0x30(nullptr),
      mOldDrawFlag(0) {}

void DrawPathLightMap::internalResetForDraw() {
    freeTextureBuffers();
    mpTextureBuffer_0x28 = nullptr;
    mpTextureBuffer_0x2C = nullptr;
    mpTextureBuffer_0x30 = nullptr;
}

void DrawPathLightMap::internalDraw(u16 idx) {
    const Screen::DataEfb &efb = GlobalDrawState::getScreen().GetDataEfb();

    switch (idx) {
        case 0: {
            if (field_0x18 & 1) {
                mpTextureBuffer_0x30 = TextureBuffer::alloc(efb.vp.x2, efb.vp.y2, GX_TF_RGBA8);
                mpTextureBuffer_0x30->onCapFlag(1);
                mpTextureBuffer_0x30->onCapFlag(2);
                mpTextureBuffer_0x30->setFilt(GX_NEAR, GX_NEAR);
                mpTextureBuffer_0x30->capture(efb.vp.x1, efb.vp.y1, false, -1);
            }
            mOldDrawFlag = GlobalDrawState::getDrawFlag();
            GlobalDrawState::onDrawFlag(0x1 | 0x2);
        } break;
        case 1: {
            GlobalDrawState::setDrawFlag(mOldDrawFlag);
            GXTexFmt fmt = GX_TF_RGBA8;

            if (field_0x20 == 1) {
                fmt = GX_TF_RGB5A3;
                if (field_0x1C == 1) {
                    fmt = GX_TF_RGB565;
                }
            }

            int capW = efb.vp.x1;
            int capH = efb.vp.y1;

            mpTextureBuffer_0x28 = TextureBuffer::alloc(efb.vp.x2, efb.vp.y2, fmt);

            mpTextureBuffer_0x28->onCapFlag(1);
            mpTextureBuffer_0x28->onCapFlag(2);
            if (!(field_0x18 & 2)) {
                mpTextureBuffer_0x28->onCapFlag(4);
            }
            mpTextureBuffer_0x28->setClearColor(StateGX::getClearEfb());

            mpTextureBuffer_0x28->capture(capW, capH, false, -1);
            mpTextureBuffer_0x28->setFilt(GX_NEAR, GX_NEAR);

            if (mpTextureBuffer_0x30) {
                nw4r::math::MTX34 forDL;

                StateGX::ScopedColor _color(true);
                StateGX::ScopedAlpha _alpha(true);
                StateGX::ScopedDither _dither(false);

                PostEffectBase::setProjection(GetScreen());

                MTXScale(forDL, GetScreen().GetWidth(), GetScreen().GetHeight(), 1.0f);

                mpTextureBuffer_0x30->load(GX_TEXMAP0);
                DrawGX::BeginDrawScreen(true, true, false);

                DrawGX::SetBlendMode(DrawGX::BLEND_14);

                DrawGX::DrawDL16(forDL, DrawGX::WHITE);

                mpTextureBuffer_0x30->free();
                mpTextureBuffer_0x30 = nullptr;
            }

            if (field_0x18 & 2) {
                mpTextureBuffer_0x2C = TextureBuffer::alloc(efb.vp.x2, efb.vp.y2, GX_TF_Z24X8);
                mpTextureBuffer_0x2C->onCapFlag(4);

                if (isFlag0x2()) {
                    mpTextureBuffer_0x2C->onCapFlag(0x10);
                } else {
                    mpTextureBuffer_0x2C->offCapFlag(0x10);
                }

                mpTextureBuffer_0x2C->capture(capW, capH, false, -1);
                mpTextureBuffer_0x2C->setFilt(GX_NEAR, GX_NEAR);
            }
            StateGX::invalidateTexAllGX();
        } break;
        case 2: {
            if (mpTextureBuffer_0x28) {
                if (mAlpha != 0) {
                    mpTextureBuffer_0x28->load(GX_TEXMAP0);

                    nw4r::math::MTX34 forDL;
                    PostEffectBase::setProjection(GetScreen());
                    MTXScale(forDL, GetScreen().GetWidth(), GetScreen().GetHeight(), 1.0f);

                    GXColor tevColor = {0xFF, 0xFF, 0xFF, mAlpha};

                    StateGX::ScopedColor _color(true);
                    StateGX::ScopedAlpha _alpha(false);
                    StateGX::ScopedDither _dither(true);

                    if (mpTextureBuffer_0x2C) {
                        mpTextureBuffer_0x2C->load(GX_TEXMAP1);
                        DrawGX::SetMat_ColorChannel(DrawGX::COLORCHAN_1);
                        DrawGX::SetMat_TexGen(DrawGX::TEXGEN_1);
                        DrawGX::SetMat_Ind();
                        GXSetTevKColor(GX_KCOLOR0, (GXColor){0});

                        GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA);
                        GXSetNumTevStages(2);
                        GXSetTevDirect(GX_TEVSTAGE0);
                        GXSetTevDirect(GX_TEVSTAGE1);
                        GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0);
                        GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0);

                        if (field_0x1C == 0) {
                            GXSetTevKAlphaSel(GX_TEVSTAGE0, GX_TEV_KASEL_K0_A);
                            GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
                            GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_RASC, GX_CC_ZERO);
                            GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
                            GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_TEXA, GX_CA_KONST, GX_CA_RASA, GX_CA_ZERO);
                            GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_COMP_A8_GT, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
                        } else {
                            GXSetTevKAlphaSel(GX_TEVSTAGE0, GX_TEV_KASEL_K0_A);
                            GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
                            GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC);
                            GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
                            GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA);
                            GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
                        }

                        GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0);
                        GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP1, GX_COLOR_NULL);
                        GXSetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_CPREV);
                        GXSetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
                        GXSetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV);
                        GXSetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
                        GXSetZTexture(GZ_ZT_REPLACE, GX_TF_Z24X8, 0);

                        GXSetZMode(true, field_0x1C == 0 ? GX_LEQUAL : GX_LESS, field_0x18 & 0x4 ? true : false);
                        GXSetZCompLoc(0);
                        GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0);
                        DrawGX::SetBlendMode(DrawGX::BLEND_0);
                        DrawGX::SetVtxState(DrawGX::VTX_TYPE_11);
                    } else {
                        DrawGX::BeginDrawScreen(true, true, false);
                        GXSetTevKAlphaSel(GX_TEVSTAGE0, GX_TEV_KASEL_K0_A);
                        GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_TEXA, GX_CA_ZERO, GX_CA_RASA, GX_CA_ZERO);
                        GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_COMP_A8_GT, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
                    }

                    DrawGX::DrawDL16(forDL, tevColor);
                    GXSetZTexture(GX_ZT_DISABLE, GX_TF_Z24X8, 0);
                }
                freeTextureBuffers();
            }
        } break;
    }
}

void DrawPathLightMap::freeTextureBuffers() {
    if (mpTextureBuffer_0x2C) {
        mpTextureBuffer_0x2C->free();
        mpTextureBuffer_0x2C = nullptr;
    }
    if (mpTextureBuffer_0x28) {
        mpTextureBuffer_0x28->free();
        mpTextureBuffer_0x28 = nullptr;
    }
}

} // namespace EGG
