/******************************************************************************
 *
 * Copyright (c) 2019, the Perspective Authors.
 *
 * This file is part of the Perspective library, distributed under the terms of
 * the Apache License 2.0.  The full license can be found in the LICENSE file.
 *
 */
#pragma once

#include <perspective/first.h>
#include <perspective/base.h>
#include <perspective/config.h>
#include <perspective/raw_types.h>
#include <perspective/scalar.h>
#include <perspective/computed.h>
#include <tsl/ordered_map.h>
#include <tuple>

namespace perspective {

/**
 * @brief The `t_view_config` API provides a unified view configuration object, which specifies
 * how the `View` transforms the underlying `Table`. By storing configuration values in native
 * C++ types, we allow easy integration with binding languages.
 *
 */
class PERSPECTIVE_EXPORT t_view_config {
public:
    /**
     * @brief Construct a `t_view_config` object.
     *
     * @param row_pivots
     * @param column_pivots
     * @param aggregates
     * @param columns
     * @param filter
     * @param sort
     */
    t_view_config(
        const std::vector<std::string>& row_pivots,
        const std::vector<std::string>& column_pivots,
        const tsl::ordered_map<std::string, std::vector<std::string>>& aggregates,
        const std::vector<std::string>& columns,
        const std::vector<std::tuple<std::string, std::string, std::vector<t_tscalar>>>& filter,
        const std::vector<std::vector<std::string>>& sort,
        const std::vector<t_computed_column_definition>& computed_columns,
        const std::string& filter_op,
        bool column_only);

    /**
     * @brief Given a `t_schema` specifying the underlying `Table`'s columns, construct the
     * abstractions necessary for the core engine.
     *
     * @param schema
     */
    void init(std::shared_ptr<t_schema> schema);

    /**
     * @brief Add filter terms manually, as the filter term must be calculated from the value
     * passed through the binding.
     *
     * @param term
     */
    void add_filter_term(std::tuple<std::string, std::string, std::vector<t_tscalar>> term);

    /**
     * @brief Set the number of pivot levels the engine should generate.
     *
     * @param depth
     */
    void set_row_pivot_depth(std::int32_t depth);
    void set_column_pivot_depth(std::int32_t depth);

    std::vector<std::string> get_row_pivots() const;

    std::vector<std::string> get_column_pivots() const;

    std::vector<t_aggspec> get_aggspecs() const;

    std::vector<std::string> get_columns() const;

    std::vector<t_fterm> get_fterm() const;

    std::vector<t_sortspec> get_sortspec() const;

    std::vector<t_sortspec> get_col_sortspec() const;

    std::vector<t_computed_column_definition> get_computed_columns() const;

    t_filter_op get_filter_op() const;

    bool is_column_only() const;

    std::int32_t get_row_pivot_depth() const;
    std::int32_t get_column_pivot_depth() const;

private:
    bool m_init;

    /**
     * @brief Fill the `m_aggspecs` vector with `t_aggspec` objects which define the view's
     * aggregate settings.
     *
     * The method calculates aggregates using the columns marked for display (in `m_columns`),
     * user-provided aggregates (in `m_aggregates`), and if sorts are applied using a column
     * that is not displayed.
     *
     * At the same time, the method fills the `m_aggregate_names` vector with each column that
     * has an aggregate applied.
     *
     * See documentation for the `m_aggregate_names` vector below for details regarding its
     * logic.
     *
     * @param schema
     * @return void
     */
    void fill_aggspecs(std::shared_ptr<t_schema> schema);

    /**
     * @brief Fill the `m_fterm` vector with `t_fterm` objects which define the view's filters.
     *
     * @return void
     */
    void fill_fterm();

    /**
     * @brief Fill the `m_sortspec` vectors with `t_sortspec` objects which define the view's
     * sorting.
     *
     * The method fills both the `m_sortspec` and `m_col_sortspec` vectors. The former is used
     * in all views, and the latter refers to sorting based on a column when column pivots are
     * applied; it is only used in 2-sided views.
     *
     * @return void
     */
    void fill_sortspec();

    /**
     * @brief Given a column name, find its position in `m_aggregate_names`. Used for
     * determining sort specifications.
     *
     * @param column
     * @return t_index
     */
    t_index get_aggregate_index(const std::string& column) const;

    // containers for primitive data that does not need transformation into abstractions
    std::vector<std::string> m_row_pivots;
    std::vector<std::string> m_column_pivots;
    tsl::ordered_map<std::string, std::vector<std::string>> m_aggregates;
    std::vector<std::string> m_columns;
    std::vector<std::tuple<std::string, std::string, std::vector<t_tscalar>>> m_filter;
    std::vector<std::vector<std::string>> m_sort;
    std::vector<t_computed_column_definition> m_computed_columns;

    /**
     * @brief The ordered list of aggregate columns:
     *
     * 1. all columns marked as "shown" by the user in `m_columns`
     * 2. all specified aggregates from `m_aggregates`
     * 3. all "hidden sorts", i.e. columns to sort by that do not appear in `m_columns`
     *
     */
    std::vector<std::string> m_aggregate_names;

    // store containers for abstractions which are used by the engine
    std::vector<t_aggspec> m_aggspecs;

    std::vector<t_fterm> m_fterm;

    std::vector<t_sortspec> m_sortspec;

    std::vector<t_sortspec> m_col_sortspec;

    /**
     * @brief If specified, the number of pivot levels the engine should generate.
     *
     * Used in `expand` and `collapse` tree operations, and in the grid.
     */
    std::int32_t m_row_pivot_depth;
    std::int32_t m_column_pivot_depth;

    /**
     * @brief the `t_filter_op` used to return data in the case of multiple filters being applied.
     *
     * Defaults to "and", which returns data that satisfies all the filters provided by the user. 
     */
    std::string m_filter_op;

    /**
     * @brief whether the view is `column_only`, i.e. having > 1 `column_pivots` without any
     * `row_pivots`.
     *
     */
    bool m_column_only;
};
} // end namespace perspective