#include "global.h"
#include "z64quest_hint_commands.h"

QuestHintCmd sChildSariaQuestHints[] = {
    QUEST_HINT_STRENGTH_UPG(SKIP, 0, false, 3),
    QUEST_HINT_FLAG(CHECK, EVENTCHKINF_37, false, 0x61),
    QUEST_HINT_END(0x64),

    QUEST_HINT_FLAG(CHECK, EVENTCHKINF_25, false, 0x62),
    QUEST_HINT_FLAG(CHECK, EVENTCHKINF_37, false, 0x63),
    QUEST_HINT_FLAG(CHECK, EVENTCHKINF_43, false, 0x65),
    QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_FOREST, false, 0x66),
    QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_FIRE, false, 0x66),
    QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_WATER, false, 0x66),
    QUEST_HINT_SONG(CHECK, ITEM_SONG_STORMS, false, 0x67),
    QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_SPIRIT, false, 0x68),
    QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_SHADOW, false, 0x68),
    QUEST_HINT_END(0x69),
};

QuestHintCmd sAdultSariaQuestHints[] = {
    QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_FOREST, false, 0x6A),
    QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_FIRE, false, 0x6B),
    QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_WATER, false, 0x6B),
    QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_SPIRIT, false, 0x6C),
    QUEST_HINT_MEDALLION(CHECK, ITEM_MEDALLION_SHADOW, false, 0x6C),
    QUEST_HINT_END(0x6D),
};

u32 QuestHint_CheckCondition(QuestHintCmd* hintCmd) {
    s32 type = hintCmd->byte0 & 0x1E;
    u16 flag;

    switch (type) {
        case (QUEST_HINT_CONDITION_FLAG << 1):
            flag = 1 << (hintCmd->byte1 & 0x0F);
            return ((hintCmd->byte0 & 1) == 1) ==
                   ((flag & gSaveContext.save.info.eventChkInf[(hintCmd->byte1 & 0xF0) >> 4]) != 0);

        case (QUEST_HINT_CONDITION_DUNGEON_ITEM << 1):
            return ((hintCmd->byte0 & 1) == 1) ==
                   (CHECK_DUNGEON_ITEM(hintCmd->byte1 - ITEM_DUNGEON_BOSS_KEY, gSaveContext.mapIndex) != 0);

        case (QUEST_HINT_CONDITION_ITEM << 1):
            return ((hintCmd->byte0 & 1) == 1) == (hintCmd->byte3 == INV_CONTENT(hintCmd->byte1));

        case (QUEST_HINT_CONDITION_OTHER << 1):
            switch (hintCmd->byte1 & 0xF0) {
                case (QUEST_HINT_CONDITION_STRENGTH_UPG << 4):
                    return ((hintCmd->byte0 & 1) == 1) == ((hintCmd->byte1 & 0x0F) == CUR_UPG_VALUE(UPG_STRENGTH));

                case (QUEST_HINT_CONDITION_BOOTS << 4):
                    return ((hintCmd->byte0 & 1) == 1) ==
                           (CHECK_OWNED_EQUIP(EQUIP_TYPE_BOOTS,
                                              hintCmd->byte3 - ITEM_BOOTS_KOKIRI + EQUIP_INV_BOOTS_KOKIRI) != 0);

                case (QUEST_HINT_CONDITION_SONG << 4):
                    return ((hintCmd->byte0 & 1) == 1) ==
                           (CHECK_QUEST_ITEM(hintCmd->byte3 - ITEM_SONG_MINUET + QUEST_SONG_MINUET) != 0);

                case (QUEST_HINT_CONDITION_MEDALLION << 4):
                    return ((hintCmd->byte0 & 1) == 1) ==
                           (CHECK_QUEST_ITEM(hintCmd->byte3 - ITEM_MEDALLION_FOREST + QUEST_MEDALLION_FOREST) != 0);

                case (QUEST_HINT_CONDITION_MAGIC << 4):
                    return ((hintCmd->byte0 & 1) == 1) ==
                           (((void)0, gSaveContext.save.info.playerData.isMagicAcquired) != 0);
            }
    }

    LOG_STRING("企画外 条件", "../z_elf_message.c", 156); // "Unplanned conditions"
    ASSERT(0, "0", "../z_elf_message.c", 157);

    return false;
}

u32 QuestHint_CheckConditionChain(QuestHintCmd** hintCmdPtr) {
    u32 allConditionsMet = true;

    while (((*hintCmdPtr)->byte0 & 0xE0) == (QUEST_HINT_TYPE_CHAIN << 5)) {
        // if any of the conditions checked in the chain are not met,
        // the whole chain is considered false
        if (!QuestHint_CheckCondition(*hintCmdPtr)) {
            allConditionsMet = false;
        }

        *hintCmdPtr += 1;
    }

    if (allConditionsMet) {
        return QuestHint_CheckCondition(*hintCmdPtr);
    } else {
        return false;
    }
}

u32 QuestHint_CheckRandomCondition(QuestHintCmd** hintCmdPtr) {
    QuestHintCmd* hintCmd = *hintCmdPtr;
    u32 conditions[10];
    s32 i = 0;
    s32 totalChecked = 0;
    s32 rand;

    do {
        conditions[totalChecked] = QuestHint_CheckCondition(hintCmd);
        // i is incremented if the condition was met
        i += conditions[totalChecked];
        totalChecked++;
        hintCmd++;
    } while ((hintCmd->byte0 & 0xE0) == (QUEST_HINT_TYPE_RANDOM << 5));

    // if none of the conditions checked were satisfied, there are none to use
    if (i == 0) {
        return false;
    }

    // choose a random number between 0 and the total amount of conditions met
    rand = Rand_ZeroFloat(i);

    for (i = 0; i < totalChecked; i++) {
        // keep decrementing the random number until it reaches 0 then use that hint
        if (conditions[i]) {
            if (rand > 0) {
                rand--;
            } else {
                return true;
            }
        }

        *hintCmdPtr += 1;
    }

    return false;
}

u16 QuestHint_GetTextIdFromScript(QuestHintCmd* hintCmd) {
    while (true) {
        switch (hintCmd->byte0 & 0xE0) {
            case (QUEST_HINT_TYPE_CHECK << 5):
                if (QuestHint_CheckCondition(hintCmd)) {
                    return hintCmd->byte2 | 0x100;
                }
                break;

            case (QUEST_HINT_TYPE_CHAIN << 5):
                if (QuestHint_CheckConditionChain(&hintCmd)) {
                    return hintCmd->byte2 | 0x100;
                }
                break;

            case (QUEST_HINT_TYPE_RANDOM << 5):
                if (QuestHint_CheckRandomCondition(&hintCmd)) {
                    return hintCmd->byte2 | 0x100;
                }
                break;

            case (QUEST_HINT_TYPE_SKIP << 5):
                if (QuestHint_CheckCondition(hintCmd)) {
                    hintCmd += hintCmd->byte2; // skip the specified amount
                    hintCmd--;                 // decrement by 1 because it will be incremented again below
                }
                break;

            case (QUEST_HINT_TYPE_END << 5):
                return hintCmd->byte2 | 0x100;

            default:
                LOG_STRING("企画外 条件", "../z_elf_message.c", 281); // "Unplanned conditions"
                ASSERT(0, "0", "../z_elf_message.c", 282);
        }

        hintCmd++;
    }
}

u16 QuestHint_GetSariaTextId(PlayState* play) {
    Player* player = GET_PLAYER(play);
    QuestHintCmd* sariaQuestHints;

    if (!LINK_IS_ADULT) {
        if (Actor_FindNearby(play, &player->actor, ACTOR_EN_SA, 4, 800.0f) == NULL) {
            sariaQuestHints = sChildSariaQuestHints;
        } else {
            return 0x0160; // Special text about Saria preferring to talk to you face-to-face
        }
    } else {
        sariaQuestHints = sAdultSariaQuestHints;
    }

    return QuestHint_GetTextIdFromScript(sariaQuestHints);
}

u16 QuestHint_GetNaviTextId(PlayState* play) {
    if (play->naviQuestHints == NULL) {
        return 0;
    } else {
        return QuestHint_GetTextIdFromScript(play->naviQuestHints);
    }
}
