#include "toBeSorted/dowsing_target.h"

#include "c/c_math.h"
#include "d/flag/itemflag_manager.h"
#include "d/flag/storyflag_manager.h"
#include "d/lyt/d_lyt_common_icon_item_maps.h"
#include "m/m_mtx.h"
#include "toBeSorted/tlist.h"

typedef TList<DowsingTarget, offsetof(DowsingTarget, mLink)> DowsingList;

DowsingList DOWSING_LISTS[8];

static const int DOWSING_TARGET_STORY_FLAGS[8] = {
    STORYFLAG_ZELDA_DOWSING, STORYFLAG_RUPEE_DOWSING,        STORYFLAG_ZELDA_DOWSING, STORYFLAG_CRYSTAL_DOWSING,
    STORYFLAG_HEART_DOWSING, STORYFLAG_GODDESS_CUBE_DOWSING, STORYFLAG_109,           STORYFLAG_TREASURE_DOWSING,
};

DowsingTarget::~DowsingTarget() {
    doUnregister();
}

void DowsingTarget::initialize(DowsingSlot type, u16 unk, const mVec3_c *offset, f32 unk2) {
    reinitialize(type);
    field_0x0E = unk;
    if (offset != nullptr) {
        mOffset = *offset;
    }
}

static bool insertDowsingTarget(DowsingTarget *target);
static bool removeDowsingTarget(DowsingTarget *target);

bool DowsingTarget::doRegister() {
    return insertDowsingTarget(this);
}

bool DowsingTarget::doUnregister() {
    return removeDowsingTarget(this);
}

void DowsingTarget::reinitialize(DowsingSlot type) {
    bool res = doUnregister();
    mDowsingSlot = type;
    if (res) {
        doRegister();
    }
}

void DowsingTarget::getPosition(mVec3_c &position) {
    mMtx_c mtx;
    mMtx_c mtx2;
    MTXTrans(mtx.m, mpActor->mPosition.x, mpActor->mPosition.y, mpActor->mPosition.z);
    mtx.YrotM(mpActor->mRotation.y);
    MTXScale(mtx2.m, mpActor->mScale.x, mpActor->mScale.y, mpActor->mScale.z);
    MTXConcat(mtx.m, mtx2.m, mtx.m);
    MTXMultVec(mtx.m, mOffset, position);
}

bool DowsingTarget::hasZeldaDowsing() {
    return StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_ZELDA_DOWSING);
}

bool DowsingTarget::hasKikwiDowsing() {
    return StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_KIKWI_DOWSING) &&
           !StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_KIKWI_FIND_FINISHED);
}

bool DowsingTarget::hasKeyPieceDowsing() {
    return StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_KEYPIECE_DOWSING) &&
           !StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_KEYPIECE_COMPLETE);
}

bool DowsingTarget::hasDesertNodeDowsing() {
    return StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_GENERATOR_DOWSING) &&
           !StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_LMF_RAISED);
}

inline bool checkTrial(u16 itemflag, u16 storyflag) {
    return (ItemflagManager::sInstance->getFlagDirect(itemflag) &&
            !StoryflagManager::sInstance->getCounterOrFlag(storyflag)) ?
               true :
               false;
}

bool DowsingTarget::hasAnyTrialDowsing() {
    return checkTrial(ITEM_FARORES_COURAGE, STORYFLAG_FARON_TRIAL_UNLOCKED) ||
           checkTrial(ITEM_NAYRUS_WISDOM, STORYFLAG_LANAYRU_TRIAL_UNLOCKED) ||
           checkTrial(ITEM_DINS_POWER, STORYFLAG_ELDIN_TRIAL_UNLOCKED) ||
           checkTrial(ITEM_SOTH, STORYFLAG_HYLIA_TRIAL_UNLOCKED);
}

bool DowsingTarget::hasSacredWaterDowsing() {
    return StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_SACRED_WATER_DOWSING) &&
           !StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_WATER_DRAGON_HEALED);
}

bool DowsingTarget::hasSandshipDowsing() {
    return StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_SANDSHIP_DOWSING) &&
           !StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_SANDSHIP_ENTERED);
}

bool DowsingTarget::hasTadtoneDowsing() {
    return StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_TADTONE_DOWSING) &&
           !StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_ALL_TADTONES_GATHERED);
}

bool DowsingTarget::hasPropellerDowsing() {
    return StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_PROPELLER_DOWSING);
}

bool DowsingTarget::hasWaterBasinDowsing() {
    return StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_WATER_BASIN_DOWSING);
}

bool DowsingTarget::hasCrystalBallDowsing() {
    return StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_CRYSTAL_BALL_DOWSING);
}

bool DowsingTarget::hasPumpkinDowsing() {
    return StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_PUMPKIN_DOWSING);
}

bool DowsingTarget::hasNewPlantSpeciesDowsing() {
    return StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_KIKWI_SPECIES_DOWSING);
}

bool DowsingTarget::hasPartyWheelDowsing() {
    return StoryflagManager::sInstance->getCounterOrFlag(STORYFLAG_PARTY_WHEEL_DOWSING);
}

bool DowsingTarget::hasDowsingInSlot(int slot) {
    if (slot >= 8) {
        return false;
    }

    if (slot == 6) {
        return true;
    }

    if (slot == 0) {
        return hasZeldaDowsing() || hasAnyTrialDowsing() || hasSacredWaterDowsing() || hasSandshipDowsing() ||
               hasTadtoneDowsing() || hasPropellerDowsing() || hasWaterBasinDowsing();
    } else if (slot == 2) {
        return hasCrystalBallDowsing() || hasPumpkinDowsing() || hasNewPlantSpeciesDowsing() || hasKikwiDowsing() ||
               hasKeyPieceDowsing() || hasDesertNodeDowsing() || hasPartyWheelDowsing();
    } else {
        u16 flag = DOWSING_TARGET_STORY_FLAGS[slot];
        flag = StoryflagManager::sInstance->getCounterOrFlag(flag);
        if (flag != 0) {
            return true;
        }
    }

    return false;
}

DowsingTarget *DowsingTarget::getDowsingInfo(
    const mVec3_c &playerPosition, const mVec3_c &dowsingDirection, f32 *p3, f32 *p4, f32 *intensity, int slot
) {
    if (slot >= 8) {
        return nullptr;
    }
    mVec3_c dwsDir = dowsingDirection;
    dwsDir.normalize();
    DowsingList *list = &DOWSING_LISTS[slot];
    DowsingTarget *best = nullptr;

    for (DowsingList::Iterator node = list->GetBeginIter(); node != list->GetEndIter(); ++node) {
        mVec3_c targetPos;
        node->getPosition(targetPos);
        mVec3_c targetDir = mVec3_c(targetPos - playerPosition);
        f32 proximity = nw4r::ut::Max(10000.0f - targetDir.mag(), 0.0f);

        targetDir.normalize();

        // Same code as in d_sword_swing_effect_mgr...
        f32 dot = dwsDir.dot(targetDir);
        dot = cM::minMaxLimit(dot, -1.0f, 1.0f);
        f32 a = 1.0f - dot * dot <= 0.0f ? 0.0f : nw4r::math::FrSqrt(1.0f - dot * dot) * (1.0f - dot * dot);

        f32 f9 = (5461 - labs(cM::atan2s(a, dot))) * (1.0f / 5461.0f);
        if (!(f9 < 0.0f)) {
            f32 val;
            f32 f10 = cM::minMaxLimit(proximity * 0.0001f, 0.0f, 0.9f) * 1.1111112f;
            if (f9 > 0.85f) {
                f9 = 0.85f;
            }

            f9 = f9 * 1.1764705f;
            f9 *= f9;
            if (f9 > 1.0f) {
                f9 = 1.0f;
            }

            val = f9 * f10;
            if (val > *intensity) {
                *intensity = val;
                *p3 = f10;
                *p4 = f9;
                best = &*node;
            }
        }
    }
    return best;
}

void DowsingTarget::init() {}

void DowsingTarget::execute() {}

static bool insertDowsingTarget(DowsingTarget *target) {
    u8 slot = target->getSlot();
    if (slot == DowsingTarget::SLOT_NONE) {
        return false;
    }

    if (DOWSING_LISTS[slot].GetPosition(target) != DOWSING_LISTS[slot].GetEndIter()) {
        return false;
    }
    DOWSING_LISTS[slot].append(target);
    return true;
}

static bool removeDowsingTarget(DowsingTarget *target) {
    u8 slot = target->getSlot();
    if (slot == DowsingTarget::SLOT_NONE) {
        return false;
    }

    if (DOWSING_LISTS[slot].GetPosition(target) != DOWSING_LISTS[slot].GetEndIter()) {
        DOWSING_LISTS[slot].remove(target);
        return true;
    }
    return false;
}

s32 DowsingTarget::getTopDowsingIcon() {
    if (hasZeldaDowsing()) {
        return LYT_CMN_DowsingZelda;
    } else if (hasAnyTrialDowsing()) {
        return LYT_CMN_DowsingTrialGate;
    } else if (hasSacredWaterDowsing()) {
        return LYT_CMN_DowsingHolyWater;
    } else if (hasSandshipDowsing()) {
        return LYT_CMN_DowsingSandship;
    } else if (hasTadtoneDowsing()) {
        return LYT_CMN_DowsingTadtones;
    } else if (hasPropellerDowsing()) {
        return LYT_CMN_DowsingWindmillPropeller;
    } else if (hasWaterBasinDowsing()) {
        return LYT_CMN_DowsingWaterBasin;
    }
    return LYT_CMN_DowsingZelda;
}

s32 DowsingTarget::getLeftDowsingIcon() {
    if (hasCrystalBallDowsing()) {
        return LYT_CMN_DowsingCrystalBall;
    } else if (hasPumpkinDowsing()) {
        return LYT_CMN_DowsingPumpkinPatchPlower;
    } else if (hasNewPlantSpeciesDowsing()) {
        return LYT_CMN_DowsingNewPlantSpecies;
    } else if (hasPartyWheelDowsing()) {
        return LYT_CMN_DowsingPartyWheel;
    } else if (hasKikwiDowsing()) {
        return LYT_CMN_DowsingKikwi;
    } else if (hasKeyPieceDowsing()) {
        return LYT_CMN_DowsingEarthTempleKey;
    } else if (hasDesertNodeDowsing()) {
        return LYT_CMN_DowsingPowerGenerators;
    }
    return LYT_CMN_DowsingCrystalBall;
}
