// Globales Modul fuer include-Anweisungen
module;
#include <print>
#include <string>
#include <charconv>
#include <fstream>

// Modulnamen exportieren
export module Chart;

constexpr unsigned short NOOFPOS{ 10U };        // Anzahl der Positionen
constexpr unsigned short NOOFENTRIES{ 5U };     // Anzahl der einzulesenden Eintraege
constexpr unsigned short INVALID{ 9999U };      // Merker fuer ungueltige Position

// Klasse fuer einen Hitparadeneintrag
// Die Klasse wird nur intern verwendet und nicht exportiert
class Entry
{
    std::string title;                          // Titel
    std::string artist;                         // Interpret
    unsigned short positions[NOOFPOS];          // Feld mit den Positionen
public:
    bool ReadData (std::ifstream& infile);      // Hitparaden-Eintrag einlesen
    unsigned short GetMaxPosition() const;      // Hoechste Position des Titel
    const std::string& GetTitle() const
    {
        return title;
    }
    const std::string& GetArtist() const
    {
        return artist;
    }
};

// Liest einen Hitparaden-Eintrag ein und speichert ihn ab
// infile: Dateistream mit der Hitparadendatei
// return: true = Einlesen erfolgreich
bool Entry::ReadData (std::ifstream& infile)
{
    std::string pos;        // String mit den Positionen

    // Titel einlesen und bei Fehler abbrechen
    std::getline(infile, title, ';');
    if (infile.eof())
        return false;
    // Es wird davon ausgegangen, dass nach dem Einlesen des Titels
    // die restlichen Daten auch eingelesen werden koennen
    // Interpret einlesen
    std::getline(infile, artist, ';');
    // Alle Positionen in einen String einlesen
    std::getline(infile, pos);

    // Positionen in numerische Werte wandeln
    auto index = 0U;                                // Index in das positions-Feld
    auto stringEnd = pos.c_str() + pos.length();    // char* auf Stringende
    // Erste Position umwandeln
    auto res = std::from_chars(pos.c_str(), stringEnd, positions[index]);

    // Danach alle weiteren Positionen umwandeln
    for (index=1; index<NOOFPOS; index++)
    {
        // Nach dem Aufruf von from_chars() zeigt die Eigenschaft res.ptr
        // auf das naechste Semikolon (daher die +1 bei der Startposition)
        // Ist der String mit dem Positionen abgearbeitet ist die
        // Eigenschaft res.ec ungleich errc(), da keine Daten mehr vorliegen
        res = std::from_chars(res.ptr + 1, stringEnd, positions[index]);
        if (res.ec != std::errc())
            return false;
    }
    return true;    // Einlesen  war erfolgreich, Daten sind gueltig
}

// Sucht nach der hoechsten Position (kleinste Position != 0) im aktuellen
// Hitparaden-Eintrag
unsigned short Entry::GetMaxPosition() const
{
    unsigned short maxPos = INVALID;                // maximale Position

    // Alle Positionen durchlaufen
    for (auto elem : positions)
    {
        // Wenn neue maximale Position gefunden, diese merken
        if ((elem != 0) && (elem < maxPos))
        {
            maxPos = elem;
        }
    }
    // Maximale Position zurueckgeben. Wenn keine gefunden (alle Positionen
    // sind 0), wird INVALID zurueckgeben
    return maxPos;
}

// Klasse fuer die Hitparade exportieren
// -------------------------------------
export class Chart
{
    Entry entries[NOOFENTRIES];     // Hitparaden-Eintraege
public:
    bool ReadChart(const std::string& fname);  // Eintraege einlesen
    void PrintEntries() const;                 // Eintraege ausgeben
};

// Eintraege einlesen
bool Chart::ReadChart [[nodiscard]] (const std::string& fname)
{
    // Datei oeffnen
    std::ifstream infile(fname);
    if (!infile)
        return false;
    // Tabellenkopf einlesen und verwerfen
    entries[0].ReadData(infile);

    // Alle Eintraege durchlaufen
    for (auto& elem : entries)
    {
        // und einlesen. Bei Fehler, Abbruch!
        if (!elem.ReadData(infile))
            return false;
    }
    return true;
}

// Eintraege ausgeben
void Chart::PrintEntries() const
{
    // Alle Eintraege durchlaufen
    for (auto elem : entries)
    {
        // Titel, Interpret und hoechste Position ausgeben
        std::println("{} mit '{}', Pos:{}", elem.GetArtist(),
                     elem.GetTitle(), elem.GetMaxPosition());
   }
}
