#include <print>
#include <fstream>
#include <string>
#include <stack>
#include <utility>

// XML Tag: Zeilennummer und Tag-Name
using data = std::pair<unsigned int,std::string>;
// Stack zur Ablage der noch offenen XML-Tags
std::stack<data> xmlParse;

const char* FILENAME = "../../04_Data/test.xml";

// main() Funktion
int main()
{
    // Datei zum Lesen oeffnen
    std::ifstream inFile(FILENAME);
    if (!inFile)
    {
        std::println("Datei {} konnte nicht geoeffnet werden!", FILENAME);
        exit(1);
    }

    // Zeilenzaehler und eingelesene Zeile
    unsigned int lineCount = 2;
    std::string line;

    // 1. Zeile ueberspringen, enthaelt xml-Version
    std::getline(inFile, line);
    try
    {
        // Datei durchlaufen
        do
        {
            // Zeile einlesen
            std::getline(inFile,line);
            // Dateiende erreicht?
            if (inFile.eof())
                continue;

            // XML-Tag Anfang pruefen
            auto openPar = line.find('<');
            // Wenn keine XML-Tag gefunden, Fehler
            if (openPar == std::string::npos)
                throw data(lineCount, "XML Tag nicht gefunden");
            // XML-Tag am Zeilenende pruefen
            if (line[line.size()-1] != '>')
                throw data(lineCount,"Fehlerhafter XML-Tag");

            // Pruefe ob schliessender XML-Tag </...>
            if (line[openPar+1] == '/')
            {
                // Dazugehoeriges oeffnendes XML-Tag vom Stack
                if (xmlParse.size() == 0)
                    throw data(lineCount,"Oeffnender XML-Tag nicht gefunden");
                auto openXML = xmlParse.top();
                xmlParse.pop();
                // Oeffnenden und schliessenden XML-Tag vergleichen
                if (line.compare(openPar+2,line.size()-openPar-3,openXML.second) != 0)
                    throw data(openXML.first,"Schliessender XML-Tag nicht gefunden");
            }
            else
            {
                // XMl-Tag schliesst selbst
                // Ueberspringen, da immer gueltig
                if (line[line.size()-2] != '/')
                {
                    // Extrahiere XML-Tag
                    auto xmlEnd = line.find_first_of("> ",openPar);
                    if (xmlEnd == std::string::npos)
                        throw data(lineCount,"Fehlerhafter XML-Tag");
                    auto xmlTag = line.substr(openPar+1,xmlEnd-openPar-1);
                    // Pruefe ob XML-Tag in gleicher Zeile geschlossen wird
                    if (openPar = line.find('<',openPar+1); openPar != std::string::npos)
                    {
                        auto xmlTag2 = line.substr(openPar+2, line.size()-openPar-3);
                        // XML-Tags nicht identisch? Fehler
                        if (xmlTag != xmlTag2)
                            throw data(lineCount,"Fehlerhafter XML-Tag");
                    }
                    else
                    {
                        // Oeffnender XML-Tag
                        // Zeilennummer und XML-Tag auf Stack
                        xmlParse.push(data(lineCount,xmlTag));
                    }
                }
            }
            // Zeilenzaehler inkrementierenm
            lineCount++;
        } while (!inFile.eof());
        std::println("Keine XML Fehler gefunden!");
    }
    catch (const data& err)
    {
        std::println("Zeile {}: {}", err.first, err.second);
    }

    // Datei schliessen
    inFile.close();
}
