/*=============================================================================
    Copyright (c) 2001-2015 Joel de Guzman
    Copyright (c) 2025 Nana Sakisaka

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/

#include "test.hpp"

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/at.hpp>
#include <boost/variant.hpp>

#include <iterator>
#include <string>
#include <cstring>
#include <iostream>

namespace x3 = boost::spirit::x3;

namespace {
int got_it = 0;
}

struct my_rule_class
{
    //template <std::forward_iterator It, std::sentinel_for<It> Se, typename Exception, typename Context>
    //x3::error_handler_result
    //on_error(It const&, Se const& last, Exception const& x, Context const&)
    template <std::forward_iterator It, std::sentinel_for<It> Se, typename Exception, typename Context>
    x3::error_handler_result
    on_error(It const&, Se const& last, Exception const& x, Context const&)
    {
        std::cout
            << "Error! Expecting: "
            << x.which()
            << ", got: \""
            << std::string(x.where(), last)
            << "\""
            << std::endl
            ;
        return x3::error_handler_result::fail;
    }

    template <std::forward_iterator It, std::sentinel_for<It> Se, typename Attribute, typename Context>
    void
    on_success(It const&, Se const&, Attribute&, Context const&)
    {
        ++got_it;
    }
};

struct on_success_gets_preskipped_iterator
{
    static bool ok;

    template <std::forward_iterator It, std::sentinel_for<It> Se, typename Attribute, typename Context>
    void on_success(It before, Se const& after, Attribute&, Context const&)
    {
        bool const before_was_b = 'b' == *before;
        ok = before_was_b && (++before == after);
    }
};
bool on_success_gets_preskipped_iterator::ok = false;


int main()
{
    using spirit_test::test_attr;
    using spirit_test::test;

    using namespace boost::spirit::x3::standard;
    using boost::spirit::x3::rule;
    using boost::spirit::x3::int_;
    using boost::spirit::x3::lit;

    // show that ra = rb and ra %= rb works as expected
    {
        rule<class a, int> ra;
        rule<class b, int> rb;
        int attr;

        auto ra_def = (ra %= int_);
        BOOST_TEST(test_attr("123", ra_def, attr));
        BOOST_TEST(attr == 123);

        auto rb_def = (rb %= ra_def);
        BOOST_TEST(test_attr("123", rb_def, attr));
        BOOST_TEST(attr == 123);

        auto rb_def2 = (rb = ra_def);
        BOOST_TEST(test_attr("123", rb_def2, attr));
        BOOST_TEST(attr == 123);
    }

    // show that ra %= rb works as expected with semantic actions
    {
        rule<class a, int> ra;
        rule<class b, int> rb;
        int attr;

        auto f = [](auto&){};
        auto ra_def = (ra %= int_[f]);
        BOOST_TEST(test_attr("123", ra_def, attr));
        BOOST_TEST(attr == 123);

        auto ra_def2 = (rb = (ra %= int_[f]));
        BOOST_TEST(test_attr("123", ra_def2, attr));
        BOOST_TEST(attr == 123);
    }


    // std::string as container attribute with auto rules
    {
        std::string attr;

        // test deduced auto rule behavior

        auto text = rule<class text_id, std::string>()
            = +(!char_(')') >> !char_('>') >> char_);

        attr.clear();
        BOOST_TEST(test_attr("x", text, attr));
        BOOST_TEST(attr == "x");
    }

    // error handling
    {
        auto r = rule<my_rule_class, char const*>()
            = '(' > int_ > ',' > int_ > ')';

        BOOST_TEST(test("(123,456)", r));
        BOOST_TEST(!test("(abc,def)", r));
        BOOST_TEST(!test("(123,456]", r));
        BOOST_TEST(!test("(123;456)", r));
        BOOST_TEST(!test("[123,456]", r));

        BOOST_TEST(got_it == 1);
    }

    // on_success gets pre-skipped iterator
    {
        auto r = rule<on_success_gets_preskipped_iterator, char const*>()
            = lit("b");
        BOOST_TEST(test("a b", 'a' >> r, lit(' ')));
        BOOST_TEST(on_success_gets_preskipped_iterator::ok);
    }

    {
        using v_type = boost::variant<double, int>;
        auto r1 = rule<class r1_id, v_type>()
            = int_;
        v_type v;
        BOOST_TEST(test_attr("1", r1, v) && v.which() == 1 &&
            boost::get<int>(v) == 1);

        using ov_type = boost::optional<int>;
        auto r2 = rule<class r2_id, ov_type>()
            = int_;
        ov_type ov;
        BOOST_TEST(test_attr("1", r2, ov) && ov && boost::get<int>(ov) == 1);
    }

    // test handling of single element fusion sequences
    {
        using boost::fusion::vector;
        using boost::fusion::at_c;
        auto r = rule<class r_id, vector<int>>()
            = int_;

        vector<int> v(0);
        BOOST_TEST(test_attr("1", r, v) && at_c<0>(v) == 1);
    }

    // attribute compatibility test
    {
        constexpr auto expr = int_;

        long long i = 0;
        BOOST_TEST(test_attr("1", expr, i) && i == 1);

        constexpr rule<class int_rule, int> int_rule("int_rule");
        constexpr auto int_rule_def = int_;
        constexpr auto start = int_rule = int_rule_def;

        long long j = 0;
        BOOST_TEST(test_attr("1", start, j) && j == 1);
    }

    return boost::report_errors();
}
