
module;
#include <fstream>
#include <string>
#include <map>
#include <format>
#include <utility>
#include <print>
#include <vector>
#include <algorithm>
#include <ranges>

export module ChartList;
using namespace std::string_literals;

// Funktion zur Umwandlung in Kleinbuchstaben
void ConvertLower(std::string& text)
{
    for (auto& elem: text)
        elem = std::tolower(elem);
};

export class ChartList
{
    // Map zum Einlesen der Jahres-Hitparade
    // Schluessel ist pair<Titel,Interpret> und
    // Datum das Feld mit den einzelnen Platzierungen in den Jahren
    // Es wird eine Map benoetigt um doppelte Titel/Interprete beim
    // Einlesen der Daten zu verhindern
    std::map<std::pair<std::string,std::string>,
             std::vector<unsigned short>> yearsChart;
    // Vektor mit den Jahreszahlen aus der Kopfzeile der Jahres-Hitparade
    std::vector<std::string> years;
    // Anzahl der Jahre
    size_t noYears;
    // Anzahl der eingelesenen Titel
    unsigned short noTitle;
public:
    ChartList()
    {
        noYears = 0;        // Alle Zaehler zuruecksetzen
        noTitle = 0;
    }
    // Einlesen der Jahres-Hitparade
    bool Read(std::string_view fileName);
    // Anzahl der Jahre und Titel zurueckgeben
    std::pair<size_t, unsigned short> GetSize() const
    {
        return std::pair(noYears, noTitle);
    }
    // !!! Hier die Erweiterungen eingefuegen !!!
    void SearchMinMaxPos(int maxEval) const noexcept;
    void SearchTitle(const char8_t letter) const noexcept;
};

// **** Einlesen der Jahres-Hitparade ****
// fileName enthaelt den Dateinamem mit den Jahres-Hitparade
bool ChartList::Read(std::string_view fileName)
{
    // Datei mit der Jahres-Hitparade oeffnen
    std::ifstream infile(fileName.data());
    if (!infile)
        throw std::format("Kann Datei nicht oeffnen!");

    // 1. Zeile enthaelt Spaltenueberschriften und damit auch die Anzahl der
    // Jahre in der Jahres-Hitparade
    std::string line;
    std::getline(infile, line);

    // Jahreszahlen aus eingelesener 1. Zeile extrahieren
    // und im Vektor years ablegen
    auto pos = line.find(';') + 1;// Titel und Interpret ueberspringen
    pos = line.find(';', pos) + 1;

    // Nun alle Zeichen zwischen 2 Semikolon extrahieren bis zum Zeilenende
    size_t epos;
    do
    {
        // Suche ab aktueller Stelle nach dem naechsten Semikolon
        epos = line.find(';', pos);
        // Sub-String mit Jahresangabe ablegen
        years.push_back(line.substr(pos, epos - pos));
        pos = epos + 1;
    } while (epos != std::string::npos);

    // Alle Titel, Interpret und dessen Platzierungen einlesen
    std::string title, inter;                   // Titel und Interpret
    noYears = years.size();                     // Anzahl der Jahre in der Hitparade
    // Vektor zum Einlesen der Platzierungen
    std::vector<unsigned short> chartPos(noYears);

    do
    {
        // Titel und Interpret einlesen
        std::getline(infile, title, ';');
        if (!infile.eof())
        {
            // alles in Kleinbuchstaben wandeln
            ConvertLower(title);
            std::getline(infile, inter, ';');
            ConvertLower(inter);
            // Alle Jahresplatzierungen einlesen
            for (int iyear = 0; iyear < noYears; iyear++)
            {
                infile >> chartPos.at(iyear);
                infile.ignore(1);   // Semikolon ueberspringen
            }
            // Titel und Interpret zusammenfuegen und Datensatz in Map ablegen
            auto res = yearsChart.try_emplace({ title, inter }, chartPos);
            if (!res.second)
            {
                infile.close();
                throw std::format("Fehler beim Ablegen von {}, {}\n", title, inter);
            }
            noTitle++;
        }
    } while (!infile.eof());
    // Datei mit den Charts schliessen
    infile.close();
    return true;
}

void ChartList::SearchMinMaxPos(int maxEval) const noexcept
{
    int counter = 0;        // Titelzaehler

    // Alle Eintraege durchlaufen
    for (const auto& elem: yearsChart)
    {
        if (counter++ == maxEval)
            break;
        std::print("{} von {}: ", elem.first.first, elem.first.second);
        auto [min, max] = std::ranges::minmax(elem.second |
            std::views::filter(
                [](unsigned short pos)
                { return pos > 0; }
            )
            );
        std::println("Hoechste={}, Niedrigste={}", min, max);
    }
}
void ChartList::SearchTitle(const char8_t letter) const noexcept
{
    int counter = 0;
    auto searchPos = yearsChart.begin();
    while (searchPos != yearsChart.end())
    {
        searchPos = std::find_if(searchPos, yearsChart.end(),
            [=](const auto& elem)
            { return elem.first.first[0] == letter; }
        );
        if (searchPos != yearsChart.end())
        {
            std::println("Titel {} von {}", searchPos->first.first, searchPos->first.second);
            searchPos++;
            counter++;
        }
    };
    std::println("{} Titel gefunden.", counter);
}
