//
//  GameSettings.hpp
//  eagle0
//
//  Created by Dan Crosby on 9/3/18.
//  Copyright © 2018 none. All rights reserved.
//

#ifndef GameSettings_hpp
#define GameSettings_hpp

#include <memory>
#include <vector>

#include "src/main/cpp/net/eagle0/common/RandomGenerator.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/BattalionType.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/map/Terrain.hpp"
#include "src/main/flatbuffer/net/eagle0/shardok/storage/battalion_type.hpp"
#include "src/main/flatbuffer/net/eagle0/shardok/storage/weather.hpp"
#include "src/main/protobuf/net/eagle0/shardok/common/weather.pb.h"
#include "src/main/resources/net/eagle0/shardok/generated_settings_keys.hpp"
#include "src/main/resources/net/eagle0/shardok/settings_generated.h"

namespace shardok {

using net::eagle0::shardok::common::Weather;
using net::eagle0::shardok::settings::SettingsStruct;

extern const SettingsKeys kSettingsKeys;

class GameSettings {
private:
    SettingsStruct backingStruct;

    std::vector<BattalionTypeSPtr> battalionTypes;

    std::shared_ptr<RandomGenerator> randomGenerator;

    std::vector<int> firePropensityByTerrain = std::vector<int>(kTerrainTypeCount);
    std::vector<int> firePropensityByWeatherConditions = std::vector<int>(7);

public:
    explicit GameSettings(const std::shared_ptr<RandomGenerator> &generator = nullptr);

    class SettingNotFoundException : public std::exception {
    private:
        string description;

    public:
        explicit SettingNotFoundException(const string &key)
            : description(std::string("Unable to find key ") + key) {}

        [[nodiscard]] auto what() const noexcept -> const char * override {
            return description.c_str();
        };
    };

    class Getter {
    private:
        const GameSettings &settings;

    public:
        explicit Getter(const GameSettings &settings) : settings(settings) {}

        virtual ~Getter() = default;

        [[nodiscard]] auto ActionCostFor(int pointValue) const -> ActionCost;

        [[nodiscard]] auto Backing() const -> const SettingsStruct & {
            return settings.backingStruct;
        }

        [[nodiscard]] auto GetRandomGenerator() const -> const std::shared_ptr<RandomGenerator> {
            return settings.randomGenerator;
        }

        [[nodiscard]] auto GetBattalionType(BattalionTypeId typeId) const
                -> const BattalionTypeSPtr &;

        [[nodiscard]] auto GetFirePropensityByTerrainType(TerrainType terrainType) const -> int;

        [[nodiscard]] auto GetFirePropensityByWeatherConditions(
                Weather::Conditions conditions) const -> int;

        [[nodiscard]] auto GetFirePropensityByWeatherConditions(
                net::eagle0::shardok::storage::fb::WeatherConditions conditions) const -> int;
    };

    [[nodiscard]] auto GetGetter() const -> Getter { return Getter(*this); }

    class Setter {
    private:
        GameSettings &settings;

    public:
        explicit Setter(GameSettings &settings) : settings(settings) {}

        void SetDouble(const string &key, double value);
        void SetInt(const string &key, int value);
        void SetBool(const string &key, bool value);

        void SetFromTypesAndValues(
                const std::unordered_map<string, string> &types,
                const std::unordered_map<string, string> &values);

        void SetRandomGenerator(std::shared_ptr<RandomGenerator> generator);

        void RegisterBattalionType(const std::shared_ptr<const BattalionType> &newType);

        void SetFirePropensity(TerrainType terrainType, int propensity);

        void SetFirePropensityFromWeather(Weather::Conditions wc, int propensity);
    };

    auto GetSetter() -> Setter { return Setter(*this); }

    virtual ~GameSettings() = default;
};

typedef std::shared_ptr<GameSettings> GameSettingsSPtr;
typedef GameSettings::Getter SettingsGetter;
typedef GameSettings::Setter SettingsSetter;
}  // namespace shardok

#endif /* GameSettings_hpp */
