#include <algorithm>
#include <cassert>
#include <functional>
#include <iostream>
#include <memory>
#include <set>
#include <vector>

class Ammunition {
public:
    Ammunition(size_t amount)
        : _amount(amount) {}
    virtual ~Ammunition() = default;

    virtual void fire() = 0;
    virtual void draw() = 0;
    virtual int getDamage() const = 0;

    size_t amount() const { return _amount; }

private:
    size_t _amount = 0;
};

class FireStrategy {
public:
    virtual void fire(const Ammunition& ammunition) = 0;
};

class SDLFireStrategy : public FireStrategy {
public:
    void fire(const Ammunition& ammunition) override {
        std::cout << "SDL Deal: " << ammunition.getDamage() << " damage!" << '\n';
    }
};

class WAMFireStrategy : public FireStrategy {
public:
    void fire(const Ammunition& ammunition) override {
        std::cout << "WAM Deal: " << ammunition.getDamage() << " damage!" << '\n';
    }
};

class DrawStrategy {
public:
    virtual void draw(const Ammunition& ammunition) = 0;
};

class OpenGLDrawStrategy : public DrawStrategy {
public:
    void draw(const Ammunition& ammunition) override {
        std::cout << "OpenGL Ammunition: " << ammunition.amount() << '\n';
    }
};

class MetalDrawStrategy : public DrawStrategy {
public:
    void draw(const Ammunition& ammunition) override {
        std::cout << "Metal Ammunition: " << ammunition.amount() << '\n';
    }
};

class RoundShot : public Ammunition {
public:
    RoundShot(size_t amount, std::unique_ptr<FireStrategy>&& fireStrategy, std::unique_ptr<DrawStrategy>&& drawStrategy)
        : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {}
    void fire() override {
        _fireStrategy->fire(*this /* other args if needed*/);
    }
    void draw() override {
        _drawStrategy->draw(*this /* other args if needed*/);
    }
    int getDamage() const override { return 10; }

private:
    std::unique_ptr<FireStrategy> _fireStrategy;
    std::unique_ptr<DrawStrategy> _drawStrategy;
};

class ChainShot : public Ammunition {
public:
    ChainShot(size_t amount, std::unique_ptr<FireStrategy>&& fireStrategy, std::unique_ptr<DrawStrategy>&& drawStrategy)
        : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {}
    void fire() override {
        _fireStrategy->fire(*this /* other args if needed*/);
    }
    void draw() override {
        _drawStrategy->draw(*this /* other args if needed*/);
    }
    int getDamage() const override { return 15; }

private:
    std::unique_ptr<FireStrategy> _fireStrategy;
    std::unique_ptr<DrawStrategy> _drawStrategy;
};

class GrapeShot : public Ammunition {
public:
    GrapeShot(size_t amount, std::unique_ptr<FireStrategy>&& fireStrategy, std::unique_ptr<DrawStrategy>&& drawStrategy)
        : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {}
    void fire() override {
        _fireStrategy->fire(*this /* other args if needed*/);
    }
    void draw() override {
        _drawStrategy->draw(*this /* other args if needed*/);
    }
    int getDamage() const override { return 20; }

private:
    std::unique_ptr<FireStrategy> _fireStrategy;
    std::unique_ptr<DrawStrategy> _drawStrategy;
};

int main() {
    std::unique_ptr<Ammunition> grapeShot = std::make_unique<GrapeShot>(
        100, std::make_unique<SDLFireStrategy>(), std::make_unique<OpenGLDrawStrategy>());

    grapeShot->fire();
    grapeShot->draw();
}