/*
Przykład użycia biblioteki boost::spirit.qi do tworzenia parserów
Autor: Marcel Zawadzki
Data: 11.01.2024

Biblioteka Boost.Spirit.Qi umożliwia tworzenie parserów dla różnych rodzajów danych. Możliwe jest
zarówno testowanie dopasowania do wzorca, jak i pozyskanie danych zawertych we wzorcu.

Uruchomienie wymaga zainstalowanych bibliotek Boost.
Aby zbudować program na systemie Linux z zainstalowanymi bibliotekami Boost można skorzystać z następującej komendy:
g++ -o qi_demo qi_demo.cpp -lboost_system

Źródła:
- Dokumentacja Boost.Spirit.Qi: https://www.boost.org/doc/libs/1_78_0/libs/spirit/doc/html/index.html
- Artykuł o Boost.Spirit.Qi: https://medium.com/@alinakipoglu/parsing-with-spirit-qi-fcaeaf4357b3
*/


#include <iostream>
#include <vector>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/stl.hpp>
#include <boost/phoenix/operator.hpp>
#include <cmath>

using namespace std;

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

// Definicje parserów

// Parser dla liczb zmiennoprzecinkowych
template <typename Iterator>
bool parseDouble(Iterator first, Iterator last, double& n)
{
    int a = 0;
    int b = 0;
    n = 0.0;
    bool r = qi::phrase_parse(first, last,
    //  Gramatyka parsera
    (
        qi::int_[phoenix::ref(a) = qi::_1] >>   //liczba całkowita przekazana do referencji
        "," >>                                     //znak
        qi::int_[phoenix::ref(b) = qi::_1]
        |                                          //lub
        qi::double_[phoenix::ref(n) = qi::_1]   //liczba zmiennoprzecinkowa przekazana do referencji
    ),
    ascii::space);

    //sprawdzenie czy wykryto liczbę zmiennoprzecinkową z przecinkiem
    if (b != 0) {
        n = (double) a + (double) b  / pow (10, ceil( log10 ( b ) ) );
    }

    // Sprawdzenie czy dopasowanie jest pełne
    if (!r || first != last)
        return false;
    else
        return r;
}


// Parser dopasujący wzorzec tekstu
template <typename Iterator>
bool parseTextPattern(Iterator first, Iterator last)
{
    bool r = qi::phrase_parse(first, last,
    // Parser grammar
    (
    "start" >>
    +(ascii::alpha) >>      //1 lub więcej litera
    qi::double_
    ),
    ascii::space);

    // Sprawdzenie czy dopasowanie jest pełne
    if (!r || first != last)
        return false;
    else
        return r;
}


// Parser do komendy rysującej linię w Postscript
template <typename Iterator>
bool parseCommand(Iterator first, Iterator last, vector<double>& v)
{
    bool r = qi::phrase_parse(first, last,
        //  Gramatyka parsera
        (
        qi::double_[phoenix::push_back(phoenix::ref(v), qi::_1)] >>  //liczba zmiennoprzecinkowa dodana do wektora
        qi::double_[phoenix::push_back(phoenix::ref(v), qi::_1)] >>
        'l'
        ),
        ascii::space);

    // Sprawdzenie czy dopasowanie jest pełne
    if (!r || first != last)
        return false;
    else
        return r;
}


// Parser do listy liczb
template <typename Iterator>
bool parseDoubleList(Iterator first, Iterator last, vector<double>& v)
{
    bool r = qi::phrase_parse(first, last,
            //  Gramatyka parsera
    (
    qi::double_[phoenix::push_back(phoenix::ref(v), qi::_1)] % ','  //lista separowana znakiem
    ),
    ascii::space);

    // Sprawdzenie czy dopasowanie jest pełne
    if (!r || first != last)
        return false;
    else
        return r;
}



int main()
{
    // Przykład 1: Parsowanie liczby zmiennoprzecinkowej
    vector<string> testStrings = {"1.23", "1,23", "123", "1 23"};
    for (string str : testStrings) {
        double doubleVal;
        bool isParsed = parseDouble(str.begin(), str.end(), doubleVal);
        cout << "Parse (" << str << ") = " << isParsed << ", " << doubleVal << endl;
    }

    cout << endl;

    // Przykład 2: Dopasowanie tekstu
    vector<string> testCommands = {"start sensor 25", "kill sensor 25", "start engine"};
    for (string str : testCommands) {
        bool isMatchedPattern = parseTextPattern(str.begin(), str.end());
        cout << "Parse (" << str << ") = " << isMatchedPattern << endl;
    }

    cout << endl;

    // Przykład 3: Parsowanie komendy w Postscript
    string testString = "200.1 120.3 l";
    vector<double> vectorVal;
    bool isParsed = parseCommand(testString.begin(), testString.end(), vectorVal);
    cout << "Parse (" << testString << ") = " << isParsed;
    for (double val : vectorVal) {
        cout << ", " << val;
    }
    cout << endl;

    cout << endl;

    // Przykład 4: Parsowanie listy liczb
    string doubleListString = "1.23, 4.56, 7.89";
    vector<double> doubleList;
    bool isParsedList = parseDoubleList(doubleListString.begin(), doubleListString.end(), doubleList);
    cout << "Parse List (" << doubleListString << ") = " << isParsedList << endl;
    cout << "Parsed values: ";
    for (double val : doubleList) {
        cout << val << " ";
    }

    cout << endl;




    return 0;
}