// Boost.Geometry (aka GGL, Generic Geometry Library)
//
// Copyright (c) 2010-2015 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2012-2015 Adam Wulkiewicz, Lodz, Poland.
//
// This file was modified by Oracle on 2014-2020.
// Modifications copyright (c) 2014-2020, Oracle and/or its affiliates.
// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
//
// 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)
//
//
#ifndef QUICKBOOK_OUTPUT_HPP
#define QUICKBOOK_OUTPUT_HPP


#include <string>
#include <vector>

#include <boost/algorithm/string.hpp>

#include <doxygen_elements.hpp>
#include <parameter_predicates.hpp>

std::string qbk_escaped(std::string const& s)
{
    // Replace _ by unicode to avoid accidental quickbook underlining.
    // 1) do NOT do this in quickbook markup, so not within []
    // (e.g. to avoid [include get_point.qbk] having unicoded)
    // 2) \[ and \] should not count as []
    std::size_t const len = s.length();
    int counter = 0;
    std::string result = "";
    for (std::size_t i = 0; i < len; i++)
    {
        switch(s[i])
        {
            case '[' : counter++; break;
            case ']' : counter--; break;
            case '\\' :
                {
                    result += s[i];
                    if (i + 1 < len)
                    {
                        result += s[i + 1];
                    }
                    i++;
                    continue;
                }
            case '_' :
                if (counter == 0)
                {
                    result += "\\u005f";
                    continue;
                }
        }
        result += s[i];
    }

    return result;
}


inline void next_item(std::string const& first, std::string const& indent,
                std::size_t items_per_line,
                std::size_t& index, std::ostream& out)
{
    if (index > 0)
    {
        if (index % items_per_line == 0)
        {
            out << "," << std::endl << indent;
        }
        else
        {
            out << ", ";
        }
    }
    else
    {
        std::cout << first;
    }
    index++;
}

void quickbook_template_parameter_list(std::vector<parameter> const& parameters,
                std::string const& related_name,
                std::ostream& out)
{
    if (!parameters.empty())
    {
        std::string const header = "template<";
        std::size_t index = 0;
        std::string const indent(header.length(), ' ');
        out << header;
        BOOST_FOREACH(parameter const& p, parameters)
        {
            if (p.fulltype.empty())
            {
                std::cerr << "Warning: template parameter " << p.name << " has no type in " << related_name << std::endl;
            }

            next_item("", indent, 4, index, out);
            out << p.fulltype;
        }
        out << ">" << std::endl;
    }
}


void quickbook_synopsis(function const& f, std::ostream& out)
{
    out << "``";
    quickbook_template_parameter_list(f.template_parameters, f.name, out);

    switch(f.type)
    {
        case function_constructor_destructor :
            out << f.name;
            break;
        case function_member :
            out << f.return_type << " " << f.name;
            break;
        case function_free :
            out << f.definition;
            break;
        case function_define :
            out << "#define " << f.name;
            break;
        case function_unknown :
            // do nothing
            break;
    }

    // Output the parameters
    // Because we want to be able to skip, we cannot use the argstring
    {
        std::size_t index = 0;
        std::string const indent(f.name.length() + f.return_type.length() + 2, ' ');
        BOOST_FOREACH(parameter const& p, f.parameters)
        {
            if (! p.skip)
            {
                next_item("(", indent, 3, index, out);

                out << p.fulltype << (p.fulltype.empty() ? "" : " ")
                    << p.name
                    << (p.default_value.empty() ? "" : " = ")
                    << p.default_value;
            }
        }
        if (index > 0)
        {
            out << ")";
        }
        else if (f.type != function_define)
        {
            out << "()";
        }
    }

    out << "``"
        << std::endl
        << std::endl;
}


void quickbook_synopsis(enumeration const& e, std::ostream& out)
{
    out << "``enum " << e.name;
    bool first = true;

    bool has_many_values = e.enumeration_values.size() > 4;

    BOOST_FOREACH(enumeration_value const& value, e.enumeration_values)
    {
        if (has_many_values)
        {
            if (first)
            {
                out << std::endl << "{";
            }
            else
            {
                out << ",";
            }
            out << std::endl << "    " << value.name;
        }
        else
        {
            out << (first ? " {" : ", ") << value.name;
        }
        if (! value.initializer.empty())
        {
            // Doxygen 1.6 does not include "=" in the <initializer> tag, Doxygen 1.8 does.
            // We just remove the "=" to have consistent output
            out << " = " << boost::trim_copy(boost::replace_all_copy(value.initializer, "=", ""));
        }
        first = false;
    }
    if (! first)
    {
        if (has_many_values)
        {
            out << std::endl;
        }
        out << "};";
    }
    out << "``"
        << std::endl
        << std::endl;
}

inline bool includes(std::string const& filename, std::string const& header)
{
    std::string result;

    std::ifstream cpp_file(filename.c_str());
    if (cpp_file.is_open())
    {
        while (! cpp_file.eof() )
        {
            std::string line;
            std::getline(cpp_file, line);
            boost::trim(line);
            if (boost::starts_with(line, "#include")
                && boost::contains(line, header))
            {
                return true;
            }
        }
    }
    return false;
}


std::string fix_location(std::string const& raw_location)
{
    if ( raw_location.find("detail/") == std::string::npos
         || raw_location.find("/interface.hpp") == std::string::npos )
    {
        return raw_location;
    }

    std::string fixed_location(raw_location);

    fixed_location.erase(fixed_location.find("detail/"), 7u);
    fixed_location.erase(fixed_location.find("/interface"), 10u);

    return fixed_location;
}


std::string fix_include_header(std::string const& header)
{
    if ( header.find("geometry/geometry.hpp") == std::string::npos )
    {
        return header;
    }

    std::string fixed_header(header);

    fixed_header.erase(fixed_header.find("geometry/"), 9u);

    return fixed_header;
}


void quickbook_header(std::string const& raw_location,
    configuration const& config,
    std::ostream& out)
{
    std::string location = fix_location(raw_location);

    if (! location.empty())
    {
        std::vector<std::string> including_headers;

        // Select headerfiles containing to this location
        BOOST_FOREACH(std::string const& header, config.convenience_headers)
        {
            if (includes(config.convenience_header_path + header, location))
            {
                including_headers.push_back(header);
            }
        }

        out << "[heading Header]" << std::endl;
        if (! including_headers.empty())
        {
            out << "Either"
                << (including_headers.size() > 1 ? " one of" : "")
                << std::endl << std::endl;
            BOOST_FOREACH(std::string const& header, including_headers)
            {
                out << "`#include <" << fix_include_header(config.start_include + header) << ">`" << std::endl << std::endl;
            }

            out << std::endl << "Or" << std::endl << std::endl;
        }
        out << "`#include <" << location << ">`" << std::endl;
        out << std::endl;
    }
}


void quickbook_markup(std::vector<markup> const& qbk_markup,
            markup_order_type order, markup_type type,
            std::ostream& out)
{
    bool has_output = false;
    BOOST_FOREACH(markup const& inc, qbk_markup)
    {
        if (inc.type == type && inc.order == order)
        {
            out << inc.value << std::endl;
            has_output = true;
        }
    }
    if (has_output)
    {
        out << std::endl;
    }
}



void quickbook_string_with_heading_if_present(std::string const& heading,
            std::string const& contents, std::ostream& out)
{
    if (! contents.empty())
    {
        out << "[heading " << heading << "]" << std::endl
            << qbk_escaped(contents) << std::endl
            << std::endl;
    }
}

inline std::string to_section_name(std::string const& name)
{
    // Make section-name lowercase and remove :: because these are filenames
    return boost::to_lower_copy(boost::replace_all_copy(name, "::", "_"));
}

void quickbook_output_function_parameters(function const& f, std::ostream& out)
{
    BOOST_FOREACH(parameter const& p, f.parameters)
    {
        if (! p.skip)
        {
            out << "[* " << p.fulltype << "]: ['" << p.name << "]:  " << p.brief_description << std::endl << std::endl;
        }
    }
    out << std::endl;
    out << std::endl;
}

void quickbook_output_function_return(function const& f, std::ostream& out)
{
    if (! f.return_description.empty())
    {
        out << f.return_description << std::endl;
    }

    out << std::endl;
}

inline std::string namespace_skipped(std::string const& name, configuration const& config)
{
    return config.skip_namespace.empty()
        ? name
        : boost::replace_all_copy(name, config.skip_namespace, "")
        ;
}

inline std::string output_if_different(std::string const& s, std::string const& s2)
{
    return boost::equals(s, s2)
        ? ""
        : s + " "
        ;
}

inline void quickbook_output_indexterm(std::string const& term, std::ostream& out
            //, std::string const& secondary = ""
            )
{
    out << "'''";
    if (boost::contains(term, "::"))
    {
        // "Unnamespace" it and add all terms (also namespaces)
        std::vector<std::string> splitted;
        std::string for_split = boost::replace_all_copy(term, "::", ":");
        boost::split(splitted, for_split, boost::is_any_of(":"), boost::token_compress_on);
        BOOST_FOREACH(std::string const& part, splitted)
        {
            out << "<indexterm><primary>" << part << "</primary></indexterm>";
        }
    }
    else
    {
        out << "<indexterm><primary>" << term;
        /*if (! secondary.empty())
        {
            out << "<secondary>" << secondary << "</secondary>";
        }*/
        out << "</primary></indexterm>";
    }
    out << "'''" << std::endl;
}

void quickbook_output(function const& f, configuration const& config, std::ostream& out)
{
    // Write the parsed function
    int arity = (int)f.parameters.size();

    std::string additional_description;

    if (! f.additional_description.empty())
    {
        additional_description = " (";
        additional_description += f.additional_description;
        additional_description += ")";
    }

    out << "[section:" << to_section_name(f.name);
    // Make section name unique if necessary by arity and additional description
    if (! f.unique)
    {
        out << "_" << arity;
        if (! f.additional_description.empty())
        {
            out << "_" << boost::to_lower_copy(boost::replace_all_copy(f.additional_description, " ", "_"));
        }
    }
    out << " " << f.name
        << additional_description
        << "]" << std::endl
        << std::endl;

    quickbook_output_indexterm(f.name, out);

    out << qbk_escaped(f.brief_description) << std::endl;
    out << std::endl;

    quickbook_string_with_heading_if_present("Description", f.detailed_description, out);

    // Synopsis
    quickbook_markup(f.qbk_markup, markup_before, markup_synopsis, out);
    out << "[heading Synopsis]" << std::endl;
    quickbook_synopsis(f, out);
    quickbook_markup(f.qbk_markup, markup_after, markup_synopsis, out);


    out << "[heading Parameters]" << std::endl
        << std::endl;

    out << "[table" << std::endl << "[";
    if (f.type != function_define)
    {
        out << "[Type] [Concept] ";
    }
    out << "[Name] [Description] ]" << std::endl;

    // First: output any template parameter which is NOT used in the normal parameter list
    BOOST_FOREACH(parameter const& tp, f.template_parameters)
    {
        if (! tp.skip)
        {
            std::vector<parameter>::const_iterator it = std::find_if(f.parameters.begin(), f.parameters.end(), par_by_type(tp.name));

            if (it == f.parameters.end())
            {
                out << "[[" << tp.name << "] [" << tp.brief_description << "] [ - ] [Must be specified]]" << std::endl;
            }
        }
    }

    BOOST_FOREACH(parameter const& p, f.parameters)
    {
        if (! p.skip)
        {
            out << "[";
            std::vector<parameter>::const_iterator it = std::find_if(f.template_parameters.begin(),
                f.template_parameters.end(), par_by_name(p.type));

            if (f.type != function_define)
            {
                out << "[" << p.fulltype
                    << "] [" << (it == f.template_parameters.end() ? "" : it->brief_description)
                    << "] ";
            }
            out << "[" << p.name
                << "] [" << p.brief_description
                << "]]"
                << std::endl;
        }
    }
    out << "]" << std::endl
        << std::endl
        << std::endl;

    quickbook_string_with_heading_if_present("Returns", f.return_description, out);

    quickbook_header(f.location, config, out);
    quickbook_markup(f.qbk_markup, markup_any, markup_default, out);

    out << std::endl;
    out << "[endsect]" << std::endl;
    out << std::endl;
}

void quickbook_output(enumeration const& e, configuration const& config, std::ostream& out)
{
    out << "[section:" << to_section_name(e.name);
    out << " " << e.name
        << "]" << std::endl
        << std::endl;

    quickbook_output_indexterm(e.name, out);
    BOOST_FOREACH(enumeration_value const& value, e.enumeration_values)
    {
        quickbook_output_indexterm(value.name, out);
    }

    out << e.brief_description << std::endl;
    out << std::endl;

    quickbook_string_with_heading_if_present("Description", e.detailed_description, out);

    // Synopsis
    quickbook_markup(e.qbk_markup, markup_before, markup_synopsis, out);
    out << "[heading Synopsis]" << std::endl;
    quickbook_synopsis(e, out);
    quickbook_markup(e.qbk_markup, markup_after, markup_synopsis, out);


    out << "[heading Values]" << std::endl
        << std::endl;

    out << "[table" << std::endl << "[";
    out << "[Value] [Description] ]" << std::endl;

    BOOST_FOREACH(enumeration_value const& value, e.enumeration_values)
    {
        out << "[[" << value.name
            << "] [" << value.brief_description
            << "]]"
            << std::endl;
    }
    out << "]" << std::endl
        << std::endl
        << std::endl;

    quickbook_header(e.location, config, out);
    quickbook_markup(e.qbk_markup, markup_any, markup_default, out);

    out << std::endl;
    out << "[endsect]" << std::endl;
    out << std::endl;
}

void quickbook_output_function(std::vector<function> const& functions,
                                function_type type,
                                std::string const& title,
                                configuration const& , std::ostream& out)
{
    std::string returns = type == function_constructor_destructor ? "" : " [Returns]";
    out << "[heading " << title << "(s)]" << std::endl
        << "[table" << std::endl
        << "[[Function] [Description] [Parameters] " << returns << "]" << std::endl;

    BOOST_FOREACH(function const& f, functions)
    {
        if (f.type == type)
        {
            out << "[[";
            quickbook_synopsis(f, out);
            out << "] [" << f.brief_description << "] [";
            quickbook_output_function_parameters(f, out);
            out << "]";
            if ( type != function_constructor_destructor )
            {
                out << "[" << std::endl;
                quickbook_output_function_return(f, out);
                out << "]" << std::endl;
            }
            out << "]" << std::endl;
        }
    }
    out << "]" << std::endl
        << std::endl;
}

void quickbook_output(class_or_struct const& cos, configuration const& config, std::ostream& out)
{
    // Skip namespace
    std::string short_name = namespace_skipped(cos.fullname, config);

    // Write the parsed function
    out << "[section:" << to_section_name(short_name) << " " << short_name << "]" << std::endl
        << std::endl;

    quickbook_output_indexterm(short_name, out);

    out << cos.brief_description << std::endl;
    out << std::endl;

    quickbook_string_with_heading_if_present("Description", cos.detailed_description, out);

    quickbook_markup(cos.qbk_markup, markup_before, markup_synopsis, out);
    out << "[heading Synopsis]" << std::endl
        << "``";
    quickbook_template_parameter_list(cos.template_parameters, cos.name, out);
    out << (cos.is_class ? "class" : "struct")
        << " " << short_name << std::endl;

    if (! cos.base_classes.empty())
    {
        out << "      : ";
        bool first = true;
        BOOST_FOREACH(base_class const& bc, cos.base_classes)
        {
            if (! first)
            {
                out << std::endl << "      , ";
            }
            out << output_if_different(bc.derivation, "private")
                << output_if_different(bc.virtuality, "non-virtual")
                << namespace_skipped(bc.name, config);
            first = false;
        }
        out << std::endl;
    }

    out << "{" << std::endl;
    if (! cos.variables.empty() && config.output_member_variables)
    {
        size_t maxlength = 0;
        BOOST_FOREACH(parameter const& p, cos.variables)
        {
            if (! p.skip)
            {
                size_t length = 6 + p.fulltype.size() + p.name.size();
                if (length > maxlength) maxlength = length;
            }
        }
        BOOST_FOREACH(parameter const& p, cos.variables)
        {
            if (! p.skip)
            {
                size_t length = 4 + p.fulltype.size() + p.name.size();
                out << "  " << p.fulltype << " " << p.name << ";";
                if (! p.brief_description.empty())
                {
                    while(length++ < maxlength) out << " ";
                    out << "// " << p.brief_description;
                }
                out << std::endl;
            }
        }
    }
    else
    {
        out << "  // ..." << std::endl;
    }
    out << "};" << std::endl
        << "``" << std::endl << std::endl;
    quickbook_markup(cos.qbk_markup, markup_after, markup_synopsis, out);



    if (! cos.template_parameters.empty())
    {
        bool has_default = false;
        BOOST_FOREACH(parameter const& p, cos.template_parameters)
        {
            if (! p.default_value.empty())
            {
                has_default = true;
            }
        }

        out << "[heading Template parameter(s)]" << std::endl
            << "[table" << std::endl
            << "[[Parameter]";
        if (has_default)
        {
            out << " [Default]";
        }
        out << " [Description]]" << std::endl;

        BOOST_FOREACH(parameter const& p, cos.template_parameters)
        {
            out << "[[" << p.fulltype;
            if (has_default)
            {
                out << "] [" << p.default_value;
            }
            out << "] [" << p.brief_description << "]]" << std::endl;
        }
        out << "]" << std::endl
            << std::endl;
    }


    std::map<function_type, int> counts;
    BOOST_FOREACH(function const& f, cos.functions)
    {
        counts[f.type]++;
    }

    if (counts[function_constructor_destructor] > 0)
    {
        quickbook_output_function(cos.functions, function_constructor_destructor, "Constructor", config, out);
    }

    if (counts[function_member] > 0)
    {
        quickbook_output_function(cos.functions, function_member, "Member Function", config, out);
    }

    quickbook_header(cos.location, config, out);
    quickbook_markup(cos.qbk_markup, markup_any, markup_default, out);

    out << "[endsect]" << std::endl
        << std::endl;
}


// ----------------------------------------------------------------------------------------------- //
// ALT
// ----------------------------------------------------------------------------------------------- //

std::string remove_template_parameters(std::string const& name)
{
    std::string res;
    std::string::size_type prev_i = 0, i = 0;
    int blocks_counter = 0;
    for ( ;; )
    {
        std::string::size_type next_begin = name.find('<', i);
        std::string::size_type next_end = name.find('>', i);

        if ( next_begin == next_end )
        {
            res += name.substr(prev_i, next_begin - prev_i);
            break;
        }
        else if ( next_begin < next_end )
        {
            i = next_begin + 1;
            if ( blocks_counter == 0 )
                res += name.substr(prev_i, next_begin - prev_i) + "<...>";
            blocks_counter++;
        }
        else
        {
            i = next_end + 1;
            blocks_counter--;
            if ( blocks_counter == 0 )
                prev_i = i;
        }
    }

    return res;
}

std::string remove_template_parameters_block(std::string const& name)
{
    std::string res = remove_template_parameters(name);
    size_t i = res.find("<...>");
    if (i != std::string::npos)
        res = res.substr(0, i) + res.substr(i + 5);
    return res;
}

std::string replace_brackets(std::string const& str)
{
    return boost::replace_all_copy(boost::replace_all_copy(str, "[", "\\["), "]", "\\]");
}

void quickbook_output_enumerations(std::vector<enumeration> const& enumerations,
                                  configuration const& ,
                                  std::ostream& out)
{
    out << "[table" << std::endl
        << "[[Enumeration][Description]]" << std::endl;

    for ( size_t i = 0 ; i < enumerations.size() ; ++i )
    {
        enumeration const& e = enumerations[i];

        out << "[[[link " << e.id << " `";
        out << e.name;
        out << "`]][" << e.brief_description << "]]" << std::endl;
    }
    out << "]" << std::endl
        << std::endl;
}

std::string quickbook_synopsis_short(function const& f)
{
    std::string result;

    if ( f.type != function_unknown )
        result += f.name;

    bool first = true;
    BOOST_FOREACH(parameter const& p, f.parameters)
    {
        if ( !p.skip && p.default_value.empty() )
        {
            result += first ? "(" : ", ";
            result += remove_template_parameters(p.fulltype_without_links);
            first = false;
        }
    }

    if (! first)
        result += ")";
    else if (f.type != function_define)
        result += "()";

    return result;
}

std::string quickbook_section_escape_characters(std::string const& name)
{
    std::string result;
    result.reserve(name.size());
    for (size_t i = 0; i < name.size(); ++i)
    {
        if ( ('a' <= name[i] && name[i] <= 'z')
          || ('0' <= name[i] && name[i] <= '9') )
            result += name[i];
        else if ('A' <= name[i] && name[i] <= 'Z')
            result += name[i] + ('a' - 'A');
        else if (name[i] == '_')
            result += '_';
        else
        {
            std::stringstream ss;
            ss << ((int)(unsigned char)(name[i]));
            result += ss.str();
        }
    }
    return result;
}

std::string quickbook_section_function(function const& f, size_t param_length = 3)
{
    if (param_length < 1)
        param_length = 1;

    std::string result;

    if ( f.type != function_unknown )
        result += quickbook_section_escape_characters(f.name);

    BOOST_FOREACH(parameter const& p, f.parameters)
    {
        if ( !p.skip && p.default_value.empty() )
        {
            std::string pn = remove_template_parameters_block(p.fulltype_without_links);
            size_t i = pn.find(p.type);
            if (i != std::string::npos)
                pn = pn.substr(0, i) + pn.substr(i + p.type.size());

            std::string mods;
            if (pn.find("const") != std::string::npos)
                mods += 'c';
            if (pn.find("volatile") != std::string::npos)
                mods += 'v';
            if (pn.find("&&") != std::string::npos)
                mods += "rr";
            else if (pn.find("&") != std::string::npos)
                mods += 'r';

            size_t n = (std::min)(param_length, (size_t)p.type.size());
            if (n > 0)
            {
                result += '_';
                result += quickbook_section_escape_characters(p.type.substr(0, n));
                if (!mods.empty())
                {
                    result += '_';
                    result += mods;
                }
            }
        }
    }

    return result;
}

std::string quickbook_section_class(std::string const& name, size_t namespace_length = 3)
{
    if (namespace_length < 1)
        namespace_length = 1;

    std::string result;
    size_t b = 0;
    for (;;)
    {
        size_t e = name.find("::", b);
        if (e != std::string::npos)
        {
            size_t n = (std::min)(e - b, namespace_length);
            result += quickbook_section_escape_characters(name.substr(b, n)) + '_';
            b = e + 2;
        }
        else
            break;
    }
    result += quickbook_section_escape_characters(name.substr(b));    
    return result;
}

void quickbook_output_functions(std::vector<function> const& functions,
                                function_type type,
                                configuration const& ,
                                std::ostream& out,
                                bool display_all = false,
                                std::string const& ColTitle = "Function")
{
    bool show_modifiers = false;
    BOOST_FOREACH(function const& f, functions)
    {
        if ( (display_all || f.type == type) && (f.is_const || f.is_static) && !f.brief_description.empty() )
            show_modifiers = true;
    }

    out << "[table\n"
        << "[";
    if ( show_modifiers )
        out << "[Modifier]";
    out << "[" << ColTitle << "]";
    out << "[Description]";
    out << "]" << std::endl;

    for ( size_t i = 0 ; i < functions.size() ; ++i )
    {
        function const& f = functions[i];

        if ( f.brief_description.empty() )
            continue;

        if (display_all || f.type == type)
        {
            out << "[";
            if ( show_modifiers )
            {
                out << "[";
                out << (f.is_static ? "`static`" : "");
                out << (f.is_const ? " `const`" : "");
                out << "]";
            }
            out << "[[link " << f.id << " `";
            out << quickbook_synopsis_short(f);
            out << "`]]";
            out << "[" << f.brief_description << "]";
            out << "]" << std::endl;
        }
    }
    out << "]" << std::endl
        << std::endl;
}

void output_paragraphs_note_warning(element const& el, std::ostream & out)
{
    // Additional paragraphs
    if ( !el.paragraphs.empty() )
    {
        BOOST_FOREACH(paragraph const& p, el.paragraphs)
        {
            if ( !p.title.empty() )
                out << "[heading " << p.title << "]" << std::endl;
            else
                out << "\n\n" << std::endl;
            out << p.text << std::endl;
            out << std::endl;
        }
    }

    // Note
    if ( !el.note.empty() )
    {
        out << "[note " << el.note << "]" << std::endl;
        out << std::endl;
    }

    // Warning
    if ( !el.warning.empty() )
    {
        out << "[warning " << el.warning << "]" << std::endl;
        out << std::endl;
    }
}

void inline_str_with_links(std::string const& str, std::ostream & out)
{
    typedef std::string::size_type ST;

    bool link_started = false;
    bool first = true;
    for ( ST i = 0 ; i < str.size() ; ++i )
    {
        if ( !link_started )
        {
            if ( str[i] == '[' && str.substr(i, 6) == "[link " )
            {
                if ( !first )
                {
                    out << "`";
                    first = true;
                }
                link_started = true;
                out << "[^[link ";
                i += 5; // (+ 6 - 1)
            }
            else
            {
                if ( first )
                {
                    out << "`";
                    first = false;
                }
                out << str[i];
            }
        }
        else
        {
            if ( str[i] == '\\' )
            {
                out << str[i];
                ++i;
                if ( i < str.size() )
                    out << str[i];
            }
            else if ( str[i] == ']' )
            {
                out << "]]";
                link_started = false;
            }
            else
                out << str[i];
        }
    }

    if ( !first )
        out << "`";
    if ( link_started )
        out << "]]";
}

void quickbook_template_parameter_list_alt(std::vector<parameter> const& parameters, std::ostream& out)
{
    std::string next_param;

    if ( 2 < parameters.size() )
        next_param = std::string("`,`\n") + "         ";
    else
        next_param = "`,` ";

    if (!parameters.empty())
    {
        out << "`template<`" ;
        bool first = true;
        BOOST_FOREACH(parameter const& p, parameters)
        {
            out << (first ? "" : next_param.c_str());
            inline_str_with_links(p.fulltype, out);

            if ( !p.default_value.empty() )
            {
                out << " = ";
                // don't display default values from details
                if ( p.default_value.find("detail") != std::string::npos )
                    out << "/default/";
                else
                    inline_str_with_links(p.default_value, out);
            }

            first = false;
        }
        out << "`>`";
    }
}

void quickbook_synopsis_alt(function const& f, std::ostream& out)
{
    out << "[pre\n";
    quickbook_template_parameter_list_alt(f.template_parameters, out);
    out << "\n";

    std::size_t offset = 1; // '('
    switch(f.type)
    {
        case function_constructor_destructor :
            out << "`" << f.name << "`";
            offset += f.name.size();
            break;
        case function_member :
        case function_free :
            // don't display return types from details
            if ( f.return_type.find("detail") != std::string::npos )
            {
                out << "/unspecified/";
                offset += 11;
            }
            else
            {
                inline_str_with_links(f.return_type, out);
                offset += f.return_type_without_links.size();
            }
            out << " `" << f.name << "`";
            offset += 1 + f.name.size();
            break;
        case function_define :
            out << "`#define " << f.name << "`";
            offset += 8 + f.name.size();
            break;
        case function_unknown :
            // do nothing
            break;
    }

    std::string par_end("`,` ");
    if ( 2 < f.parameters.size() )
        par_end = std::string("`,`\n") + std::string(offset, ' ');

    // Output the parameters
    // Because we want to be able to skip, we cannot use the argstring
    {
        bool first = true;
        BOOST_FOREACH(parameter const& p, f.parameters)
        {
            if (! p.skip)
            {
                out << (first ? "`(`" : par_end);
                if ( !p.fulltype.empty() )
                {
                    inline_str_with_links(p.fulltype, out);
                    out << " ";
                }
                if ( !p.name.empty() )
                    out << "`" << p.name << "`";
                if ( !p.default_value.empty() )
                {
                    out << " = ";
                    // don't display default values from details
                    if ( p.default_value.find("detail") != std::string::npos )
                        out << "/default/";
                    else
                        inline_str_with_links(p.default_value, out);
                }
                first = false;
            }
        }

        if (! first)
            out << "`)`\n";
        else if (f.type != function_define)
            out << "`()`\n";
    }

    out << "]"
        << std::endl
        << std::endl;
}

void quickbook_synopsis_alt(class_or_struct const& cos, configuration const& config, std::ostream & out)
{
    std::string short_name = namespace_skipped(cos.fullname, config);

    out << "[pre\n";

    quickbook_template_parameter_list_alt(cos.template_parameters, out);
    out << "\n";

    out << (cos.is_class ? "`class " : "`struct ");
    {
        std::string::size_type last_scope = std::string::npos;
        std::string::size_type i = short_name.find("<");
        for(std::string::size_type j = short_name.find("::") ; j < i ; j = short_name.find("::", j+1))
            last_scope = j;
        if ( last_scope == std::string::npos )
            out << short_name << "`" << std::endl;
        else
            out << short_name.substr(last_scope + 2) << "`" << std::endl;
    }

    if (! cos.base_classes.empty())
    {
        bool first = true;
        BOOST_FOREACH(base_class const& bc, cos.base_classes)
        {
            // don't display base classes from details
            if ( bc.name.find("detail") != std::string::npos )
                continue;

            if (first)
                out << "`      : ";
            else
                out << std::endl << "      , ";
            out << output_if_different(bc.derivation, "private")
                << output_if_different(bc.virtuality, "non-virtual")
                << namespace_skipped(bc.name, config);
            first = false;
        }
        if (!first)
            out << "`" << std::endl;
    }

    out << "`{`" << std::endl
        << "`  // ...`" << std::endl
        << "`};`" << std::endl
        << "]" << std::endl << std::endl;
}

void quickbook_synopsis_alt(enumeration const& e, std::ostream& out)
{
    std::string values_separator =
        e.enumeration_values.size() <= 2 ?
            std::string(", ") :
            ( std::string(",\n") + std::string(e.name.size() + 7, ' ') );

    out << "``enum " << e.name << " ";
    bool first = true;
    BOOST_FOREACH(enumeration_value const& value, e.enumeration_values)
    {
        out << (first ? "{" : values_separator.c_str());
        out << value.name;
        if ( !value.initializer.empty() )
        {
            out << " = " << boost::trim_copy(boost::replace_all_copy(value.initializer, "=", ""));
        }
        first = false;
    }
    if (! first)
    {
        out << "};";
    }
    out << "``"
        << std::endl
        << std::endl;
}

template <typename Range>
bool has_brief_description(Range const& rng)
{
    typedef typename Range::value_type V;
    BOOST_FOREACH(V const& bc, rng)
    {
        if ( !bc.brief_description.empty() )
            return true;
    }
    return false;
}

template <typename Range>
bool has_brief_description(Range const& rng, function_type t)
{
    typedef typename Range::value_type V;
    BOOST_FOREACH(V const& bc, rng)
    {
        if ( bc.type == t && !bc.brief_description.empty() )
            return true;
    }
    return false;
}

void quickbook_output_functions_details(std::vector<function> const& functions,
                                        function_type type,
                                        configuration const& config,
                                        std::ostream& out,
                                        bool display_all = false)
{
    for ( size_t i = 0 ; i < functions.size() ; ++i )
    {
        function const& f = functions[i];

        if ( f.brief_description.empty() )
            continue;

        if ( display_all || f.type == type )
        {
            // Section
            out << "[#" << f.id << "]" << std::endl;
            std::string synopsis_short = quickbook_synopsis_short(f);
            if (config.alt_max_synopsis_length > 0
                && synopsis_short.size() > config.alt_max_synopsis_length)
            {
                out << "[section:" << quickbook_section_function(f) << " " << synopsis_short << "]" << std::endl;
            }
            else
            {
                out << "[section " << synopsis_short << "]" << std::endl;
            }

            quickbook_output_indexterm(f.name, out);

            // Brief description
            out << f.brief_description << std::endl;
            out << std::endl;

            // Detail description
            if ( !f.detailed_description.empty() )
            {
                out << "[heading Description]" << std::endl;
                out << f.detailed_description;
            }

            // Synopsis
            quickbook_markup(f.qbk_markup, markup_before, markup_synopsis, out);
            out << "[heading Synopsis]" << std::endl;
            quickbook_synopsis_alt(f, out);
            quickbook_markup(f.qbk_markup, markup_after, markup_synopsis, out);

            if ( f.is_static || f.is_virtual || f.is_explicit || f.is_const )
            {
                out << "[heading Modifier(s)]" << std::endl;
                out << "``"
                    << (f.is_static ? "static " : "")
                    << (f.is_virtual ? "virtual " : "")
                    << (f.is_explicit ? "explicit " : "")
                    << (f.is_const ? "const " : "")
                    << "``";
            }

            // Template parameters
            if ( !f.template_parameters.empty() && has_brief_description(f.template_parameters) )
            {
                out << "[heading Template parameter(s)]" << std::endl
                    << "[table" << std::endl
                    << "[[Parameter] [Description]]" << std::endl;

                BOOST_FOREACH(parameter const& p, f.template_parameters)
                {
                    if ( p.brief_description.empty() )
                        continue;

                    out << "[[`";
                    if ( p.fulltype.find("typename ") == 0 )
                        out << p.fulltype.substr(9);
                    else if ( p.fulltype.find("class ") == 0 )
                        out << p.fulltype.substr(6);
                    else
                        out << p.fulltype;
                    out << "`][" << p.brief_description << "]]" << std::endl;
                }
                out << "]" << std::endl
                    << std::endl;
            }

            // Parameters
            if ( !f.parameters.empty() && has_brief_description(f.parameters) )
            {
                out << "[heading Parameter(s)]" << std::endl;
                out << "[table " << std::endl;
                out << "[";
                if ( f.type != function_define )
                    out << "[Type]";
                out << "[Name][Description]]" << std::endl;
                BOOST_FOREACH(parameter const& p, f.parameters)
                {
                    if (!p.skip)
                    {
                        out << "[";
                        if ( f.type != function_define )
                        {
                            out << "[";
                            inline_str_with_links(p.fulltype, out);
                            out << "]";
                        }
                        out << "[ `" << p.name << "` ][" << p.brief_description << "]]"<< std::endl;
                    }
                }
                out << "]" << std::endl;
            }

            // Precondition
            if ( !f.precondition.empty() )
            {
                out << "[heading Precondition(s)]" << std::endl;
                out << f.precondition << std::endl;
                out << std::endl;
            }

            // Return
            if ( !f.return_description.empty() )
            {
                out << "[heading Returns]" << std::endl;
                out << f.return_description << std::endl;
            }

            // Additional paragraphs, note, warning
            output_paragraphs_note_warning(f, out);

            // QBK markup
            quickbook_markup(f.qbk_markup, markup_any, markup_default, out);

            // Section end
            out << "[endsect]" << std::endl
                //<< "[br]" << std::endl
                << std::endl;
        }
    }
}

void quickbook_output_enumeration_details(enumeration const& e, configuration const& , std::ostream& out)
{
    out << "[#" << e.id << "]\n";
    out << "[section " << e.name << "]" << std::endl
        << std::endl;

    quickbook_output_indexterm(e.name, out);
    BOOST_FOREACH(enumeration_value const& value, e.enumeration_values)
    {
        quickbook_output_indexterm(value.name, out);
    }

    out << e.brief_description << std::endl;
    out << std::endl;

    if ( !e.detailed_description.empty() )
    {
        out << "[heading Description]\n\n";
        out << e.detailed_description << "\n\n";
    }

    // Additional paragraphs, note, warning
    output_paragraphs_note_warning(e, out);

    quickbook_markup(e.qbk_markup, markup_any, markup_default, out);

    // Synopsis
    quickbook_markup(e.qbk_markup, markup_before, markup_synopsis, out);
    out << "[heading Synopsis]" << std::endl;
    quickbook_synopsis_alt(e, out);
    quickbook_markup(e.qbk_markup, markup_after, markup_synopsis, out);


    out << "[heading Values]" << std::endl
        << std::endl;

    out << "[table" << std::endl << "[";
    out << "[Value] [Description] ]" << std::endl;

    BOOST_FOREACH(enumeration_value const& value, e.enumeration_values)
    {
        out << "[[" << value.name << "] [" << value.brief_description << "]]\n";
    }
    out << "]\n\n\n";

    out << std::endl;
    out << "[endsect]" << std::endl;
    out << std::endl;
}

void quickbook_output_alt(documentation const& doc, configuration const& config, std::ostream& out)
{
    if ( !doc.group_id.empty() )
    {
        out << "[section:" << doc.group_id << " " << doc.group_title << "]" << std::endl;
    }

    if ( !doc.enumerations.empty() )
    {
        out << "[heading Enumerations]\n";
        quickbook_output_enumerations(doc.enumerations, config, out);
    }

    if ( !doc.defines.empty() )
    {
        out << "[heading Defines]\n";
        quickbook_output_functions(doc.defines, function_unknown, config, out, true, "Define");
    }

    if ( !doc.functions.empty() )
    {
        out << "[heading Functions]\n";
        quickbook_output_functions(doc.functions, function_unknown, config, out, true, "Function");
    }

    BOOST_FOREACH(enumeration const& e, doc.enumerations)
    {
        quickbook_output_enumeration_details(e, config, out);
    }

    quickbook_output_functions_details(doc.defines, function_unknown, config, out, true);
    quickbook_output_functions_details(doc.functions, function_unknown, config, out, true);

    if ( !doc.group_id.empty() )
    {
        out << "[endsect]" << std::endl
            << std::endl;
    }
}

void quickbook_output_alt(class_or_struct const& cos, configuration const& config, std::ostream& out)
{
    // Skip namespace
    std::string short_name = namespace_skipped(cos.fullname, config);

    BOOST_ASSERT(configuration::alt == config.output_style);

    if ( !cos.id.empty() )
        out << "[#" << cos.id << "]" << std::endl;
    out << "[section " << short_name << "]" << std::endl << std::endl;
    //out << "[section:" << quickbook_section_class(short_name, 1) << " " << short_name << "]" << std::endl << std::endl;

    // WARNING! Can't be used in the case of specializations
    quickbook_output_indexterm(short_name, out);

    // Brief

    out << cos.brief_description << std::endl;
    out << std::endl;

    // Description

    quickbook_string_with_heading_if_present("Description", cos.detailed_description, out);

    // Additional paragraphs, note, warning
    output_paragraphs_note_warning(cos, out);

    // Markup
    quickbook_markup(cos.qbk_markup, markup_any, markup_default, out);

    // Header

    quickbook_header(cos.location, config, out);

    // Class synposis

    quickbook_markup(cos.qbk_markup, markup_before, markup_synopsis, out);
    out << "[heading Synopsis]" << std::endl;
    quickbook_synopsis_alt(cos, config, out);
    quickbook_markup(cos.qbk_markup, markup_after, markup_synopsis, out);

    // Template parameters

    if (! cos.template_parameters.empty())
    {
        if ( has_brief_description(cos.template_parameters) )
        {
            out << "[heading Template parameter(s)]" << std::endl
                << "[table" << std::endl
                << "[[Parameter] [Description]]" << std::endl;

            BOOST_FOREACH(parameter const& p, cos.template_parameters)
            {
                if ( p.brief_description.empty() )
                    continue;

                out << "[[`";
                if ( p.fulltype.find("typename ") == 0 )
                    out << p.fulltype.substr(9);
                else if ( p.fulltype.find("class ") == 0 )
                    out << p.fulltype.substr(6);
                else
                    out << p.fulltype;
                out << "`][" << p.brief_description << "]]" << std::endl;
            }
            out << "]" << std::endl
                << std::endl;
        }
    }

    // Typedefs

    if ( !cos.typedefs.empty() )
    {
        if ( has_brief_description(cos.typedefs) )
        {
            out << "[heading Typedef(s)]" << std::endl
                << "[table" << std::endl
                << "[[Type]";
            out << " [Description]]" << std::endl;

            BOOST_FOREACH(base_element const& e, cos.typedefs)
            {
                if ( e.brief_description.empty() )
                    continue;

                out << "[[";
                if ( !e.id.empty() )
                    out << "[#" << e.id << "]" << " ";
                out << "`" << e.name << "`";
                out << "][" << e.brief_description << "]]" << std::endl;
            }
            out << "]" << std::endl
                << std::endl;
        }
    }

    // Members

    bool display_ctors = has_brief_description(cos.functions, function_constructor_destructor);
    bool display_members = has_brief_description(cos.functions, function_member);

    std::map<function_type, int> counts;
    BOOST_FOREACH(function const& f, cos.functions)
    {
        counts[f.type]++;
    }

    if (display_ctors && counts[function_constructor_destructor] > 0)
    {
        out << "[heading Constructor(s) and destructor]" << std::endl;
        quickbook_output_functions(cos.functions, function_constructor_destructor, config, out);
    }

    if (display_members && counts[function_member] > 0)
    {
        out << "[heading Member(s)]" << std::endl;
        quickbook_output_functions(cos.functions, function_member, config, out);
    }

    // Details start

    //if ( display_ctors || display_members )
    //    out << "[br]" << std::endl;

    if (display_ctors && counts[function_constructor_destructor] > 0)
        quickbook_output_functions_details(cos.functions, function_constructor_destructor, config, out);

    if (display_members && counts[function_member] > 0)
        quickbook_output_functions_details(cos.functions, function_member, config, out);

    // Details end

    out << "[endsect]" << std::endl
        << std::endl;
}

#endif // QUICKBOOK_OUTPUT_HPP
