//
//  ShardokGamesManager.hpp
//  Server
//
//  Created by Dan Crosby on 10/29/17.
//  Copyright © 2017 none. All rights reserved.
//

#ifndef ServerGamesManager_hpp
#define ServerGamesManager_hpp

#include <functional>
#include <memory>
#include <shared_mutex>
#include <utility>
#include <vector>

#include "src/main/cpp/net/eagle0/shardok/ai/ShardokAIClient.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/settings/GameSettings.hpp"
#include "src/main/protobuf/net/eagle0/shardok/common/game_status.pb.h"
#include "src/main/protobuf/net/eagle0/shardok/common/player_info.pb.h"
#include "src/main/protobuf/net/eagle0/shardok/common/victory_condition.pb.h"

class GameStatePersister;

using net::eagle0::shardok::common::GameStatus;
using net::eagle0::shardok::common::PlayerInfo;
using std::string;
using std::vector;
using VictoryConditionProto = net::eagle0::shardok::common::VictoryCondition;

namespace shardok {

class ShardokGameController;

struct PlayerInfoWithUnits {
public:
    const PlayerInfoProto playerInfo;
    const vector<Unit> units;

    static auto MakePlayerInfo(
            const PlayerId pid,
            const bool def,
            const int food,
            const vector<VictoryConditionProto> &victoryConditions,
            const vector<PlayerId> &alliedPids,
            const int32_t eagleFactionId,
            const bool isAi) -> PlayerInfoProto {
        PlayerInfoProto pi;
        pi.set_player_id(pid);
        pi.set_is_defender(def);
        pi.set_starting_food(food);
        *pi.mutable_victory_conditions() = {begin(victoryConditions), end(victoryConditions)};
        for (const PlayerId alliedPid : alliedPids) { pi.add_allies()->set_player_id(alliedPid); }
        pi.set_eagle_faction_id(eagleFactionId);
        pi.set_is_ai(isAi);

        return pi;
    }

    PlayerInfoWithUnits(
            const PlayerId pid,
            const bool isAi,
            const int32_t eagleFactionId,
            const bool def,
            const int food,
            const vector<VictoryConditionProto> &victoryConditions,
            const vector<PlayerId> &alliedPids,
            vector<Unit> us)
        : playerInfo(MakePlayerInfo(
                  pid,
                  def,
                  food,
                  victoryConditions,
                  alliedPids,
                  eagleFactionId,
                  isAi)),
          units(std::move(us)) {}
};

class ShardokGamesManager {
private:
    GameSettingsSPtr gameSettings;

    std::shared_mutex runningControllersLock;
    gtl::flat_hash_map<GameId, std::shared_ptr<ShardokGameController>> runningControllers;

    auto LockedCreateGame(
            const std::shared_ptr<ShardokGameController> &controller,
            const vector<PlayerInfoWithUnits> &playerInfos,
            const string &serializedRequest) -> GameId;

public:
    explicit ShardokGamesManager(const std::vector<std::string> &extraSettings);

    auto GetController(const GameId &gameId) -> std::shared_ptr<ShardokGameController>;

    auto UnlockedCreateSpecifiedGame(
            const GameId &gameId,
            const vector<PlayerInfoWithUnits> &playerSetupInfos,
            const string &mapName,
            const string &customMapBytes,
            int month,
            const string &serializedRequest) -> GameId;

    auto UnlockedCreateFromGameStateBytes(
            const byte_vector &gameStateBytes,
            int32_t startingHistoryCount,
            const string &mapName) -> GameId;
};
}  // namespace shardok

#endif /* ServerGamesManager_hpp */
