// Erweiterte Version von CData3 mit der Moeglichkeit,
// CData4-Objekte mit println() auszugeben
module;
#include <iostream>
#include <string>
#include <string_view>
#include <stdexcept>

// Makro für Debug-Ausgaben
//#define TRACE_METH
#ifdef TRACE_METH
#include <print>
#define TRACE(T_)   std::println("Obj-Nr:{} {}",objNum,T_);
#define TRACES(T_)  std::println("Obj-Nr:{} {}",self.objNum,T_);
#else
#define TRACE(T_)
#define TRACES(T_)
#endif

export module CData4;

export class CData4
{
    std::string text;
    // Objektzaehler fuer Debug-Zwecke
    inline static int objCount = 0;
    int objNum;             // Objektnummer
public:
    // Standard-ctor
    CData4();
    // ctor mit Text
    CData4(std::string_view _text);
    // copy-ctor
    CData4(const CData4& src);
    // move-ctor
    CData4(CData4&& src);
    // Zuweisungsoperator
    CData4& operator = (this CData4& self, const CData4& src);
    // move-Zuweisungsopertor
    CData4& operator = (this CData4& self, CData4&& src);
    // dtor
    ~CData4();
    // Ausgabe des Datenfeldes
    friend std::ostream& operator << (std::ostream& out, const CData4& obj);
    // Ueberladener Indexoperator
    auto operator [] (size_t index);
    // space-operator
    auto operator <=> (this const CData4& self, const CData4& op2);
    // Vergleich auf Gleichheit
    bool operator == (const CData4& op2) const;
    // Fuer print() und format()
    friend struct std::formatter<CData4>;
};
// Standard-ctor
CData4::CData4()
{
    objNum = ++objCount;
    TRACE(">>> ctor CData4()");
}
// ctor mit Datenfeldgroesse
CData4::CData4(std::string_view _text): text(_text)
{
    objNum = ++objCount;
    TRACE(">>> ctor CData4(text)");
}
// copy-ctor
CData4::CData4(const CData4& src): text(src.text)
{
    objNum = ++objCount;
    TRACE(">>> copy-ctor")
}
// move ctor
CData4::CData4(CData4&& src)
{
    // Der move-ctor von std::string versetzt das
    // Quellobjekt in einen gueltigen Zustand
    text = std::move(src.text);
    objNum = src.objNum;
    TRACE(">>> move-ctor");
}
// Zuweisungsoperator
CData4& CData4::operator = (this CData4& self, const CData4& src)
{
    if (&self == &src)   // keine Zuweisung auf sich selbst!
        return self;
    self.text = src.text;  // neue Daten uebernehmen
    TRACES("operator =");
    return self;
}
// move-Zuweisungsopertor
CData4& CData4::operator = (this CData4& self, CData4&& src)
{
    if (&self == &src)   // keine Zuweisung auf sich selbst!
        return self;
    self.text = std::move(src.text);  // neue Daten uebernehmen
    TRACES("move-operator =");
    return self;
}
// dtor
CData4::~CData4()
{
    TRACE("<<< dtor");
}
// Ueberladener Indexoperator
auto CData4::operator [] (size_t index)
{
    if ((index < 0) || (index >= text.length()))
        throw std::out_of_range("Index ausserhalb des Bereichs!");
    return text[index];
}
// Ausgabe des Datenfeldes
export std::ostream& operator << (std::ostream& out, const CData4& obj)
{
    out << obj.text;
    return out;
}
// space-operator
auto CData4::operator <=> (this const CData4& self, const CData4& op2)
{
    // Nun die Daten vergleichen
    // Aufruf des operators ==
    return (self == op2) <=> true;
}
// Vergleich auf Gleichheit
bool CData4::operator == (const CData4& op2) const
{
    // Nun die Daten vergleichen
    return text == op2.text;
}

// Spezialisertes formatter-Template fuer
// den Datentyp CData4
template<>
struct std::formatter<CData4>
{
    constexpr auto parse(auto& ctx)
    {
        // Formatspezifikation analysieren
        // hier noch nicht erforderlich
        return ctx.begin();
    }
    auto format(const CData4& s,
        std::format_context& ctx) const
    {
        // format_to() zur formatierten Ausgabe aufrufen
        // Formatierte Daten in Puffer den ctx.out() liefert
        return std::format_to(ctx.out(),
            "{}",s.text);

    }
};

export extern template struct std::formatter<CData4>;
