/** Copyright 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Roland Olbricht et al.
 *
 * This file is part of Overpass_API.
 *
 * Overpass_API is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * Overpass_API is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Overpass_API.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef DE__OSM3S___OVERPASS_API__STATEMENTS__PRINT_H
#define DE__OSM3S___OVERPASS_API__STATEMENTS__PRINT_H

#include <map>
#include <string>
#include <vector>
#include "../data/collect_members.h"
#include "../data/utils.h"
#include "../frontend/output_handler.h"
#include "statement.h"


class Set_Comparison;

/* === Print (''out'') ===

The ''out'' statement can be configured with an arbitrary number of parameters that are appended, separated by whitespace, between the word ''out'' and the semicolon.

The ''out'' action takes an input set. It doesn't return a result set. The input set can be changed by prepending the variable name.

Allowed values, in any order, are:

* One of the following the degree of verbosity; default is ''body'':
** ''ids'': Print only the ids of the elements.
** ''skel'': Print also the minimum information necessary for geometry.
These are coordinates for nodes. For ways and relations, it are the members with role and ref.
** ''body'': Print all information necessary to use the data. These are also tags for all elements and the roles for relation members.
** ''tags'': Print only ids and tags for each element and not coordinates or members.
** ''meta'': Print everything known about the elements.
This includes additionally to ''body'' for all OSM elements the version, changeset id, timestamp, and the user data of the user that last touched the object.
Derived elements metadata attributes are also missing for derived elements.

The modificator ''noids'' lets all ids to be omitted from the statement.

* One of the following modificators for geolocated information, default is to print none:
** ''geom'': Add the full geometry to each object. This adds coordinates to each node, to each node member of a way or relation, and it adds a sequence of "nd" members with coordinates to all relations.
** ''bb'': Adds only the bounding box of each element to the element. For nodes this is equivalent to "geom".
For ways it is the enclosing bounding box of all nodes.
For relations it is the enclosing bounding box of all node and way members, relations as members have no effect.
** ''center'': This adds only the center of the above mentioned bounding box to ways and relations.
Note: The center point is not guaranteed to lie inside the polygon ([http://overpass-turbo.eu/s/4rr example]).
* A bounding box in the format "(south,west,north,east)" (normally used with the ''geom'' verbosity type). In this case, only elements whose coordinates are inside this bounding box are produced. For way segments, the first or last coordinates outside this bounding box are also produced to allow for properly formed segments (this restriction has no effect on derived elements without any geometry).

* One of the following for the sort order can be added. Default is ''asc'':
** ''asc'': Sort by object id.
** ''qt'': Sort by quadtile index; this is roughly geographical and significantly faster than order by ids (derived elements generated by ''make'' or ''convert'' statements without any geometry will be grouped separately, only sorted by id).

The modificator ''count'' lets the statement print only the [[#Counting of elements (out count)|total counts of elements]] in the input set and not the individual elements.
It cannot be combined with anything else.

A non-negative integer as modificator limits the output to a maximum of the given number.
Default is no limit.

*/


class Print_Statement : public Statement
{
  public:
    Print_Statement(int line_number_, const std::map< std::string, std::string >& attributes, Parsed_Query& global_settings);
    virtual std::string get_name() const { return "print"; }
    virtual std::string get_result_name() const { return ""; }
    virtual void execute(Resource_Manager& rman);
    virtual ~Print_Statement();

    static Generic_Statement_Maker< Print_Statement > statement_maker;

    static std::string mode_string_xml(Output_Mode mode)
    {
      if ((mode & (Output_Mode::VERSION | Output_Mode::META)) == (Output_Mode::VERSION | Output_Mode::META))
        return " mode=\"meta\"";
      else if ((mode & (Output_Mode::MEMBERS | Output_Mode::TAGS)) == (Output_Mode::MEMBERS | Output_Mode::TAGS))
        return "";
      else if ((mode & Output_Mode::TAGS) == Output_Mode::TAGS)
        return " mode=\"tags\"";
      else if ((mode & Output_Mode::MEMBERS) == Output_Mode::MEMBERS)
        return " mode=\"skeleton\"";
      else if ((mode & Output_Mode::COUNT) == Output_Mode::COUNT)
        return " mode=\"count\"";

      return " mode=\"ids_only\"";
    }

    static std::string mode_string_ql(Output_Mode mode)
    {
      if ((mode & (Output_Mode::VERSION | Output_Mode::META)) == (Output_Mode::VERSION | Output_Mode::META))
        return " meta";
      else if ((mode & (Output_Mode::MEMBERS | Output_Mode::TAGS)) == (Output_Mode::MEMBERS | Output_Mode::TAGS))
        return "";
      else if ((mode & Output_Mode::TAGS) == Output_Mode::TAGS)
        return " tags";
      else if ((mode & Output_Mode::MEMBERS) == Output_Mode::MEMBERS)
        return " skel";
      else if ((mode & Output_Mode::COUNT) == Output_Mode::COUNT)
        return " count";

      return " ids";
    }

    static std::string geometry_string_xml(Output_Mode mode)
    {
      if ((mode & (Output_Mode::GEOMETRY | Output_Mode::BOUNDS | Output_Mode::CENTER))
          == (Output_Mode::GEOMETRY | Output_Mode::BOUNDS | Output_Mode::CENTER))
        return " geometry=\"full\"";
      else if ((mode & (Output_Mode::GEOMETRY | Output_Mode::BOUNDS | Output_Mode::CENTER))
          == (Output_Mode::BOUNDS | Output_Mode::CENTER))
        return " geometry=\"bounds\"";
      else if ((mode & (Output_Mode::GEOMETRY | Output_Mode::BOUNDS | Output_Mode::CENTER))
          == Output_Mode::CENTER)
        return " geometry=\"center\"";

      return "";
    }

    static std::string geometry_string_ql(Output_Mode mode)
    {
      if ((mode & (Output_Mode::GEOMETRY | Output_Mode::BOUNDS | Output_Mode::CENTER))
          == (Output_Mode::GEOMETRY | Output_Mode::BOUNDS))
        return " geom";
      else if ((mode & (Output_Mode::GEOMETRY | Output_Mode::BOUNDS | Output_Mode::CENTER))
          == (Output_Mode::BOUNDS))
        return " bb";
      else if ((mode & (Output_Mode::GEOMETRY | Output_Mode::BOUNDS | Output_Mode::CENTER))
          == Output_Mode::CENTER)
        return " center";

      return "";
    }

    static std::string ids_string_xml(Output_Mode mode)
    {
      return (mode & Output_Mode::ID) ? "" : " ids=\"no\"";
    }

    static std::string ids_string_ql(Output_Mode mode)
    {
      return (mode & Output_Mode::ID) ? "" : " noids";
    }

    virtual std::string dump_xml(const std::string& indent) const
    {
      return indent + "<print"
          + (input != "_" ? std::string(" from=\"") + input + "\"" : "")
          + mode_string_xml(mode)
          + (order == order_by_id ? "" : " order=\"quadtile\"")
          + (limit == std::numeric_limits< unsigned int >::max() ? "" : " limit=\"" + ::to_string(limit) + "\"")
          + geometry_string_xml(mode)
          + ids_string_xml(mode)
          + (south > north ? "" : " s=\"" + to_string(south) + "\"")
          + (south > north ? "" : " w=\"" + to_string(west) + "\"")
          + (south > north ? "" : " n=\"" + to_string(north) + "\"")
          + (south > north ? "" : " e=\"" + to_string(east) + "\"")
          + "/>\n";
    }

    virtual std::string dump_compact_ql(const std::string& indent) const { return dump_subquery_map_ql(indent, false); }
    virtual std::string dump_pretty_ql(const std::string& indent) const { return dump_subquery_map_ql(indent, true); }

    std::string dump_subquery_map_ql(const std::string& indent, bool pretty) const
    {
      return indent + (input != "_" ? "." + input + " " : "") + "out"
          + mode_string_ql(mode)
          + (order == order_by_id ? "" : " qt")
          + (limit == std::numeric_limits< unsigned int >::max() ? "" : " " + ::to_string(limit))
          + geometry_string_ql(mode)
          + ids_string_ql(mode)
          + (south > north ? "" : "(" + to_string(south) + "," + to_string(west) + ","
              + to_string(north) + "," + to_string(east) + ")") + ";";
    }

  private:
    std::string input;
    Output_Mode mode;
    enum { order_by_id, order_by_quadtile } order;
    unsigned int limit;
    Set_Comparison* collection_print_target;
    bool diff_valid;

    double south;
    double north;
    double west;
    double east;

    virtual void execute_comparison(Resource_Manager& rman);
};


#endif
