
module;
#include <print>
#include <cstdlib>
#include <iostream>
#include <iomanip>

// Makro für Debug-Ausgaben
//#define TRACE_METH
#ifdef TRACE_METH
#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 CData2;

export class CData2
{
    int* pData = nullptr;   // Zeiger auf Datenfeld
    size_t dSize;           // Groesse Datenfeld
    // Objektzaehler fuer Debug-Zwecke
    inline static int objCount = 0;
    int objNum;             // Objektnummer
public:
    // Standard-ctor (pData=nullptr!)
    CData2();
    // ctor mit Datenfeldgroesse
    CData2(size_t _dSize);
    // copy-ctor
    CData2(const CData2& src);
    // move-ctor
    CData2(CData2&& src);
    // Zuweisungsoperator
    CData2& operator = (this CData2& self, const CData2& src);
    // move-Zuweisungsopertor
    CData2& operator = (this CData2& self, CData2&& src);
    // dtor
    ~CData2();
    // Ausgabe des Datenfeldes
    friend std::ostream& operator << (std::ostream& out, const CData2& obj);
    // Ueberladener Indexoperator
    int& operator [] (size_t index);
    // space-operator
    auto operator <=> (this const CData2& self, const CData2& op2);
    // Vergleich auf Gleichheit
    bool operator == (const CData2& op2) const;
};
// Standard-ctor
CData2::CData2()
{
    objNum = ++objCount;
    TRACE(">>> ctor CData2()");
}
// ctor mit Datenfeldgroesse
CData2::CData2(size_t _dSize)
{
    dSize = _dSize;
    pData = new int[dSize];
    for (size_t index = 0; index < dSize; index++)
        pData[index] = std::rand() % 100;
    objNum = ++objCount;
    TRACE(">>> ctor CData2(size)");
}
// copy-ctor
CData2::CData2(const CData2& src) :
    CData2(src.dSize)
{
    for (size_t index = 0; index < dSize; index++)
        pData[index] = src.pData[index];
    objNum = ++objCount;
    TRACE(">>> copy-ctor")
}
// move ctor
CData2::CData2(CData2&& src)
{
    dSize = src.dSize;
    pData = std::move(src.pData);
    objNum = src.objNum;
    src.pData = nullptr;    // WICHTIG!!!!
    TRACE(">>> move-ctor");
}
// Zuweisungsoperator
CData2& CData2::operator = (this CData2& self, const CData2& src)
{
    if (&self == &src)   // keine Zuweisung auf sich selbst!
        return self;
    delete[] self.pData;     // bisherige Daten freigeben
    self.dSize = src.dSize;  // neue Daten uebernehmen
    self.pData = new int[self.dSize];
    for (size_t index = 0; index < self.dSize; index++)
        self.pData[index] = src.pData[index];
    TRACES("operator =");
    return self;
}
// move-Zuweisungsopertor
CData2& CData2::operator = (this CData2& self, CData2&& src)
{
    if (&self == &src)   // keine Zuweisung auf sich selbst!
        return self;
    delete[] self.pData;     // bisherige Daten loeschen
    self.dSize = src.dSize;  // neue Daten uebernehmen
    self.pData = src.pData;  // durch Zuweisung des Zeigers
    src.pData = nullptr;     // WICHTIG!!!!
    TRACES("move-operator =");
    return self;
}
// dtor
CData2::~CData2()
{
    delete[] pData;
    TRACE("<<< dtor");
}
// Ueberladener Indexoperator
int& CData2::operator [] (size_t index)
{
    // size_t ist immer ein unsigned Integerwert
    // und kann damit niemals <0 sein
    if (index >= dSize)
        index = dSize-1;
    return pData[index];
}
// Ausgabe des Datenfeldes
export std::ostream& operator << (std::ostream& out, const CData2& obj)
{
    out << "Daten Objekt " << obj.objNum << ":\t\n";
    for (size_t index = 0; index < obj.dSize; index++)
        out << std::setw(3) << obj.pData[index] << ',';
    out << '\n';
    return out;
}
// space-operator
auto CData2::operator <=> (this const CData2& self, const CData2& op2)
{
    // Zuerst die Groesse vergleichen
    if (auto cmp = self.dSize <=> op2.dSize; cmp != 0)
        return cmp;
    // Nun die Daten vergleichen
    // Aufruf des operators ==
    return (self == op2) <=> true;
}
// Vergleich auf Gleichheit
bool CData2::operator == (const CData2& op2) const
{
    if (dSize != op2.dSize)
        return false;
    // Nun die Daten vergleichen
    bool isEqual = true;
    for (size_t index = 0; index < dSize; index++)
    {
        // Wenn Daten ungleich, Schleife verlassen
        if (pData[index] != op2.pData[index])
        {
            isEqual = false;
            break;
        }
    }
    return isEqual;
}