/**
 * ======================================================================================
 * TEMAT: std::source_location (C++20) jako nowoczesna alternatywa dla makr preprocesora
 * * DOKUMENTACJA:
 * Zgodnie z dobrymi praktykami programowania w C++, należy unikać stosowania 
 * preprocesora na rzecz mechanizmów wbudowanych w język. Do C++20 tworzenie systemów 
 * logowania wymagało użycia makr (__FILE__, __LINE__, __func__), aby system mógł 
 * automatycznie rozpoznać miejsce wywołania w kodzie źródłowym.
 * * Standard C++20 rozwiązuje ten problem klasą `std::source_location`. 
 * Udostępnia ona metadane o kodzie źródłowym w sposób obiektowy i bezpieczny typologicznie. 
 * Kiedy użyjemy `std::source_location::current()` jako domyślnego argumentu funkcji, 
 * kompilator wstrzykuje informacje o miejscu WYWOŁANIA tej funkcji (a nie jej definicji).
 * * W poniższej aplikacji zaimplementowano nowoczesny Logger, który łączy `source_location`
 * ze strumieniowaniem za pomocą wprowadzonych w C++20 mechanizmów formatowania 
 * (`std::format`), całkowicie eliminując potrzebę definiowania makr.
 * * ODNOŚNIKI DO MATERIAŁÓW:
 * 1. Cppreference: https://en.cppreference.com/w/cpp/utility/source_location
 * 2. Propozycja standardu (P1208R6): https://wg21.link/p1208r6
 * 3. std::format w C++20: https://en.cppreference.com/w/cpp/utility/format/format
 * ======================================================================================
 */

#include <iostream>
#include <string_view>
#include <source_location>
#include <vector>
#include <format> // Wymaga kompilatora obsługującego std::format (np. GCC 13+, MSVC 19.29+)

class Logger {
public:
    enum class Level { INFO, WARNING, ERROR };

    // Główna funkcja logująca
    static void log(std::string_view message, 
                    Level level = Level::INFO, 
                    const std::source_location& loc = std::source_location::current()) 
    {
        // Użycie std::format (C++20) do eleganckiego ułożenia ciągów znaków.
        // Format: [POZIOM] plik:linia:kolumna `funkcja`: Wiadomość
        std::cout << std::format("{}[{}] {}:{}:{} `{}`: {}{}\n",
                                 getColorCode(level),
                                 levelToString(level),
                                 loc.file_name(),
                                 loc.line(),
                                 loc.column(),
                                 loc.function_name(),
                                 message,
                                 resetColorCode());
    }

private:
    // Konwersja na std::string_view – bez alokacji pamięci w czasie działania
    static constexpr std::string_view levelToString(Level level) {
        switch (level) {
            case Level::INFO:    return "INFO";
            case Level::WARNING: return "WARN";
            case Level::ERROR:   return "ERROR";
            default:             return "UNKN";
        }
    }

    // Proste formatowanie ANSI dla terminali
    static constexpr std::string_view getColorCode(Level level) {
        switch (level) {
            case Level::INFO:    return "\033[36m"; // Cyan
            case Level::WARNING: return "\033[33m"; // Żółty
            case Level::ERROR:   return "\033[31m"; // Czerwony
            default:             return "\033[0m";
        }
    }

    static constexpr std::string_view resetColorCode() {
        return "\033[0m";
    }
};

// Przykładowa funkcja biznesowa symulująca przetwarzanie danych
void processData(const std::vector<int>& data) {
    Logger::log("Rozpoczecie przetwarzania danych...");

    if (data.empty()) {
        // Zgłoszenie błędu - wstrzyknie aktualną linię i wskaże na funkcję `processData`
        Logger::log("Otrzymano pusty kontener danych! Przetwarzanie przerwane.", Logger::Level::ERROR);
        return;
    }

    // Symulacja ryzykownej operacji
    if (data.size() > 1000) {
        Logger::log("Duzy zbior danych. Moze to potrwac dluzej niz zwykle.", Logger::Level::WARNING);
    }

    Logger::log("Przetwarzanie zakonczone sukcesem.");
}

int main() {
    std::cout << "--- Nowoczesne logowanie: C++20 std::source_location + std::format ---\n\n";

    Logger::log("Uruchomienie aplikacji w funkcji main.");

    std::vector<int> empty_dataset;
    processData(empty_dataset);

    std::vector<int> large_dataset(1500);
    processData(large_dataset);

    return 0;
}