// Activates a latent effect if true otherwise deactivates the latent effect
bool CLatentEffectContainer::ApplyLatentEffect(CLatentEffect& effect, bool expression)
{
    if (expression)
    {
        return effect.Activate();
    }
    else
    {
        return effect.Deactivate();
    }
}
void CLatentEffectContainer::DelLatentEffects(uint8 reqLvl, uint8 slot)
{
	for (int16 i = m_LatentEffectList.size()-1; i >= 0; --i) 
	{
		if (m_LatentEffectList.at(i)->GetSlot() == slot)
		{
			CLatentEffect* latent = m_LatentEffectList.at(i);
			if( latent->IsActivated() )
			{
				latent->Deactivate();
			}
			m_LatentEffectList.erase(m_LatentEffectList.begin() + i);
			delete latent;
		}
	}
}
void CLatentEffectContainer::CheckLatentsPetType(PETTYPE petID)
{
	for (uint16 i = 0; i < m_LatentEffectList.size(); ++i) 
	{
		if( m_LatentEffectList.at(i)->GetConditionsID() == LATENT_PET_ID)
		{
			CLatentEffect* latent = m_LatentEffectList.at(i);
			if( (PETTYPE)latent->GetConditionsValue() == petID )
			{
				latent->Activate();
			}
			else
			{
				latent->Deactivate();
			}
		}
	}
}
void CLatentEffectContainer::CheckLatentsPartyMembers(uint8 members)
{
	for (uint16 i = 0; i < m_LatentEffectList.size(); ++i) 
	{
		if( m_LatentEffectList.at(i)->GetConditionsID() == LATENT_PARTY_MEMBERS)
		{
			CLatentEffect* latent = m_LatentEffectList.at(i);
			if( latent->GetConditionsValue() <= members )
			{
				latent->Activate();
			}
			else
			{
				latent->Deactivate();
			}
		}
	}
}
// Processes a single CLatentEffect* and finds the expression to evaluate for
// activation/deactivation and attempts to apply
bool CLatentEffectContainer::ProcessLatentEffect(CLatentEffect& latentEffect)
{
    // Our default case un-finds our latent prevent us from toggling a latent we don't have programmed
    auto expression = false;
    auto latentFound = true;

    // find the latent type from the enum and find the expression to tests againts
    switch (latentEffect.GetConditionsID())
    {
    case LATENT_HP_UNDER_PERCENT:
        expression = ((float)m_POwner->health.hp / m_POwner->health.maxhp) * 100 <= latentEffect.GetConditionsValue();
        break;
    case LATENT_HP_OVER_PERCENT:
        expression = ((float)m_POwner->health.hp / m_POwner->health.maxhp) * 100 >= latentEffect.GetConditionsValue();
        break;
    case LATENT_HP_UNDER_TP_UNDER_100:
        expression = ((float)m_POwner->health.hp / m_POwner->health.maxhp) * 100 <= latentEffect.GetConditionsValue() && m_POwner->health.tp < 1000;
        break;
    case LATENT_HP_OVER_TP_UNDER_100:
        expression = ((float)m_POwner->health.hp / m_POwner->health.maxhp) * 100 >= latentEffect.GetConditionsValue() && m_POwner->health.tp < 1000;
        break;
    case LATENT_MP_UNDER_PERCENT:
        expression = m_POwner->health.maxmp && (float)(m_POwner->health.mp / m_POwner->health.maxmp) * 100 <= latentEffect.GetConditionsValue();
        break;
    case LATENT_MP_UNDER:
        expression = m_POwner->health.mp <= latentEffect.GetConditionsValue();
        break;
    case LATENT_TP_UNDER:
        expression = m_POwner->health.tp < latentEffect.GetConditionsValue();
        break;
    case LATENT_TP_OVER:
        expression = m_POwner->health.tp > latentEffect.GetConditionsValue();
        break;
    case LATENT_SUBJOB:
        expression = m_POwner->GetSJob() == latentEffect.GetConditionsValue();
        break;
    case LATENT_PET_ID:
        expression = m_POwner->PPet != nullptr && m_POwner->PPet->objtype == TYPE_PET && ((CPetEntity*)m_POwner->PPet)->m_PetID == latentEffect.GetConditionsValue();
        break;
    case LATENT_WEAPON_DRAWN:
        expression = m_POwner->animation == ANIMATION_ATTACK;
        break;
    case LATENT_WEAPON_SHEATHED:
        expression = m_POwner->animation != ANIMATION_ATTACK;
        break;
    case LATENT_STATUS_EFFECT_ACTIVE:
        expression = m_POwner->StatusEffectContainer->HasStatusEffect((EFFECT)latentEffect.GetConditionsValue());
        break;
    case LATENT_NO_FOOD_ACTIVE:
        expression = !m_POwner->StatusEffectContainer->HasStatusEffect(EFFECT_FOOD);
        break;
    case LATENT_PARTY_MEMBERS:
        expression = m_POwner->PParty != nullptr && latentEffect.GetConditionsValue() <= m_POwner->PParty->members.size();
        break;
    case LATENT_PARTY_MEMBERS_IN_ZONE:
    {
        auto inZone = 0;
        if (m_POwner->PParty != nullptr)
        {
            for (auto member : m_POwner->PParty->members)
            {
                if (member->getZone() == m_POwner->getZone())
                {
                    ++inZone;
                }
            }
        }
        expression = latentEffect.GetConditionsValue() <= inZone;
        break;
    }
    case LATENT_AVATAR_IN_PARTY:
        if (m_POwner->PParty != nullptr)
        {
            for (auto member : m_POwner->PParty->members)
            {
                if (member->PPet != nullptr)
                {
                    auto PPet = (CPetEntity*)member->PPet;
                    if (PPet->m_PetID == latentEffect.GetConditionsValue() &&
                        PPet->PAI->IsSpawned())
                    {
                        expression = true;
                        break;
                    }
                }
            }
        }
        else if (m_POwner->PParty == nullptr && m_POwner->PPet != nullptr)
        {
            auto PPet = (CPetEntity*)m_POwner->PPet;
            if (PPet->m_PetID == latentEffect.GetConditionsValue() &&
                !PPet->isDead())
            {
                expression = true;
            }
        }
        break;
    case LATENT_JOB_IN_PARTY:
        if (m_POwner->PParty != nullptr)
        {
            for (auto member : m_POwner->PParty->members)
            {
                if (member->id != m_POwner->id)
                {
                    if (member->GetMJob() == latentEffect.GetConditionsValue())
                    {
                        expression = true;
                        break;
                    }
                }
            }
        }
        break;
    case LATENT_ZONE:
        expression = latentEffect.GetConditionsValue() == m_POwner->getZone();
        break;
    case LATENT_SYNTH_TRAINEE:
        // todo: figure this out
        break;
    case LATENT_SONG_ROLL_ACTIVE:
        expression = m_POwner->StatusEffectContainer->HasStatusEffectByFlag(EFFECTFLAG_ROLL | EFFECTFLAG_SONG);
        break;
    case LATENT_TIME_OF_DAY:
    {
        uint32 VanadielHour = CVanaTime::getInstance()->getHour();
        switch (latentEffect.GetConditionsValue())
        {
        case 0:
            //daytime: 06:00 to 18:00
            expression = VanadielHour >= 6 && VanadielHour < 18;
            break;
        case 1:
            //nighttime: 18:00 to 06:00
            expression = VanadielHour >= 18 || VanadielHour < 6;
            break;
        case 2:
            //dusk - dawn: 17:00 to 7:00
            expression = VanadielHour >= 17 || VanadielHour < 7;
            break;
        }
        break;
    }
    case LATENT_HOUR_OF_DAY:
    {
        uint32 VanadielHour = CVanaTime::getInstance()->getHour();
        switch (latentEffect.GetConditionsValue())
        {
        case 1:
            //new day
            expression = VanadielHour == 4;
            break;
        case 2:
            //dawn
            expression = VanadielHour >= 6 && VanadielHour < 7;
            break;
        case 3:
            //day
            expression = VanadielHour >= 7 && VanadielHour < 17;
            break;
        case 4:
            //dusk
            expression = VanadielHour >= 16 && VanadielHour < 18;
            break;
        case 5:
            //evening
            expression = VanadielHour >= 18 && VanadielHour < 20;
            break;
        case 6:
            //dead of night
            expression = VanadielHour >= 20 || VanadielHour < 4;
            break;
        }
        break;
    }
    case LATENT_FIRESDAY:
        expression = CVanaTime::getInstance()->getWeekday() == FIRESDAY;
        break;
    case LATENT_EARTHSDAY:
        expression = CVanaTime::getInstance()->getWeekday() == EARTHSDAY;
        break;
    case LATENT_WATERSDAY:
        expression = CVanaTime::getInstance()->getWeekday() == WATERSDAY;
        break;
    case LATENT_WINDSDAY:
        expression = CVanaTime::getInstance()->getWeekday() == WINDSDAY;
        break;
    case LATENT_DARKSDAY:
        expression = CVanaTime::getInstance()->getWeekday() == DARKSDAY;
        break;
    case LATENT_ICEDAY:
        expression = CVanaTime::getInstance()->getWeekday() == ICEDAY;
        break;
    case LATENT_LIGHTNINGSDAY:
        expression = CVanaTime::getInstance()->getWeekday() == LIGHTNINGDAY;
        break;
    case LATENT_LIGHTSDAY:
        expression = CVanaTime::getInstance()->getWeekday() == LIGHTSDAY;
        break;
    case LATENT_MOON_PHASE:
    {
        uint32 MoonPhase = CVanaTime::getInstance()->getMoonPhase();
        uint32 MoonDirection = CVanaTime::getInstance()->getMoonDirection(); //directions: 1 = waning, 2 = waxing, 0 = neither
        switch (latentEffect.GetConditionsValue())
        {
        case 0:
            //New Moon - 10% waning -> 5% waxing
            expression = MoonPhase <= 5 || (MoonPhase <= 10 && MoonDirection == 1);
            break;
        case 1:
            //Waxing Crescent - 7% -> 38% waxing
            expression = MoonPhase >= 7 && MoonPhase <= 38 && MoonDirection == 2;
            break;
        case 2:
            //First Quarter - 40%% -> 55% waxing
            expression = MoonPhase >= 40 && MoonPhase <= 55 && MoonDirection == 2;
            break;
        case 3:
            //Waxing Gibbous - 57% -> 88%
            expression = MoonPhase >= 57 && MoonPhase <= 88 && MoonDirection == 2;
            break;
        case 4:
            //Full Moon - waxing 90% -> waning 95%
            expression = MoonPhase >= 95 || (MoonPhase >= 90 && MoonDirection == 2);
            break;
        case 5:
            //Waning Gibbous - 93% -> 62%
            expression = MoonPhase >= 62 && MoonPhase <= 93 && MoonDirection == 1;
            break;
        case 6:
            //Last Quarter - 60% -> 45%
            expression = MoonPhase >= 45 && MoonPhase <= 60 && MoonDirection == 1;
            break;
        case 7:
            //Waning Crescent - 43% -> 12%
            expression = MoonPhase >= 12 && MoonPhase <= 43 && MoonDirection == 1;
            break;
        }
        break;
    }
    case LATENT_JOB_MULTIPLE_5:
        expression = m_POwner->GetMLevel() % 5 == 0;
        break;
    case LATENT_JOB_MULTIPLE_10:
        expression = m_POwner->GetMLevel() % 10 == 0;
        break;
    case LATENT_JOB_MULTIPLE_13_NIGHT:
        expression = m_POwner->GetMLevel() % 13 == 0 && CVanaTime::getInstance()->SyncTime() == TIME_NIGHT;
        break;
    case LATENT_JOB_LEVEL_ODD:
        expression = m_POwner->GetMLevel() % 2 == 1;
        break;
    case LATENT_JOB_LEVEL_EVEN:
        expression = m_POwner->GetMLevel() % 2 == 0;
        break;
    case LATENT_WEAPON_DRAWN_HP_UNDER:
        expression = m_POwner->health.hp < latentEffect.GetConditionsValue() && m_POwner->animation == ANIMATION_ATTACK;
        break;
    case LATENT_MP_UNDER_VISIBLE_GEAR:
        //TODO: figure out if this is actually right
        //CItemArmor* head = (CItemArmor*)(m_POwner->getStorage(LOC_INVENTORY)->GetItem(m_POwner->equip[SLOT_HEAD]));
        //CItemArmor* body = (CItemArmor*)(m_POwner->getStorage(LOC_INVENTORY)->GetItem(m_POwner->equip[SLOT_BODY]));
        //CItemArmor* hands = (CItemArmor*)(m_POwner->getStorage(LOC_INVENTORY)->GetItem(m_POwner->equip[SLOT_HANDS]));
        //CItemArmor* legs = (CItemArmor*)(m_POwner->getStorage(LOC_INVENTORY)->GetItem(m_POwner->equip[SLOT_LEGS]));
        //CItemArmor* feet = (CItemArmor*)(m_POwner->getStorage(LOC_INVENTORY)->GetItem(m_POwner->equip[SLOT_FEET]));

        //int32 visibleMp = 0;
        //visibleMp += (head ? head->getModifier(Mod::MP) : 0);
        //visibleMp += (body ? body->getModifier(Mod::MP) : 0);
        //visibleMp += (hands ? hands->getModifier(Mod::MP) : 0);
        //visibleMp += (legs ? legs->getModifier(Mod::MP) : 0);
        //visibleMp += (feet ? feet->getModifier(Mod::MP) : 0);

        //TODO: add mp percent too
        //if ((float)( mp / ((m_POwner->health.mp - m_POwner->health.modmp) + (m_POwner->PMeritPoints->GetMerit(MERIT_MAX_MP)->count * 10 ) + 
        //    visibleMp) ) <= m_LatentEffectList.at(i)->GetConditionsValue())
        //{
        //    m_LatentEffectList.at(i)->Activate();
        //}
        //else
        //{
        //    m_LatentEffectList.at(i)->Deactivate();
        //}
        break;
    case LATENT_HP_OVER_VISIBLE_GEAR:
        //TODO: figure out if this is actually right
        //CItemArmor* head = (CItemArmor*)(m_POwner->getStorage(LOC_INVENTORY)->GetItem(m_POwner->equip[SLOT_HEAD]));
        //CItemArmor* body = (CItemArmor*)(m_POwner->getStorage(LOC_INVENTORY)->GetItem(m_POwner->equip[SLOT_BODY]));
        //CItemArmor* hands = (CItemArmor*)(m_POwner->getStorage(LOC_INVENTORY)->GetItem(m_POwner->equip[SLOT_HANDS]));
        //CItemArmor* legs = (CItemArmor*)(m_POwner->getStorage(LOC_INVENTORY)->GetItem(m_POwner->equip[SLOT_LEGS]));
        //CItemArmor* feet = (CItemArmor*)(m_POwner->getStorage(LOC_INVENTORY)->GetItem(m_POwner->equip[SLOT_FEET]));

        //int32 visibleHp = 0;
        //visibleHp += (head ? head->getModifier(Mod::HP) : 0);
        //visibleHp += (body ? body->getModifier(Mod::HP) : 0);
        //visibleHp += (hands ? hands->getModifier(Mod::HP) : 0);
        //visibleHp += (legs ? legs->getModifier(Mod::HP) : 0);
        //visibleHp += (feet ? feet->getModifier(Mod::HP) : 0);

        //TODO: add mp percent too
        //if ((float)( hp / ((m_POwner->health.hp - m_POwner->health.modhp) + (m_POwner->PMeritPoints->GetMerit(MERIT_MAX_HP)->count * 10 ) + 
        //    visibleHp) ) <= m_LatentEffectList.at(i)->GetConditionsValue())
        //{
        //    m_LatentEffectList.at(i)->Activate();
        //}
        //else
        //{
        //    m_LatentEffectList.at(i)->Deactivate();
        //}
        break;
    case LATENT_WEAPON_BROKEN:
    {
        auto slot = latentEffect.GetSlot();
        auto item = (CItemWeapon*)m_POwner->getEquip((SLOTTYPE)slot);
        switch (slot)
        {
        case SLOT_MAIN:
        case SLOT_SUB:
        case SLOT_RANGED:
            expression = item != nullptr && item->isUnlocked();
            break;
        }
        break;
    }
    case LATENT_IN_DYNAMIS:
        expression = m_POwner->isInDynamis();
        break;
    case LATENT_IN_ASSAULT:
        expression = m_POwner->isInAssault();
        break;
    case LATENT_FOOD_ACTIVE:
        expression = m_POwner->StatusEffectContainer->HasStatusEffect(EFFECT_FOOD) &&
            m_POwner->StatusEffectContainer->GetStatusEffect(EFFECT_FOOD)->GetSubID() == latentEffect.GetConditionsValue();
        break;
    case LATENT_JOB_LEVEL_BELOW:
        expression = m_POwner->GetMLevel() < latentEffect.GetConditionsValue();
        break;
    case LATENT_JOB_LEVEL_ABOVE:
        expression = m_POwner->GetMLevel() >= latentEffect.GetConditionsValue();
        break;
    case LATENT_WEATHER_ELEMENT:
        expression = latentEffect.GetConditionsValue() == zoneutils::GetWeatherElement(battleutils::GetWeather((CBattleEntity*)m_POwner, false));
        break;
    case LATENT_NATION_CONTROL:
    {
        //player is logging in/zoning
        if (m_POwner->loc.zone == nullptr)
        {
            break;
        }

        auto region = m_POwner->loc.zone->GetRegionID();
        auto hasSignet = m_POwner->StatusEffectContainer->HasStatusEffect(EFFECT_SIGNET);
        auto hasSanction = m_POwner->StatusEffectContainer->HasStatusEffect(EFFECT_SANCTION);
        auto hasSigil = m_POwner->StatusEffectContainer->HasStatusEffect(EFFECT_SIGIL);

        switch (latentEffect.GetConditionsValue())
        {
        case 0:
            //under own nation's control
            expression = region < 28 && conquest::GetRegionOwner(region) == m_POwner->profile.nation && (hasSignet || hasSanction || hasSigil);
            break;
        case 1:
            //outside of own nation's control
            expression = region < 28 && m_POwner->profile.nation != conquest::GetRegionOwner(region) && (hasSignet || hasSanction || hasSigil);
            break;
        }
        break;
    }
    case LATENT_ZONE_HOME_NATION:
    {
        //player is logging in/zoning
        if (m_POwner->loc.zone == nullptr)
        {
            break;
        }

        auto PZone = m_POwner->loc.zone;
        auto region = (REGIONTYPE)latentEffect.GetConditionsValue();

        switch (region)
        {
        case REGION_SANDORIA:
            expression = m_POwner->profile.nation == 0 && PZone->GetRegionID() == region;
            break;
        case REGION_BASTOK:
            expression = m_POwner->profile.nation == 1 && PZone->GetRegionID() == region;
            break;
        case REGION_WINDURST:
            expression = m_POwner->profile.nation == 2 && PZone->GetRegionID() == region;
            break;
        default:
            break;
        }
        break;
    }
    case LATENT_MP_OVER:
        expression = m_POwner->health.mp >= latentEffect.GetConditionsValue();
        break;
    case LATENT_WEAPON_DRAWN_MP_OVER:
        expression = m_POwner->health.mp > latentEffect.GetConditionsValue() && m_POwner->animation == ANIMATION_ATTACK;
        break;
    case LATENT_ELEVEN_ROLL_ACTIVE:
        expression = m_POwner->StatusEffectContainer->CheckForElevenRoll();
        break;
    default:
        latentFound = false;
        ShowWarning("Latent ID %d unhandled in ProcessLatentEffect\n", latentEffect.GetConditionsID());
        break;
    }

    // if we did not hit the default case, attempt to apply the latent effect based on the expression
    if (latentFound)
    {
        return ApplyLatentEffect(latentEffect, expression);
    }
    return false;
}