#include "aicast.hpp"

#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"

#include "../mwworld/class.hpp"

#include "aicombataction.hpp"
#include "creaturestats.hpp"
#include "spellcasting.hpp"
#include "steering.hpp"

MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell)
    : mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(0)
{
    ActionSpell action = ActionSpell(spellId);
    bool isRanged;
    mDistance = action.getCombatRange(isRanged);
}

MWMechanics::AiPackage *MWMechanics::AiCast::clone() const
{
    return new AiCast(*this);
}

bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& characterController, MWMechanics::AiState& state, float duration)
{
    MWWorld::Ptr target;
    if (actor.getCellRef().getRefId() == mTargetId)
    {
        // If the target has the same ID as caster, consider that actor casts spell with Self range.
        target = actor;
    }
    else
    {
        target = getTarget();
        if (!target)
            return true;

        if (!mManual && !pathTo(actor, target.getRefData().getPosition().pos, duration, mDistance))
        {
            return false;
        }

        osg::Vec3f dir = target.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3();
        bool turned = smoothTurn(actor, getZAngleToDir(dir), 2, osg::DegreesToRadians(3.f));
        turned &= smoothTurn(actor, getXAngleToDir(dir), 0, osg::DegreesToRadians(3.f));

        if (!turned)
            return false;
    }

    // Check if the actor is already casting another spell
    bool isCasting = MWBase::Environment::get().getMechanicsManager()->isCastingSpell(actor);
    if (isCasting && !mCasting)
        return false;

    if (!mCasting)
    {
        MWBase::Environment::get().getMechanicsManager()->castSpell(actor, mSpellId, mManual);
        mCasting = true;
        return false;
    }

    // Finish package, if actor finished spellcasting
    return !isCasting;
}

MWWorld::Ptr MWMechanics::AiCast::getTarget() const
{
    MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mTargetId, false);

    return target;
}

int MWMechanics::AiCast::getTypeId() const
{
    return AiPackage::TypeIdCast;
}

unsigned int MWMechanics::AiCast::getPriority() const
{
    return 3;
}