// Copyright (c) 2015-2022, Lawrence Livermore National Security, LLC.
// See top-level LICENSE file for details.

/// \file QuerySpec.h
/// \brief %Caliper query specification definition

#pragma once

#include <map>
#include <string>
#include <vector>

namespace cali
{

/// \brief Describes a %Caliper data processing rule (filter, aggregation, formatting)
/// \ingroup ReaderAPI
struct QuerySpec
{
    //
    // --- Types
    //

    /// \brief Template type for list-style query options
    template<class T>
    struct SelectionList {
        enum SelectionOpt {
            Default, ///< Take the default
            None,    ///< Take none
            All,     ///< Take all available
            List     ///< User-defined list
        }              selection; ///< Selection specification

        /// \brief User-defined list
        std::vector<T> list;

        /// \brief Use path (main nested region hierarchy) attributes
        bool           use_path;
    };

    /// \brief Describe function signatures in query specs
    struct FunctionSignature {
        int          id;
        const char*  name;     ///< Function name
        int          min_args; ///< Minimum required number of arguments
        int          max_args; ///< Maximum allowed number of function arguments
        const char** args;     ///< Names of the function arguments
    };

    static const FunctionSignature FunctionSignatureTerminator;

    /// \brief An aggregation function invocation in a query spec
    struct AggregationOp {
        FunctionSignature        op;    ///< The aggregation operator
        std::vector<std::string> args;  ///< Arguments for the aggregation operator (typically, the attribute name)

        AggregationOp()
            : op(FunctionSignatureTerminator)
        { }

        AggregationOp(const FunctionSignature& s)
            : op(s)
        { }

        AggregationOp(const FunctionSignature& s, const std::vector<std::string>& a)
            : op(s), args(a)
        { }
    };

    /// \brief Sort description.
    struct SortSpec {
        /// \brief The sort order.
        enum Order {
            None, Ascending, Descending
        }           order;
        /// \brief Name of the attribute to be sorted.
        std::string attribute;

        SortSpec(const std::string& s, Order o = Ascending)
            : order(o), attribute(s)
        { }
    };

    /// \brief Filter condition
    struct Condition {
        enum Op   {
            None,
            Exist,       NotExist,
            Equal,       NotEqual,
            LessThan,    GreaterThan,
            LessOrEqual, GreaterOrEqual
        }           op;
        std::string attr_name;
        std::string value;

        Condition()
            : op(Op::None)
        { }

        Condition(Condition::Op o, const std::string& name, const std::string& val)
            : op(o), attr_name(name), value(val)
        { }
    };

    /// \brief Output formatter specification.
    struct FormatSpec {
        enum Opt {
            Default, User
        }                        opt;       ///< Default or user-defined formatter
        FunctionSignature        formatter; ///< The formatter to use. Signatures provided by FormatProcessor.
        std::map<std::string, std::string> kwargs; ///< Arguments to the formatter.
    };

    struct PreprocessSpec {
        std::string   target;
        AggregationOp op;
        Condition     cond;
    };

    //
    // --- Data members
    //

    typedef SelectionList<AggregationOp> AggregationSelection;
    typedef SelectionList<std::string>   AttributeSelection;
    typedef SelectionList<Condition>     FilterSelection;
    typedef SelectionList<SortSpec>      SortSelection;

    /// \brief List of aggregations to be performed.
    AggregationSelection         aggregate;
    /// \brief List of attribute names that form the aggregation key (i.e., GROUP BY spec).
    AttributeSelection           groupby;

    /// \brief List of attributes to print in output
    AttributeSelection           select;

    /// \brief List of filter clauses (filters will be combined with AND)
    FilterSelection              filter;

    /// \brief List of sort specifications
    SortSelection                sort;

    /// \brief Output formatter specification
    FormatSpec                   format;

    /// \brief Output aliases for attributes (i.e., "select x AS y" )
    std::map<std::string, std::string> aliases;

    /// \brief Units for attributes (i.e. SELECT x AS y UNIT z)
    std::map<std::string, std::string> units;

    /// \brief List of preprocessing operations (i.e., "LET y=f(x)")
    std::vector<PreprocessSpec>  preprocess_ops;
};

} // namespace cali
