#include "global.h"

void ViMode_LogPrint(OSViMode* osViMode) {
    LOG_ADDRESS("osvimodep", osViMode);
    LOG_ADDRESS("osvimodep->comRegs.ctrl", osViMode->comRegs.ctrl);
    LOG_ADDRESS("osvimodep->comRegs.width", osViMode->comRegs.width);
    LOG_ADDRESS("osvimodep->comRegs.burst", osViMode->comRegs.burst);
    LOG_ADDRESS("osvimodep->comRegs.vSync", osViMode->comRegs.vSync);
    LOG_ADDRESS("osvimodep->comRegs.hSync", osViMode->comRegs.hSync);
    LOG_ADDRESS("osvimodep->comRegs.leap", osViMode->comRegs.leap);
    LOG_ADDRESS("osvimodep->comRegs.hStart", osViMode->comRegs.hStart);
    LOG_ADDRESS("osvimodep->comRegs.xScale", osViMode->comRegs.xScale);
    LOG_ADDRESS("osvimodep->fldRegs[0].vStart", osViMode->fldRegs[0].vStart);
    LOG_ADDRESS("osvimodep->fldRegs[0].vBurst", osViMode->fldRegs[0].vBurst);
    LOG_ADDRESS("osvimodep->fldRegs[0].origin", osViMode->fldRegs[0].origin);
    LOG_ADDRESS("osvimodep->fldRegs[0].yScale", osViMode->fldRegs[0].yScale);
    LOG_ADDRESS("osvimodep->fldRegs[0].vIntr", osViMode->fldRegs[0].vIntr);
    LOG_ADDRESS("osvimodep->fldRegs[1].vStart", osViMode->fldRegs[1].vStart);
    LOG_ADDRESS("osvimodep->fldRegs[1].vBurst", osViMode->fldRegs[1].vBurst);
    LOG_ADDRESS("osvimodep->fldRegs[1].origin", osViMode->fldRegs[1].origin);
    LOG_ADDRESS("osvimodep->fldRegs[1].yScale", osViMode->fldRegs[1].yScale);
    LOG_ADDRESS("osvimodep->fldRegs[1].vIntr", osViMode->fldRegs[1].vIntr);
}

// This function configures the custom VI mode (`viMode.customViMode`) based on the other flags in `viMode`.
void ViMode_Configure(ViMode* viMode, s32 mode, s32 type, s32 unk_70, s32 unk_74, s32 unk_78, s32 unk_7C, s32 width,
                      s32 height, s32 unk_left, s32 unk_right, s32 unk_top, s32 unk_bottom) {
    s32 not_70;
    s32 not_74;
    s32 not_78;
    s32 not_7C;
    s32 cond_4C;
    s32 cond_48;
    s32 cond_44;
    s32 cond_40;
    s32 cond_3C;
    s32 cond_38;
    s32 cond_34;
    s32 yScaleLo;
    s32 yScaleHi0;
    s32 yScaleHi1;

    not_70 = !unk_70;
    not_74 = !unk_74;
    not_78 = !unk_78;
    not_7C = !unk_7C;

    cond_4C = not_70 && not_78;
    cond_48 = not_70 && unk_78;
    cond_44 = unk_70 && unk_78;
    cond_40 = unk_70 && not_78;
    cond_3C = unk_70 && not_74 && unk_78 && unk_7C;
    cond_38 = unk_70 && unk_74 && unk_78 && not_7C;
    cond_34 = not_70 && unk_74 && unk_78 && not_7C;

    unk_top &= ~1;
    unk_bottom &= ~1;

    yScaleLo = (cond_4C ? 2 : 1) * (((height << 11)) / (SCREEN_HEIGHT * 2 + unk_bottom - unk_top) / (unk_70 ? 1 : 2));

    yScaleHi0 = not_78 ? (cond_40 ? 0x1000000 : 0x2000000) : 0;
    yScaleHi1 = not_78 ? (cond_40 ? 0x3000000 : 0x2000000) : 0;

    viMode->customViMode.type = mode;
    viMode->customViMode.comRegs.ctrl = OS_VI_UNK2000 | OS_VI_UNK1000 | OS_VI_GAMMA | OS_VI_GAMMA_DITHER |
                                        (!cond_44 ? OS_VI_UNK40 : 0) | (not_74 ? OS_VI_DIVOT : 0) |
                                        (not_7C ? OS_VI_UNK2 | OS_VI_UNK1 : OS_VI_UNK2);

    if (cond_3C) {
        viMode->customViMode.comRegs.ctrl |= 0x100;
    } else if (cond_38 | cond_34) {
        viMode->customViMode.comRegs.ctrl |= 0x300;
    } else if (unk_74) {
        viMode->customViMode.comRegs.ctrl |= 0x200;
    } else {
        viMode->customViMode.comRegs.ctrl |= 0;
    }

    viMode->customViMode.comRegs.width = width * (cond_48 ? 2 : 1);

    if (type == 1) {
        viMode->customViMode.comRegs.burst = 0x3E52239;
        viMode->customViMode.comRegs.vSync = 0x20C;
        viMode->customViMode.comRegs.hSync = 0xC15;
        viMode->customViMode.comRegs.leap = 0xC150C15;
        viMode->customViMode.comRegs.hStart = 0x6C02EC;
        viMode->customViMode.fldRegs[0].vStart = 0x2501FF;
        viMode->customViMode.fldRegs[0].vBurst = 0xE0204;
    } else if (type == 0) {
        viMode->customViMode.comRegs.burst = 0x404233A;
        viMode->customViMode.comRegs.vSync = 0x270;
        viMode->customViMode.comRegs.hSync = 0x150C69;
        viMode->customViMode.comRegs.leap = 0xC6F0C6E;
        viMode->customViMode.comRegs.hStart = 0x800300;
        viMode->customViMode.fldRegs[0].vStart = 0x5F0239;
        viMode->customViMode.fldRegs[0].vBurst = 0x9026B;
    } else if (type == 2) {
        viMode->customViMode.comRegs.burst = 0x4651E39;
        viMode->customViMode.comRegs.vSync = 0x20C;
        viMode->customViMode.comRegs.hSync = 0xC10;
        viMode->customViMode.comRegs.leap = 0xC1C0C1C;
        viMode->customViMode.comRegs.hStart = 0x6C02EC;
        viMode->customViMode.fldRegs[0].vStart = 0x2501FF;
        viMode->customViMode.fldRegs[0].vBurst = 0xE0204;
    }

    viMode->customViMode.fldRegs[1].vStart = viMode->customViMode.fldRegs[0].vStart;

    viMode->customViMode.comRegs.hStart += (unk_left << 16) + (s16)unk_right;
    viMode->customViMode.fldRegs[0].vStart += (unk_top << 16) + (s16)unk_bottom;
    viMode->customViMode.fldRegs[1].vStart += (unk_top << 16) + (s16)unk_bottom;

    viMode->customViMode.fldRegs[1].vBurst = viMode->customViMode.fldRegs[0].vBurst;

    if (cond_44) {
        viMode->customViMode.comRegs.vSync++;
        if (type == 2) {
            viMode->customViMode.comRegs.hSync += 0x40001;
        }
        if (type == 2) {
            viMode->customViMode.comRegs.leap += 0xFFFCFFFE;
        }
    } else {
        viMode->customViMode.fldRegs[0].vStart += 0xFFFDFFFE;
        if (type == 2) {
            viMode->customViMode.fldRegs[0].vBurst += 0xFFFCFFFE;
        }
        if (type == 0) {
            viMode->customViMode.fldRegs[1].vBurst += 0x2FFFE;
        }
    }

    viMode->customViMode.comRegs.xScale = (width << 10) / (SCREEN_WIDTH * 2 + unk_right - unk_left);
    viMode->customViMode.comRegs.vCurrent = 0;

    viMode->customViMode.fldRegs[0].origin = width * 2 * (unk_7C ? 1 : 2);
    viMode->customViMode.fldRegs[1].origin = width * 2 * (unk_7C ? 1 : 2) * (unk_70 ? 1 : 2);

    viMode->customViMode.fldRegs[0].yScale = yScaleLo | yScaleHi0;
    viMode->customViMode.fldRegs[1].yScale = yScaleLo | yScaleHi1;

    viMode->customViMode.fldRegs[0].vIntr = 2;
    viMode->customViMode.fldRegs[1].vIntr = 2;
}

void ViMode_Save(ViMode* viMode) {
    SREG(48) = viMode->viModeBase;
    SREG(49) = viMode->viWidth;
    SREG(50) = viMode->viHeight;
    SREG(51) = viMode->unk_64;
    SREG(52) = viMode->unk_60;
    SREG(53) = viMode->unk_5C;
    SREG(54) = viMode->unk_58;
    if (SREG(58) == 1) {
        SREG(58) = 0;
        switch (SREG(59)) {
            case 1:
                osSyncPrintf("osViModePalLan1\n");
                ViMode_LogPrint(&osViModePalLan1);
                break;
            case 2:
                osSyncPrintf("osViModeFpalLan1\n");
                ViMode_LogPrint(&osViModeFpalLan1);
                break;
            default:
                osSyncPrintf("Custom\n");
                ViMode_LogPrint(&viMode->customViMode);
                break;
        }
    }
}

void ViMode_Load(ViMode* viMode) {
    if ((SREG(49) & ~3) == 1) {
        SREG(49) += 4;
    }

    viMode->viModeBase = SREG(48);
    viMode->viWidth = SREG(49) & ~3;
    viMode->viHeight = SREG(50);
    viMode->unk_64 = SREG(51);
    viMode->unk_60 = SREG(52);
    viMode->unk_5C = SREG(53);
    viMode->unk_58 = SREG(54);
}

void ViMode_Init(ViMode* viMode) {
    viMode->viModeBase = 0;
    viMode->viWidth = SCREEN_WIDTH;
    viMode->viHeight = SCREEN_HEIGHT;
    viMode->unk_5C = 0;
    viMode->unk_58 = 0;
    viMode->unk_64 = 0;
    viMode->unk_60 = 0;
    viMode->viFeatures = OS_VI_DITHER_FILTER_ON | OS_VI_GAMMA_OFF;
    viMode->viTvType = osTvType;
    viMode->unk_7C = true;
    viMode->unk_78 = true;
    viMode->unk_74 = false;
    viMode->unk_70 = true;

    ViMode_Save(viMode);
}

void ViMode_Destroy(ViMode* viMode) {
}

void ViMode_ConfigureFeatures(ViMode* viMode, s32 viFeatures) {
    u32 ctrl = viMode->customViMode.comRegs.ctrl;

    if (viFeatures & OS_VI_GAMMA_ON) {
        ctrl |= OS_VI_GAMMA;
    }
    if (viFeatures & OS_VI_GAMMA_OFF) {
        ctrl &= ~OS_VI_GAMMA;
    }
    if (viFeatures & OS_VI_GAMMA_DITHER_ON) {
        ctrl |= OS_VI_GAMMA_DITHER;
    }
    if (viFeatures & OS_VI_GAMMA_DITHER_OFF) {
        ctrl &= ~OS_VI_GAMMA_DITHER;
    }
    if (viFeatures & OS_VI_DIVOT_ON) {
        ctrl |= OS_VI_DIVOT;
    }
    if (viFeatures & OS_VI_DIVOT_OFF) {
        ctrl &= ~OS_VI_DIVOT;
    }
    viMode->customViMode.comRegs.ctrl = ctrl;
}

// This function uses controller input (C buttons + D pad) to reconfigure the custom VI mode
void ViMode_Update(ViMode* viMode, Input* input) {
    ViMode_Load(viMode);
    if ((viMode->viModeBase == 1) || (viMode->viModeBase == 2) || (viMode->viModeBase == 3)) {
        gScreenWidth = viMode->viWidth;
        gScreenHeight = viMode->viHeight;
        if (CHECK_BTN_ALL(input->cur.button, BTN_START | BTN_CUP | BTN_CRIGHT)) {
            ViMode_Init(viMode);
        }
        if (CHECK_BTN_ALL(input->cur.button, BTN_CUP)) {
            if (CHECK_BTN_ALL(input->cur.button, BTN_DUP)) {
                viMode->unk_64--;
            }
            if (CHECK_BTN_ALL(input->cur.button, BTN_DDOWN)) {
                viMode->unk_64++;
            }
            if (CHECK_BTN_ALL(input->cur.button, BTN_DLEFT)) {
                viMode->unk_5C--;
            }
            if (CHECK_BTN_ALL(input->cur.button, BTN_DRIGHT)) {
                viMode->unk_5C++;
            }
        }
        if (CHECK_BTN_ALL(input->cur.button, BTN_CRIGHT)) {
            if (CHECK_BTN_ALL(input->cur.button, BTN_DUP)) {
                viMode->unk_60--;
            }
            if (CHECK_BTN_ALL(input->cur.button, BTN_DDOWN)) {
                viMode->unk_60++;
            }
            if (CHECK_BTN_ALL(input->cur.button, BTN_DLEFT)) {
                viMode->unk_58--;
            }
            if (CHECK_BTN_ALL(input->cur.button, BTN_DRIGHT)) {
                viMode->unk_58++;
            }
        }
        if (CHECK_BTN_ALL(input->cur.button, BTN_CDOWN)) {
            if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) {
                viMode->unk_70 = !viMode->unk_70;
            }
            if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) {
                viMode->unk_74 = !viMode->unk_74;
            }
            if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT)) {
                viMode->unk_78 = !viMode->unk_78;
            }
            if (CHECK_BTN_ALL(input->press.button, BTN_DRIGHT)) {
                viMode->unk_7C = !viMode->unk_7C;
            }
        }
        if (viMode->viModeBase >= 2) {
            if (viMode->unk_5C < -16) {
                viMode->unk_5C = -16;
            }
            if (viMode->unk_64 < -50) {
                viMode->unk_64 = -50;
            }
            if (viMode->unk_58 > 16) {
                viMode->unk_58 = 16;
            }
            if (viMode->unk_60 > 50) {
                viMode->unk_60 = 50;
            }
        } else {
            if (viMode->unk_5C < 0) {
                viMode->unk_5C = 0;
            }
            if (viMode->unk_64 < 0) {
                viMode->unk_64 = 0;
            }
            if (viMode->unk_58 > 0) {
                viMode->unk_58 = 0;
            }
            if (viMode->unk_60 > 0) {
                viMode->unk_60 = 0;
            }
        }
        ViMode_Configure(viMode, OS_VI_UNK28, osTvType, viMode->unk_70, viMode->unk_74, viMode->unk_78, viMode->unk_7C,
                         viMode->viWidth, viMode->viHeight, viMode->unk_5C, viMode->unk_58, viMode->unk_64,
                         viMode->unk_60);
        ViMode_ConfigureFeatures(viMode, viMode->viFeatures);
        if (viMode->viModeBase == 3) {
            ViMode_LogPrint(&osViModeNtscLan1);
            ViMode_LogPrint(&viMode->customViMode);
            viMode->viModeBase = 2;
        }
    }
    ViMode_Save(viMode);
}
