//
// Created by dancrosby on 3/4/20.
//

#ifndef EAGLE0_AISCORECALCULATOR_HPP
#define EAGLE0_AISCORECALCULATOR_HPP

#include <future>

#include "src/main/cpp/net/eagle0/shardok/ai/AIAttackLocations.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai/AIStrategy.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/ShardokCTypes.h"
#include "src/main/cpp/net/eagle0/shardok/library/ShardokEngine.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/action_point_distances/ActionPointDistancesCache.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/settings/GameSettings.hpp"
#include "src/main/flatbuffer/net/eagle0/shardok/storage/game_state.hpp"
#include "src/main/protobuf/net/eagle0/shardok/api/command_descriptor.pb.h"
#include "src/main/protobuf/net/eagle0/shardok/api/game_state_view.pb.h"

namespace shardok {

using net::eagle0::shardok::api::GameStateView;
using GameState = fb::GameState;
using shardok::PlayerId;
using std::future;
using std::vector;

using ScoreValue = double;
using CommandProto = net::eagle0::shardok::api::CommandDescriptor;

class AIScoreCalculator {
public:
    struct IndexAndScore {
        size_t index;
        CommandType type;
        ScoreValue lookaheadScore;
        ScoreValue immediateScore;
    };

private:
    [[nodiscard]] static auto DefenderScatterStrategyScoreForState(
            const GameStateW &gameState,
            int roundsRemaining,
            const SettingsGetter &settings,
            const ALCache &alCache,
            const APDCache &apdCache) -> ScoreValue;

    [[nodiscard]] static auto DefenderHoldCastlesStrategyScoreForState(
            const GameStateW &gameState,
            const CoordsSet &castleCoords,
            int roundsRemaining,
            const SettingsGetter &settings,
            const ALCache &alCache,
            const APDCache &apdCache) -> ScoreValue;

    [[nodiscard]] static auto FleeStrategyScoreForState(
            const GameStateW &gameState,
            PlayerId playerId) -> ScoreValue;

    [[nodiscard]] static auto DefenderScoreForState(
            const GameStateW &gameState,
            const AIStrategy &defenderStrategy,
            const CoordsSet &castleCoords,
            int roundsRemaining,
            const SettingsGetter &settings,
            const ALCache &alCache,
            const APDCache &apdCache) -> ScoreValue;

    [[nodiscard]] static auto AttackerScoreForState(
            const GameStateW &gameState,
            const AIStrategy &attackerStrategy,
            const CoordsSet &castleCoords,
            int roundsRemaining,
            const SettingsGetter &settings,
            const ALCache &alCache,
            const APDCache &apdCache) -> ScoreValue;

    struct ImmediateAndLookaheadScore {
        ScoreValue immediateScore;
        future<ScoreValue> lookaheadScore;
    };

    static auto BasicLookaheadCalculator(
            PlayerId pid,
            bool isDefender,
            int remainingLookahead,
            int maxRepeatCount,
            const shared_ptr<ShardokEngine> &innerEngine,
            ScoreValue currentUtility,
            const AIStrategy &attackerStrategy,
            const SettingsGetter &settingsGetter,
            const CoordsSet &allCastleCoords,
            const APDCache &apdCache,
            const ALCache &alCache) -> ScoreValue;

    static auto CalcOne(
            PlayerId pid,
            bool isDefender,
            uint32_t commandIndex,
            int remainingLookahead,
            int maxRepeatCount,
            const std::shared_ptr<RandomGenerator> &randomGenerator,
            const ShardokEngine &guessedEngine,
            const AIStrategy &attackerStrategy,
            const SettingsGetter &settingsGetter,
            const CoordsSet &allCastleCoords,
            const APDCache &apdCache,
            const ALCache &alCache) -> ImmediateAndLookaheadScore;

    struct CommandEvaluationResult {
        ScoreValue immediateScore;
        ScoreValue lookaheadScore;
    };

    static auto EvaluateCommand(
            PlayerId pid,
            bool isDefender,
            uint32_t commandIndex,
            int remainingLookahead,
            int maxRepeatCount,
            const ShardokEngine &guessedEngine,
            const AIStrategy &attackerStrategy,
            ScoreValue currentUtility,
            const SettingsGetter &settingsGetter,
            const CoordsSet &allCastleCoords,
            const APDCache &apdCache,
            const ALCache &alCache) -> CommandEvaluationResult;

public:
    [[nodiscard]] static auto GuessedStateScore(
            bool isDefender,
            const GameStateW &state,
            const AIStrategy &aiStrategy,
            const CoordsSet &allCastleCoords,
            const SettingsGetter &settingsGetter,
            const APDCache &apdCache,
            const ALCache &alCache) -> ScoreValue;

    [[nodiscard]] static auto BestCommandIndex(
            PlayerId pid,
            bool isDefender,
            int remainingLookahead,
            int maxRepeatCount,
            const ShardokEngine &guessedEngine,
            const AIStrategy &attackerStrategy,
            ScoreValue currentUtility,
            const SettingsGetter &settingsGetter,
            const CoordsSet &allCastleCoords,
            const APDCache &apdCache,
            const ALCache &alCache) -> IndexAndScore;

    [[nodiscard]] static auto CommandScore(
            PlayerId pid,
            bool isDefender,
            int remainingLookahead,
            int maxRepeatCount,
            const ShardokEngine &guessedEngine,
            const AIStrategy &attackerStrategy,
            ScoreValue currentUtility,
            const SettingsGetter &settingsGetter,
            const CoordsSet &allCastleCoords,
            const APDCache &apdCache,
            const ALCache &alCache,
            size_t commandIndex) -> ScoreValue;
};

}  // namespace shardok

#endif  // EAGLE0_AISCORECALCULATOR_HPP
