#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <vector>

struct Resource {
    Resource(std::unique_ptr<char> byte)
        : byte_(std::move(byte)) {}
    virtual ~Resource() = default;
    // Rule of 5

    char* byte() const { return byte_.get(); }
    virtual std::string name() const = 0;

protected:
    std::unique_ptr<char> byte_;
};

struct ResourceA : Resource {
    ~ResourceA() override {
        std::cout << "ResourceA D'tor\n";
    }

    ResourceA(std::unique_ptr<char> byte)
        : Resource(std::move(byte)) {}
    std::string name() const override { return std::string("ResourceA ").append(1, *byte_); }
};

struct ResourceB : Resource {
    ~ResourceB() override {
        std::cout << "ResourceB D'tor\n";
    }

    ResourceB(std::unique_ptr<char> byte)
        : Resource(std::move(byte)) {}
    std::string name() const override { return std::string("ResourceB ") + *byte_; }
};

struct ResourceFactory {
    static std::unique_ptr<Resource> makeResourceA(std::unique_ptr<char> byte) {
        return std::make_unique<ResourceA>(std::move(byte));
    }
    static std::unique_ptr<Resource> makeResourceB(std::unique_ptr<char> byte) {
        return std::make_unique<ResourceB>(std::move(byte));
    }
};

struct ResourceCollection {
    void add(std::unique_ptr<Resource> r) { resources.push_back(std::move(r)); }
    void clear() { resources.clear(); }
    Resource* operator[](int index) const { return resources[index].get(); }
    void printAll() const {
        for (const auto& res : resources) {
            std::cout << res->name() << '\n';
        }
    }

private:
    std::vector<std::unique_ptr<Resource>> resources;
};

int main() {
    ResourceCollection collection;
    collection.add(ResourceFactory::makeResourceA(std::make_unique<char>(0x78)));
    collection.add(ResourceFactory::makeResourceB(std::make_unique<char>(0x79)));
    collection.printAll();

    auto* firstByte = collection[0]->byte();
    std::cout << *firstByte << '\n';

    collection.clear();
    // Use already free memory!
    //std::cout << *firstByte << '\n';

    return 0;
}
