text: // 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, 2015.
  // Modifications copyright (c) 2014-2015, Oracle and/or its affiliates.
  
  // Contributed and/or modified by Menelaos Karavelas, 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 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;
  }
  
  void quickbook_synopsis_short(function const& f, std::ostream& out)
  {
      if ( f.type != function_unknown )
          out << f.name;
  
      bool first = true;
      BOOST_FOREACH(parameter const& p, f.parameters)
      {
          if ( !p.skip && p.default_value.empty() )
          {
              out << (first ? "(" : ", ") << remove_template_parameters(p.fulltype_without_links);
              first = false;
          }
      }
  
  
      if (! first)
          out << ")";
      else if (f.type != function_define)
          out << "()";
  }
  
  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 << " `";
              quickbook_synopsis_short(f, out);
              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& ,
                                          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
              std::stringstream ss;
              quickbook_synopsis_short(f, ss);
              out << "[#" << f.id << "]" << std::endl;
              out << "[section " << replace_brackets(ss.str()) << "]" << 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() )
      {
          std::cout << "[section:" << doc.group_id << " " << doc.group_title << "]" << std::endl;
      }
  
      if ( !doc.enumerations.empty() )
      {
          std::cout << "[heading Enumerations]\n";
          quickbook_output_enumerations(doc.enumerations, config, out);
      }
  
      if ( !doc.defines.empty() )
      {
          std::cout << "[heading Defines]\n";
          quickbook_output_functions(doc.defines, function_unknown, config, out, true, "Define");
      }
  
      if ( !doc.functions.empty() )
      {
          std::cout << "[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;
  
      // 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
  