#include <iostream>
#include <format>
#include <type_traits>
#include <string>

class KeyValue
{
public:
    KeyValue(std::string key, int value) : m_key{ std::move(key) }, m_value{ value } { }
    auto& getKey() const { return m_key; }
    auto& getValue() const { return m_value; }
private:
    std::string m_key;
    int m_value;
};

template <>
class std::formatter<KeyValue>
{
public:
    auto parse(auto& context)
    {
        auto iter = context.begin();
        const auto end = context.end();
        if (iter == end || *iter == '}')
        {
            m_out = OutputType::KeyAndValue; // {}
            return iter;
        }
        switch (*iter)
        {
        case 'a': m_out = OutputType::Key; break; // {:a} -> key
        case 'b': m_out = OutputType::Value; break; // {:b} -> value
        case 'c': m_out = OutputType::KeyAndValue; break; // {:c} -> key and value
        default:
            throw std::format_error{ "Invalid KeyValue format specifier. " };
        }

        ++iter;
        if (iter != end && *iter != '}')
            throw std::format_error{ "Invalid KeyValue format specifier. " };
        return iter;
    }
    auto format(const KeyValue& kv, auto& context)
    {
        switch (m_out)
        {
            using enum OutputType;
        case Key:
            return std::format_to(context.out(), "{}", kv.getKey());
        case Value:
            return std::format_to(context.out(), "{}", kv.getValue());
        default:
            return std::format_to(context.out(), "{} - {}", kv.getKey(), kv.getValue());
        }
    }
private:
    enum class OutputType
    {
        Key, Value, KeyAndValue
    };
    OutputType m_out{ OutputType::KeyAndValue };
};


struct Point { int x, y; };

template <>
class std::formatter<Point>
{
public:
    typename std::format_parse_context::iterator parse(std::format_parse_context& context)
    {
        auto iter = context.begin();
        for (; iter != context.end() && *iter != '}'; ++iter)
        {
            if (*iter == '#') { m_hashtag = true; }
            else throw std::format_error{ "Invalid KeyValue format specifier. " };
        }
        return iter;
    }

    typename std::format_context::iterator format(const Point& p, std::format_context& context)
    {
        char left = m_hashtag ? '[' : '(';
        char right = m_hashtag ? ']' : ')';
        return std::format_to(context.out(), "{}{}, {}{}", left, p.x, p.y, right);
    }

private:
    bool m_hashtag = false;
};

int main()
{
    KeyValue kv{ "Key", 0 };
    Point p{ .x = 1, .y = 1 };
    try
    {
        std::cout << std::format("{}\n", kv);
        std::cout << std::format("{:a}\n", kv);
        std::cout << std::format("{:b}\n", kv);
        std::cout << std::format("{:c}\n", kv);
        std::cout << std::format("{:ab}\n", kv);
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << '\n';
    }

    try
    {
        std::cout << std::format("{}\n", p);
        //std::cout << std::format("{:#}\n", p);
        //std::cout << std::format("{:#x}\n", p);
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << '\n';
    }

}