/* Copyright (c) 2020 vesoft inc. All rights reserved.
 *
 * This source code is licensed under Apache 2.0 License,
 * attached with Common Clause Condition 1.0, found in the LICENSES directory.
 */

#ifndef STORAGE_INDEXPOLICYMAKER_H
#define STORAGE_INDEXPOLICYMAKER_H
#include "base/Base.h"
#include "meta/SchemaManager.h"
#include "meta/IndexManager.h"
#include "storage/CommonUtils.h"
#include "storage/BaseProcessor.h"

namespace nebula {
namespace storage {

/**
 * OperatorList used record all scan filters, before create scan policy,
 * need collect all columns and values for prefix string.
 * std::tuple<Column name, Value, Operator>;
 */
using OperatorItem = std::tuple<std::string, VariantType, RelationalExpression::Operator>;

enum RelationType : uint8_t {
    kGTRel,
    kGERel,
    kLTRel,
    kLERel,
    kEQRel,
    kNull,
};

struct Bound {
    RelationType rel_;
    VariantType  val_;
    Bound() {
        rel_ = RelationType::kNull;
    }
    Bound(RelationType rel, const VariantType& val) {
        rel_ = rel;
        val_ = val;
    }
};

struct ScanBound {
    Bound beginBound_;
    Bound endBound_;
    ScanBound() {
        beginBound_ = Bound();
        endBound_ = Bound();
    }
    ScanBound(const Bound& begin, const Bound& end) {
        beginBound_ = begin;
        endBound_ = end;
    }
};

class IndexPolicyMaker {
public:
    virtual ~IndexPolicyMaker() = default;

protected:
    explicit IndexPolicyMaker(meta::SchemaManager *schemaMan,
                              meta::IndexManager* indexMan)
        : schemaMan_(schemaMan)
        , indexMan_(indexMan) {}

    /**
     * Details Entry method of index scan policy preparation, process logic as below :
     *         1, Trigger optimizer to optimize expression nodes
     *         2, Traverse the optimized expression, analyze and confirm PolicyType,
     *            confirm scanning mode.
     * Param  filter : From encoded string of where clause.
     * Return ErrorCode
     **/
    cpp2::ErrorCode preparePolicy(const std::string &filter);

    /**
     * Details Index scan policy generator. Confirm how to generate the scan policy
     *         through the information generated by policyPrepare. Generate corresponding
     *         execution policy according to PolicyType.
     *         In this method, it is best to use as many index columns as possible.
     **/
    bool buildPolicy();

    /**
     * Details Evaluate filter conditions.
     */
    bool exprEval(Getters &getters);

    static RelationType toRel(RelationalExpression::Operator op);

private:
    cpp2::ErrorCode decodeExpression(const std::string &filter);

    /**
     * Details Entry method of expresion traverse.
     */

    cpp2::ErrorCode traversalExpression(const Expression *expr);

    RelationalExpression::Operator reversalRelationalExprOP(RelationalExpression::Operator op);

    bool writeScanItem(const std::string& prop, const OperatorItem& item);

protected:
    meta::SchemaManager*                     schemaMan_{nullptr};
    meta::IndexManager*                      indexMan_{nullptr};
    std::unique_ptr<ExpressionContext>       expCtx_{nullptr};
    std::unique_ptr<Expression>              exp_{nullptr};
    std::shared_ptr<nebula::cpp2::IndexItem> index_{nullptr};
    bool                                     requiredFilter_{false};
    std::vector<OperatorItem>                operatorList_;
    // map<field_name, scan_item>
    std::map<std::string, ScanBound>         scanItems_;
};
}  // namespace storage
}  // namespace nebula
#endif  // STORAGE_INDEXPOLICYMAKER_H
