/*
 * ZPR - zadanie dodatkowe
 * autor: Jakub Sadowski
 *
 * Kompilator MinGW 11.0
 *
 * Nowe udogodnienia dostępne w standardzie C++20 - Biblioteka <format>
 * C++20 wprowadził nową bibliotekę <format>, która ułatwia formatowanie tekstu w sposób bezpieczny, wydajny i wygodny.
 * Poniżej zamieszczona została przykładowa aplikację demonstrującą funkcje tej biblioteki oraz porównanie wydajności
 * z innymi popularnymi metodami formatowania.
 */

#include <iostream>
#include <format>
#include <string>
#include <sstream>
#include <chrono>
#include <cstdio>



int main() {
    /// dostępne funkcje formatowania

    // Formatowanie tekstu z użyciem std::format

    int x = 42;
    float y = 21.37;
    std::string przykl = "Przykładowy";

    // std::format
    // Funkcja std::format umożliwia bezpieczne i wygodne formatowanie ciągów znaków w stylu podobnym do języka Python,
    // eliminując problemy związane z tradycyjnymi metodami formatowania, takimi jak printf czy sprintf.
    std::string message = std::format("{} {} {}{} {} ", przykl, "tekst z liczbami:", x, ",", y, x);
    std::cout << message << std::endl;

    // Użycie indeksów
    message = std::format("{3} tekst z liczbami za pomocą indeksów: {1}, {0}, {2}", 1, 2, 3, przykl);
    std::cout << message << std::endl;

    // Formatowanie wstawianych danych
    message = std::format("{0:.5s}. {1} {2:.1f}, {3:1d} ", przykl, "tekst z liczbami:", y, x);
    std::cout << message << std::endl;

    // Formatowanie szerokości i wyrównania
    message = std::format("{0:6}, {0:*<6}, {0:*>6}, {0:*^6}", 'x');
    std::cout << message << std::endl;

    // Formatowanie znaków specjalnych
    message = std::format("{0:},{0:+},{0:-},{1: }, {1:+}, {1:-}", 1, -1);
    std::cout << message << std::endl;

    // Formatowanie za pomocą std::format_to

    // format_to
    // Ta funkcja umożliwia bezpośrednie i efektywne formatowanie danych do określonego miejsca w pamięci, eliminując
    // potrzebę tworzenia dodatkowych obiektów.

    std::string buffer = "Liczba ";
    auto it = std::back_inserter(buffer);
    it = format_to(it, "za pomocą format_to: {}", 42);
    std::cout << buffer << std::endl;

    // Formatowanie z użyciem make_format_args i vformat

    // make_format_args
    // Służy ona do tworzenia obiektu std::format_args, który reprezentuje zestaw argumentów do sformatowania.
    auto args = std::make_format_args(69, "nazwa");

    // vformat
    // Wykożystywana jest do dynamicznego formatowania ciągów znaków w czasie wykonania.

    message = vformat("1:{}, 2:{}", args);
    std::cout << message << std::endl;

    /// Porównanie wydajności std::format z dwoma innymi popularnymi rozwiązaniami: printf i stringstream

    int MAX_ITER = 7000000;
    std::chrono::time_point<std::chrono::high_resolution_clock> start;
    std::chrono::time_point<std::chrono::high_resolution_clock> end;
    std::chrono::duration<double> duration;

    // Pomiar czasu dla std::format
    start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < MAX_ITER; ++i) {
        std::string result = std::format("Hello, {}! To jest numer: {}", "world", i);
    }
    end = std::chrono::high_resolution_clock::now();
    duration = end - start;
    std::cout << "std::format duration: " << duration.count() << " seconds\n";

    // Pomiar czasu dla sprintf

    start = std::chrono::high_resolution_clock::now();

    for (int i = 0; i < MAX_ITER; ++i) {
        char buf[50];
        std::sprintf(buf, "Hello, %s! To jest numer: %d", "world", i);
        std::string result(buf);
    }

    end = std::chrono::high_resolution_clock::now();
    duration = end - start;
    std::cout << "sprintf duration: " << duration.count() << " seconds\n";

    // Pomiar czasu dla stringstream

    start = std::chrono::high_resolution_clock::now();

    for (int i = 0; i < MAX_ITER; ++i) {
        std::stringstream ss;
        ss << "Hello, " << "world" << "! To jest numer: " << i;
        std::string result = ss.str();
    }

    end = std::chrono::high_resolution_clock::now();
    duration = end - start;
    std::cout << "stringstream duration: " << duration.count() << " seconds\n";

    // Pomiar czasu dla format_to
    start = std::chrono::high_resolution_clock::now();

    for (int i = 0; i < MAX_ITER; ++i) {
        std::string result;
        format_to(back_inserter(result), "Hello, {}! To jest numer: {}", "world", i);
    }

    end = std::chrono::high_resolution_clock::now();
    duration = end - start;
    std::cout << "format_to duration: " << duration.count() << " seconds\n";

    // Pomiar czasu dla vformat
    start = std::chrono::high_resolution_clock::now();

    for (int i = 0; i < MAX_ITER; ++i) {
        std::string result;
        auto args = std::make_format_args("world", i);
        result = vformat("Hello, {}! To jest numer: {}", args);
    }

    end = std::chrono::high_resolution_clock::now();
    duration = end - start;
    std::cout << "vformat duration: " << duration.count() << " seconds\n";

    /*
     * Wnioski
     *
     *Testy przeprowadzone zostały dla MinGW 11 oraz MSVC 14.30
     * Z wyników otrzymanych podczas testowania wydajności wynika że,  sprintf działa najszybciej.
     * Funkcje format, format_to oraz vformat dla MSVC działają również szybko. Najgożej wypadł stringstream.
     * Natomiast dla MinGW stringstream działał w bliżony sposób do sprintf. Funkcje format oraz vformat potrzebowały
     * zauważalnie więcej czasu. Natomiast komenda format_to działała bardzo długo.
     */

    return 0;
}


//   Odnośniki do materiałów
// - Strona na cppreference.com dotycząca biblioteki <format>:
//   https://en.cppreference.com/w/cpp/utility/format/format
// - Artykuł na geeksforgeeks.org o C++20 std::format:
//   https://www.geeksforgeeks.org/cpp-20-std-format/