/*=============================================================================
    Copyright (c) 2001-2011 Joel de Guzman
    http://spirit.sourceforge.net/

    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)
=============================================================================*/
//[reference_includes
#include <boost/spirit/include/qi.hpp>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/repository/include/qi_kwd.hpp>
#include <boost/spirit/repository/include/qi_keywords.hpp>
#include <iostream>
#include <string>
#include <cstdlib>
#include <iterator>
//]

//[reference_test
template <typename P>
void test_parser(
    char const* input, P const& p, bool full_match = true)
{
    using boost::spirit::qi::parse;

    char const* f(input);
    char const* l(f + strlen(f));
    if (parse(f, l, p) && (!full_match || (f == l)))
        std::cout << "ok" << std::endl;
    else
        std::cout << "fail" << std::endl;
}

template <typename P>
void test_phrase_parser(
    char const* input, P const& p, bool full_match = true)
{
    using boost::spirit::qi::phrase_parse;
    using boost::spirit::qi::ascii::space;
    
    char const* f(input);
    char const* l(f + strlen(f));
    if (phrase_parse(f, l, p, space) && (!full_match || (f == l)))
        std::cout << "ok" << std::endl;
    else
        std::cout << "fail" << std::endl;
}
//]

//[reference_test_attr
template <typename P, typename T>
void test_parser_attr(
    char const* input, P const& p, T& attr, bool full_match = true)
{
    using boost::spirit::qi::parse;

    char const* f(input);
    char const* l(f + strlen(f));
    if (parse(f, l, p, attr) && (!full_match || (f == l)))
        std::cout << "ok" << std::endl;
    else
        std::cout << "fail" << std::endl;
}

template <typename P, typename T>
void test_phrase_parser_attr(
    char const* input, P const& p, T& attr, bool full_match = true)
{
    using boost::spirit::qi::phrase_parse;
    using boost::spirit::qi::ascii::space;

    char const* f(input);
    char const* l(f + strlen(f));
    if (phrase_parse(f, l, p, space, attr) && (!full_match || (f == l)))
        std::cout << "ok" << std::endl;
    else
        std::cout << "fail" << std::endl;
}
//]



//[reference_keyword_list_test_data_structure
// Data structure definitions to test the kwd directive
// and the keywords list operator

struct person {
    std::string name;
    int age;
    double size;
    std::vector<std::string> favorite_colors;
   
};

std::ostream &operator<<(std::ostream &os, const person &p)
{
    os<<"Person : "<<p.name<<", "<<p.age<<", "<<p.size<<std::endl;
    std::copy(p.favorite_colors.begin(),p.favorite_colors.end(),std::ostream_iterator<std::string>(os,"\n"));
    return os;
} 

BOOST_FUSION_ADAPT_STRUCT( person,
    (std::string, name)
    (int, age)
    (double, size)
    (std::vector<std::string>, favorite_colors)
)
//]

int
main()
{
    
     // keyword_list
    {
        //[reference_using_declarations_keyword_list
        using boost::spirit::repository::qi::kwd;
        using boost::spirit::qi::inf;
        using boost::spirit::ascii::space_type; 
        using boost::spirit::ascii::char_;
        using boost::spirit::qi::double_;
        using boost::spirit::qi::int_;
        using boost::spirit::qi::rule;
        //]
       
        //[reference_keyword_list_rule_declarations
        rule<const char *, std::string(), space_type> parse_string;
        rule<const char *, person(), space_type> no_constraint_person_rule, constraint_person_rule; 
        
         parse_string   %= '"'> *(char_-'"') > '"';
        //]
        
        //[reference_keyword_list_no_constraint_rule
        no_constraint_person_rule %= 
            kwd("name")['=' > parse_string ] 
          / kwd("age")   ['=' > int_] 
          / kwd("size")   ['=' > double_ > 'm']
          ;
        //]
        

        //[reference_keyword_list
        //`Parsing a keyword list:
        // Let's declare a small list of people for which we want to collect information.
        person John,Mary,Mike,Hellen,Johny;
        test_phrase_parser_attr(
                        "name = \"John\" \n age = 10 \n size = 1.69m "
                        ,no_constraint_person_rule 
                        ,John);  // full in original order
        std::cout<<John;

        test_phrase_parser_attr(
                        "age = 10 \n size = 1.69m \n name = \"Mary\""
                        ,no_constraint_person_rule 
                        ,Mary);  // keyword oder doesn't matter
        std::cout<<Mary;

        test_phrase_parser_attr(
                         "size = 1.69m \n name = \"Mike\" \n age = 10 "
                        ,no_constraint_person_rule
                        ,Mike);  // still the same result
        
        std::cout<<Mike;
        
         /*`The code above will print:[teletype]
        
                Person : John, 10, 1.69
                Person : Mary, 10, 1.69
                Person : Mike, 10, 1.69
         */
        //]

        //[reference_keyword_list_constraint_rule
        /*`The parser definition below uses the kwd directive occurrence constraint variants to 
            make sure that the name and age keyword occur only once and allows the favorite color 
            entry to appear 0 or more times. */
        constraint_person_rule %=
            kwd("name",1)                 ['=' > parse_string ]
          / kwd("age"   ,1)                 ['=' > int_]
          / kwd("size"   ,1)                 ['=' > double_ > 'm']
          / kwd("favorite color",0,inf) [ '=' > parse_string ]
          ;
        //]
         
        //[reference_keyword_list_constraints
      
        // Here all the give constraint are resepected : parsing will succeed.
        test_phrase_parser_attr(
            "name = \"Hellen\" \n age = 10 \n size = 1.80m \n favorite color = \"blue\" \n favorite color = \"green\" "
            ,constraint_person_rule 
            ,Hellen);   
        std::cout<<Hellen;
        
       // Parsing this string will fail because the age and size minimum occurrence requirements aren't met.
       test_phrase_parser_attr(
            "name = \"Johny\"  \n favorite color = \"blue\" \n favorite color = \"green\" "
            ,constraint_person_rule
            ,Johny );
        
        /*`Parsing the first string will succeed but fail for the second string as the 
        occurrence constraints aren't met. This code should print:[teletype]
        
        Person : Hellen, 10, 1.8
        blue
        green
        */
        //]
    }
    
        
    return 0;
}
