#ifndef GAME_MWDIALOGUE_FILTER_H
#define GAME_MWDIALOGUE_FILTER_H

#include <utility>
#include <vector>

#include "../mwworld/ptr.hpp"

namespace ESM
{
    struct DialInfo;
    struct Dialogue;
    class RefId;
}

namespace MWDialogue
{
    class SelectWrapper;

    class Filter
    {
        MWWorld::Ptr mActor;
        int mChoice;
        bool mTalkedToPlayer;

        bool testActor(const ESM::DialInfo& info) const;
        ///< Is this the right actor for this \a info?

        bool testPlayer(const ESM::DialInfo& info) const;
        ///< Do the player and the cell the player is currently in match \a info?

        bool testSelectStructs(const ESM::DialInfo& info) const;
        ///< Are all select structs matching?

        bool testDisposition(const ESM::DialInfo& info, bool invert = false) const;
        ///< Is the actor disposition toward the player high enough (or low enough, if \a invert is true)?

        bool testFunctionLocal(const SelectWrapper& select) const;

        bool testSelectStruct(const SelectWrapper& select) const;

        bool testSelectStructNumeric(const SelectWrapper& select) const;

        int getSelectStructInteger(const SelectWrapper& select) const;

        bool getSelectStructBoolean(const SelectWrapper& select) const;

        int getFactionRank(const MWWorld::Ptr& actor, const ESM::RefId& factionId) const;

        bool hasFactionRankSkillRequirements(const MWWorld::Ptr& actor, const ESM::RefId& factionId, int rank) const;

        bool hasFactionRankReputationRequirements(
            const MWWorld::Ptr& actor, const ESM::RefId& factionId, int rank) const;

    public:
        using Response = std::pair<const ESM::Dialogue*, const ESM::DialInfo*>;

        Filter(const MWWorld::Ptr& actor, int choice, bool talkedToPlayer);

        std::vector<Response> list(const ESM::Dialogue& dialogue, bool fallbackToInfoRefusal, bool searchAll,
            bool invertDisposition = false) const;
        ///< List all infos that could be used on the given actor, using the current runtime state of the actor.
        /// \note If fallbackToInfoRefusal is used, the returned DialInfo might not be from the supplied ESM::Dialogue.

        bool couldPotentiallyMatch(const ESM::DialInfo& info) const;
        ///< Check if this INFO could potentially be said by the given actor, ignoring runtime state filters and
        ///< ignoring player filters.

        Response search(const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const;
        ///< Get a matching response for the requested dialogue.
        ///  Redirect to "Info Refusal" topic if a response fulfills all conditions but disposition.
    };
}

#endif
