/*=============================================================================
    Copyright (c) 2002 2004  2006Joel de Guzman
    Copyright (c) 2004 Eric Niebler
    http://spirit.sourceforge.net/

    Use, modification and distribution is subject to 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 <boost/spirit/include/classic_assign_actor.hpp>
#include <boost/spirit/include/classic_clear_actor.hpp>
#include <boost/spirit/include/classic_if.hpp>
#include <boost/spirit/include/phoenix1_casts.hpp>
#include <boost/spirit/include/phoenix1_primitives.hpp>
#include "actions.hpp"
#include "block_tags.hpp"
#include "grammar_impl.hpp"
#include "state.hpp"
#include "template_tags.hpp"
#include "utils.hpp"

namespace quickbook
{
    namespace cl = boost::spirit::classic;
    namespace ph = phoenix;

    struct block_element_grammar_local
    {
        cl::rule<scanner> heading, inner_block, inner_phrase, def_macro, table,
            table_title, table_row, variablelist, varlistentry, varlistterm,
            list, cell, preformatted, begin_section, end_section, xinclude,
            include, include_id, include_filename, template_, template_id,
            template_formal_arg, template_body, identifier, import, element_id,
            same_line;
    };

    void quickbook_grammar::impl::init_block_elements()
    {
        block_element_grammar_local& local =
            cleanup_.add(new block_element_grammar_local);

        // Actions
        error_action error(state);
        element_id_warning_action element_id_warning(state);
        raw_char_action raw_char(state);
        explicit_list_action explicit_list(state);
        scoped_parser<to_value_scoped_action> to_value(state);

        // clang-format off

        local.element_id =
            !(  ':'
            >>  (   qbk_ver(107u)
                >>  to_value(general_tags::element_id) [attribute_value_1_7]
                |   qbk_ver(0, 107u)
                >>  !(qbk_ver(105u) >> space)
                >>  (+(cl::alnum_p | '_'))      [state.values.entry(ph::arg1, ph::arg2, general_tags::element_id)]
                |   cl::eps_p                   [element_id_warning]
                )
            )
            ;

        elements.add
            ("section", element_info(element_info::section_block, &local.begin_section, block_tags::begin_section))
            ("endsect", element_info(element_info::section_block, &local.end_section, block_tags::end_section))
            ;

        local.begin_section =
                space
            >>  local.element_id
            >>  space
            >>  local.inner_phrase
            ;

        local.end_section =
                space
            >>  local.element_id
            ;

        local.heading
            =   space
            >>  !(qbk_ver(106u) >> local.element_id)
            >>  space
            >>  local.inner_phrase
            ;

        elements.add
            ("heading", element_info(element_info::conditional_or_block, &local.heading, block_tags::generic_heading))
            ("h1", element_info(element_info::conditional_or_block, &local.heading, block_tags::heading1))
            ("h2", element_info(element_info::conditional_or_block, &local.heading, block_tags::heading2))
            ("h3", element_info(element_info::conditional_or_block, &local.heading, block_tags::heading3))
            ("h4", element_info(element_info::conditional_or_block, &local.heading, block_tags::heading4))
            ("h5", element_info(element_info::conditional_or_block, &local.heading, block_tags::heading5))
            ("h6", element_info(element_info::conditional_or_block, &local.heading, block_tags::heading6))
            ;

        elements.add
            ("blurb", element_info(element_info::nested_block, &local.inner_block, block_tags::blurb))
            (":", element_info(element_info::nested_block, &local.inner_block, block_tags::blockquote))
            ("warning", element_info(element_info::nested_block, &local.inner_block, block_tags::warning))
            ("caution", element_info(element_info::nested_block, &local.inner_block, block_tags::caution))
            ("important", element_info(element_info::nested_block, &local.inner_block, block_tags::important))
            ("note", element_info(element_info::nested_block, &local.inner_block, block_tags::note))
            ("tip", element_info(element_info::nested_block, &local.inner_block, block_tags::tip))
            ;

        elements.add
            ("block", element_info(element_info::nested_block, &local.inner_phrase, block_tags::block, 106u))
            ;

        elements.add
            ("pre", element_info(element_info::nested_block, &local.preformatted, block_tags::preformatted))
            ;

        local.preformatted =
                ( qbk_ver(0, 106) >> space
                | qbk_ver(106) >> blank >> !eol
                )
            >>  to_value()
                [
                    inside_preformatted
                ]
            ;

        elements.add
            ("def", element_info(element_info::conditional_or_block, &local.def_macro, block_tags::macro_definition))
            ;

        local.def_macro =
               space
            >> macro_identifier                 [state.values.entry(ph::arg1, ph::arg2)]
            >> blank
            >> local.inner_phrase
            ;

        local.identifier =
            (cl::alpha_p | '_') >> *(cl::alnum_p | '_')
            ;

        local.template_id =
            local.identifier | (cl::punct_p - (cl::ch_p('[') | ']'))
            ;

        elements.add
            ("template", element_info(element_info::conditional_or_block, &local.template_, block_tags::template_definition))
            ;

        local.template_ =
               space
            >> local.template_id                [state.values.entry(ph::arg1, ph::arg2)]
            >> state.values.list()[
            !(
                space >> '['
                >> *(
                        space
                    >>  local.template_id       [state.values.entry(ph::arg1, ph::arg2)]
                    )
                >> space >> ']'
            )
            ]
            >>  (   cl::eps_p(*cl::blank_p >> cl::eol_p)
                >>  local.template_body         [state.values.entry(ph::arg1, ph::arg2, template_tags::block)]
                |   local.template_body         [state.values.entry(ph::arg1, ph::arg2, template_tags::phrase)]
                )
            ;

        local.template_body =
                qbk_ver(106u)
            >>  *(~cl::eps_p(']') >> skip_entity)
            |   qbk_ver(0,106u)
            >>  *(('[' >> local.template_body >> ']') | (cl::anychar_p - ']'))
            >> cl::eps_p(space >> ']')
            >> space
            ;

        elements.add
            ("variablelist", element_info(element_info::nested_block, &local.variablelist, block_tags::variable_list))
            ;

        local.variablelist =
                (cl::eps_p(*cl::blank_p >> cl::eol_p) | space)
            >>  local.table_title
            >>  *local.varlistentry
            ;

        local.varlistentry =
            space
            >>  cl::ch_p('[')
            >>  state.values.list()
            [
                (
                    local.varlistterm
                    >>  (   +local.cell
                        |   cl::eps_p           [error]
                        )
                    >>  cl::ch_p(']')
                    >>  space
                )
                | cl::eps_p                     [error]
            ]
            ;

        local.varlistterm =
            space
            >>  cl::ch_p('[')
            >>  local.inner_phrase
            >>  (   cl::ch_p(']')
                >>  space
                |   cl::eps_p                   [error]
                )
            ;

        elements.add
            ("table", element_info(element_info::nested_block, &local.table, block_tags::table))
            ;

        local.same_line = *cl::blank_p >> !(comment >> space);

        local.table =
                local.same_line
            >>  !(qbk_ver(105u) >> local.element_id)
            >>  local.same_line
            >>  local.table_title
            >>  *local.table_row
            ;

        local.table_row =
            space
            >>  cl::ch_p('[')
            >>
            (
                (
                    state.values.list(table_tags::row)
                    [   *local.cell
                    ]
                    >>  cl::ch_p(']')
                    >>  space
                )
                | cl::eps_p                     [error]
            )
            ;

        local.table_title =
                qbk_ver(0, 106)
            >>  (*(cl::anychar_p - eol))        [state.values.entry(ph::arg1, ph::arg2, table_tags::title)]
            >>  (+eol)
            |   qbk_ver(106)
            >>  to_value(table_tags::title)
                [
                    table_title_phrase
                ]
            >>  space
            ;

        elements.add
            ("ordered_list", element_info(element_info::nested_block, &local.list, block_tags::ordered_list, 106))
            ("itemized_list", element_info(element_info::nested_block, &local.list, block_tags::itemized_list, 106))
            ;

        local.list =
            *(  cl::eps_p                       [explicit_list]
            >>  local.cell
            )
            ;

        local.cell =
                space
            >>  cl::ch_p('[')
            >>  (   local.inner_block
                >>  cl::ch_p(']')
                >>  space
                |   cl::eps_p                   [error]
                )
            ;

        elements.add
            ("xinclude", element_info(element_info::conditional_or_block, &local.xinclude, block_tags::xinclude))
            ("import", element_info(element_info::conditional_or_block, &local.import, block_tags::import))
            ("include", element_info(element_info::conditional_or_block, &local.include, block_tags::include))
            ;

        local.xinclude =
               space
            >> local.include_filename
            ;

        local.import =
               space
            >> local.include_filename
            ;

        local.include =
               space
            >>
           !(
                ':'
                >> local.include_id
                >> space
            )
            >> local.include_filename
            ;

        local.include_id =
            (*((cl::alnum_p | '_') - cl::space_p))
                                                [state.values.entry(ph::arg1, ph::arg2, general_tags::include_id)];

        local.include_filename =
                qbk_ver(0, 106u)
            >>  (*(cl::anychar_p - phrase_end)) [state.values.entry(ph::arg1, ph::arg2)]
            |   qbk_ver(106u, 107u)
            >>  to_value()
                [   *(  raw_escape
                    |   (cl::anychar_p - phrase_end)
                                                [raw_char]
                    )
                ]
            |   qbk_ver(107u)
            >>  to_value() [ attribute_value_1_7 ]
            ;

        local.inner_block =
            to_value()
            [
                inside_paragraph
            ]
            ;

        local.inner_phrase =
            to_value()
            [
                paragraph_phrase
            ]
            ;

        // clang-format on
    }
}
