#include "mod_auto_face_target.h"
#include "Config.h"
#include "ObjectAccessor.h"
#include "ScriptMgr.h"
#include "SpellDefines.h"
#include "SpellInfo.h"
#include "SpellMgr.h"

namespace
{
    constexpr uint32 TARGET_FLAG_LOCATION_MASK = TARGET_FLAG_SOURCE_LOCATION | TARGET_FLAG_DEST_LOCATION;
}

void AutoFaceTarget::LoadConfig()
{
    _enabled = sConfigMgr->GetOption("AutoFaceTarget.Enable", true);
}

void AutoFaceTarget::CorrectFacing(Player* mover, uint32 spellId) const
{
    if (!_enabled)
        return;

    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
    if (!spellInfo)
        return;

    // Only spells that actually require the caster to face the target can fail
    // the in-front check; nothing to correct otherwise.
    if (!(spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT))
        return;

    // Ground/location-targeted spells (Blizzard, Flare, ...) don't carry a unit
    // target at all, so the in-front check never applies to them - leave the
    // player facing wherever they clicked.
    if (spellInfo->Targets & TARGET_FLAG_LOCATION_MASK)
        return;

    Unit* target = ObjectAccessor::GetUnit(*mover, mover->GetTarget());
    if (!target || target == mover)
        return;

    if (mover->HasInArc(static_cast(M_PI), target))
        return;

    float angle = mover->GetAngle(target);

    // SetFacingTo() only takes effect once its movement spline is evaluated on
    // a later world tick, but the in-front check that follows this hook runs
    // synchronously right away - so without also setting the orientation
    // directly, the very first cast attempt still fails and the player has to
    // press again once the spline has caught up. The spline call must come
    // first, since it captures the *current* (pre-correction) orientation as
    // its turn's starting point for the client-visible animation.
    mover->SetFacingTo(angle);
    mover->SetOrientation(angle);
}

class AutoFaceTarget_Misc : public MiscScript
{
public:
    AutoFaceTarget_Misc() : MiscScript("AutoFaceTarget_Misc", { MISCHOOK_VALIDATE_SPELL_AT_CAST_SPELL_RESULT }) { }

    void ValidateSpellAtCastSpellResult(Player* /*player*/, Unit* mover, Spell* /*spell*/, uint32 /*oldSpellId*/, uint32 spellId) override
    {
        // Mirror the core in-front check, which only ever applies to player casters.
        if (!mover->IsPlayer())
            return;

        sAutoFaceTarget->CorrectFacing(mover->ToPlayer(), spellId);
    }
};

class AutoFaceTarget_World : public WorldScript
{
public:
    AutoFaceTarget_World() : WorldScript("AutoFaceTarget_World") { }

    void OnBeforeConfigLoad(bool /*reload*/) override
    {
        sAutoFaceTarget->LoadConfig();
    }
};

void AddSC_mod_auto_face_target()
{
    new AutoFaceTarget_Misc();
    new AutoFaceTarget_World();
}