// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#ifndef DORIS_BE_SRC_QUERY_EXPRS_AGGREGATE_FUNCTIONS_H
#define DORIS_BE_SRC_QUERY_EXPRS_AGGREGATE_FUNCTIONS_H

//#include "exprs/opcode_registry.h"
#include "udf/udf.h"
#include "udf/udf_internal.h"
#include "olap/hll.h"

namespace doris {

class HllSetResolver;
class HybirdSetBase;

// Collection of builtin aggregate functions. Aggregate functions implement
// the various phases of the aggregation: Init(), Update(), Serialize(), Merge(),
// and Finalize(). Not all functions need to implement all of the steps and
// some of the parts can be reused across different aggregate functions.
// This functions are implemented using the UDA interface.

class AggregateFunctions {
public:
    // Initializes dst to NULL.
    static void init_null(doris_udf::FunctionContext*, doris_udf::AnyVal* dst);
    // Initializes dst to NULL and sets dst->ptr to NULL.
    static void init_null_string(doris_udf::FunctionContext* c, doris_udf::StringVal* dst);

    // Initializes dst to 0.
    template <typename T>
    static void init_zero(doris_udf::FunctionContext*, T* dst);

    template<typename SRC_VAL, typename DST_VAL>
    static void sum_remove(doris_udf::FunctionContext* ctx, const SRC_VAL& src, DST_VAL* dst);

    // doris_udf::StringVal GetValue() function that returns a copy of src
    static doris_udf::StringVal string_val_get_value(doris_udf::FunctionContext* ctx,
        const doris_udf::StringVal& src);
    static doris_udf::StringVal string_val_serialize_or_finalize(doris_udf::FunctionContext* ctx,
         const doris_udf::StringVal& src);

    // Implementation of Count and Count(*)
    static void count_update(doris_udf::FunctionContext*, const doris_udf::AnyVal& src,
        doris_udf::BigIntVal* dst);
    static void count_merge(doris_udf::FunctionContext*,
        const doris_udf::BigIntVal& src, doris_udf::BigIntVal* dst);
    static void count_remove(doris_udf::FunctionContext*,
        const doris_udf::AnyVal& src, doris_udf::BigIntVal* dst);
    static void count_star_update(doris_udf::FunctionContext*, doris_udf::BigIntVal* dst);

    static void count_star_remove(FunctionContext*, BigIntVal* dst);
    // Implementation of Avg.
    // TODO: Change this to use a fixed-sized BufferVal as intermediate type.
    static void avg_init(doris_udf::FunctionContext* ctx, doris_udf::StringVal* dst);
    template <typename T>
    static void avg_update(doris_udf::FunctionContext* ctx, const T& src,
        doris_udf::StringVal* dst);
    template <typename T>
    static void avg_remove(doris_udf::FunctionContext* ctx, const T& src,
        doris_udf::StringVal* dst);
    static void avg_merge(FunctionContext* ctx, const StringVal& src, StringVal*
dst);
    static doris_udf::DoubleVal avg_get_value(doris_udf::FunctionContext* ctx,
         const doris_udf::StringVal& val);
    static doris_udf::DoubleVal avg_finalize(doris_udf::FunctionContext* ctx,
         const doris_udf::StringVal& val);

    // Avg for timestamp. Uses avg_init() and AvgMerge().
    static void timestamp_avg_update(doris_udf::FunctionContext* ctx,
            const doris_udf::DateTimeVal& src,
            doris_udf::StringVal* dst);
    static void timestamp_avg_remove(doris_udf::FunctionContext* ctx,
            const doris_udf::DateTimeVal& src,
            doris_udf::StringVal* dst);
    static doris_udf::DateTimeVal timestamp_avg_get_value(doris_udf::FunctionContext* ctx,
            const doris_udf::StringVal& val);
    static doris_udf::DateTimeVal timestamp_avg_finalize(doris_udf::FunctionContext* ctx,
            const doris_udf::StringVal& val);

    // Avg for decimals.
    static void decimal_avg_init(doris_udf::FunctionContext* ctx, doris_udf::StringVal* dst);
    static void decimal_avg_update(doris_udf::FunctionContext* ctx,
            const doris_udf::DecimalVal& src,
            doris_udf::StringVal* dst);
    static void decimal_avg_merge(FunctionContext* ctx, const doris_udf::StringVal& src,
            doris_udf::StringVal* dst);
    static void decimal_avg_remove(doris_udf::FunctionContext* ctx,
            const doris_udf::DecimalVal& src,
            doris_udf::StringVal* dst);

    // static void decimal_avg_add_or_remove(doris_udf::FunctionContext* ctx,
    //        const doris_udf::DecimalVal& src,
    //        doris_udf::StringVal* dst, bool remove);
    // static void decimal_avg_add_or_remove(doris_udf::FunctionContext* ctx,
    //        const doris_udf::DecimalVal& src,
    //        doris_udf::StringVal* dst) {
    //    return decimal_avg_add_or_remove(ctx, src, dst, false);
    // }
    static doris_udf::DecimalVal decimal_avg_get_value(doris_udf::FunctionContext* ctx,
         const doris_udf::StringVal& val);
    static doris_udf::DecimalVal decimal_avg_finalize(doris_udf::FunctionContext* ctx,
         const doris_udf::StringVal& val);

    // SumUpdate, SumMerge
    template <typename SRC_VAL, typename DST_VAL>
    static void sum(doris_udf::FunctionContext*, const SRC_VAL& src, DST_VAL* dst);

    // MinUpdate/MinMerge
    template <typename T>
    static void min(doris_udf::FunctionContext*, const T& src, T* dst);

    // MaxUpdate/MaxMerge
    template <typename T>
    static void max(doris_udf::FunctionContext*, const T& src, T* dst);


    // String concat
    static void string_concat(
            doris_udf::FunctionContext*,
            const doris_udf::StringVal& src,
            const doris_udf::StringVal& separator,
            doris_udf::StringVal* result);

      /// String concat
    static void string_concat_update(FunctionContext*,
            const StringVal& src, StringVal* result);
    static void string_concat_update(FunctionContext*,
            const StringVal& src, const StringVal& separator, StringVal* result);
    static void string_concat_merge(FunctionContext*,
            const StringVal& src, StringVal* result);
    static StringVal string_concat_finalize(FunctionContext*,
            const StringVal& src);

    // Probabilistic Counting (PC), a distinct estimate algorithms.
    // Probabilistic Counting with Stochastic Averaging (PCSA) is a variant
    // of PC that runs faster and usually gets equally accurate results.
    static void pc_init(doris_udf::FunctionContext*, doris_udf::StringVal* slot);

    template <typename T>
    static void pc_update(doris_udf::FunctionContext*, const T& src, doris_udf::StringVal* dst);
    template <typename T>
    static void pcsa_update(doris_udf::FunctionContext*, const T& src, doris_udf::StringVal* dst);

    static void pc_merge(
            doris_udf::FunctionContext*,
            const doris_udf::StringVal& src,
            doris_udf::StringVal* dst);

    static doris_udf::StringVal pc_finalize(
            doris_udf::FunctionContext*,
            const doris_udf::StringVal& src);

    static doris_udf::StringVal pcsa_finalize(
            doris_udf::FunctionContext*,
            const doris_udf::StringVal& src);

    // Hyperloglog distinct estimate algorithm.
    // See these papers for more details.
    // 1) Hyperloglog: The analysis of a near-optimal cardinality estimation
    // algorithm (2007)
    // 2) HyperLogLog in Practice (paper from google with some improvements)
    static void hll_init(doris_udf::FunctionContext*, doris_udf::StringVal* slot);
    template <typename T>
    static void hll_update(doris_udf::FunctionContext*, const T& src, doris_udf::StringVal* dst);
    static void hll_merge(
            doris_udf::FunctionContext*,
            const doris_udf::StringVal& src,
            doris_udf::StringVal* dst);
    static doris_udf::StringVal hll_finalize(
            doris_udf::FunctionContext*,
            const doris_udf::StringVal& src);

    // count and sum distinct algorithm in multi distinct
    template <typename T>
    static void count_or_sum_distinct_numeric_init(doris_udf::FunctionContext* ctx, doris_udf::StringVal* dst);
    template <typename T>
    static void count_or_sum_distinct_numeric_update(FunctionContext* ctx, T& src, StringVal* dst);
    template <typename T>
    static void count_or_sum_distinct_numeric_merge(FunctionContext* ctx, StringVal& src, StringVal* dst);
    template <typename T>
    static StringVal count_or_sum_distinct_numeric_serialize(FunctionContext* ctx, const StringVal& state_sv);
    template <typename T>
    static BigIntVal count_or_sum_distinct_numeric_finalize(FunctionContext* ctx, const StringVal& state_sv);

    // count distinct in multi distinct for string 
    static void count_distinct_string_init(doris_udf::FunctionContext* ctx, doris_udf::StringVal* dst);
    static void count_distinct_string_update(FunctionContext* ctx, StringVal& src, StringVal* dst);
    static void count_distinct_string_merge(FunctionContext* ctx, StringVal& src, StringVal* dst);
    static StringVal count_distinct_string_serialize(FunctionContext* ctx, const StringVal& state_sv);
    static BigIntVal count_distinct_string_finalize(FunctionContext* ctx, const StringVal& state_sv);
 
    // count distinct in multi distinct for decimal
    static void count_or_sum_distinct_decimal_init(doris_udf::FunctionContext* ctx, doris_udf::StringVal* dst);
    static void count_or_sum_distinct_decimal_update(FunctionContext* ctx, DecimalVal& src, StringVal* dst);
    static void count_or_sum_distinct_decimal_merge(FunctionContext* ctx, StringVal& src, StringVal* dst);
    static StringVal count_or_sum_distinct_decimal_serialize(FunctionContext* ctx, const StringVal& state_sv);
    static BigIntVal count_distinct_decimal_finalize(FunctionContext* ctx, const StringVal& state_sv);
    static DecimalVal sum_distinct_decimal_finalize(FunctionContext* ctx, const StringVal& state_sv);

    // count distinct in multi disticnt for Date
    static void count_distinct_date_init(doris_udf::FunctionContext* ctx, doris_udf::StringVal* dst);
    static void count_distinct_date_update(FunctionContext* ctx, DateTimeVal& src, StringVal* dst);
    static void count_distinct_date_merge(FunctionContext* ctx, StringVal& src, StringVal* dst);
    static StringVal count_distinct_date_serialize(FunctionContext* ctx, const StringVal& state_sv);
    static BigIntVal count_distinct_date_finalize(FunctionContext* ctx, const StringVal& state_sv);
 
    template <typename T>
    static BigIntVal sum_distinct_bigint_finalize(FunctionContext* ctx, const StringVal& state_sv);
    template <typename T>
    static LargeIntVal sum_distinct_largeint_finalize(FunctionContext* ctx, const StringVal& state_sv);
    template <typename T>
    static DoubleVal sum_distinct_double_finalize(FunctionContext* ctx, const StringVal& state_sv); 

    /// Knuth's variance algorithm, more numerically stable than canonical stddev
    /// algorithms; reference implementation:
    /// http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm
    static void knuth_var_init(FunctionContext* context, StringVal* val);
    template <typename T>
        static void knuth_var_update(FunctionContext* context, const T& input, StringVal* val);
    static void knuth_var_merge(FunctionContext* context, const StringVal& src,
                              StringVal* dst);
    static DoubleVal knuth_var_finalize(FunctionContext* context, const StringVal& val);

    /// Calculates the biased variance, uses KnuthVar Init-Update-Merge functions
    static DoubleVal knuth_var_pop_finalize(FunctionContext* context, const StringVal& val);

    /// Calculates STDDEV, uses KnuthVar Init-Update-Merge functions
    static DoubleVal knuth_stddev_finalize(FunctionContext* context, const StringVal& val);

    /// Calculates the biased STDDEV, uses KnuthVar Init-Update-Merge functions
    static DoubleVal knuth_stddev_pop_finalize(FunctionContext* context, const StringVal& val);

    /// ----------------------------- Analytic Functions ---------------------------------
    /// Analytic functions implement the UDA interface (except Merge(), Serialize()) and are
    /// used internally by the AnalyticEvalNode. Some analytic functions store intermediate
    /// state as a StringVal which is needed for multiple calls to Finalize(), so some fns
    /// also implement a (private) GetValue() method to just return the value. In that
    /// case, Finalize() is only called at the end to clean up.

    // Initializes the state for RANK and DENSE_RANK
    static void rank_init(doris_udf::FunctionContext*, doris_udf::StringVal* slot);

    // Update state for RANK
    static void rank_update(doris_udf::FunctionContext*, doris_udf::StringVal* dst);

    // Update state for DENSE_RANK
    static void dense_rank_update(doris_udf::FunctionContext*, doris_udf::StringVal* dst);

    // Returns the result for RANK and prepares the state for the next Update().
    static doris_udf::BigIntVal rank_get_value(doris_udf::FunctionContext*,
        doris_udf::StringVal& src);

    // Returns the result for DENSE_RANK and prepares the state for the next Update().
    // TODO: Implement DENSE_RANK with a single doris_udf::BigIntVal. Requires src can be modified,
    // AggFnEvaluator would need to handle copying the src doris_udf::AnyVal back into the src slot.
    static doris_udf::BigIntVal dense_rank_get_value(doris_udf::FunctionContext*,
        doris_udf::StringVal& src);

    // Returns the result for RANK and DENSE_RANK and cleans up intermediate state in src.
    static doris_udf::BigIntVal rank_finalize(doris_udf::FunctionContext*,
         doris_udf::StringVal& src);

    // Implements LAST_VALUE.
    template <typename T>
    static void last_val_update(doris_udf::FunctionContext*, const T& src, T* dst);
    template <typename T>
    static void last_val_remove(doris_udf::FunctionContext*, const T& src, T* dst);

    // Implements FIRST_VALUE.
    template <typename T>
    static void first_val_update(doris_udf::FunctionContext*, const T& src, T* dst);
    // Implements FIRST_VALUE for some windows that require rewrites during planning.
    // The doris_udf::BigIntVal is unused by first_val_rewrite_update() (it is used by the
    // AnalyticEvalNode).
    template <typename T>
    static void first_val_rewrite_update(doris_udf::FunctionContext*, const T& src,
            const doris_udf::BigIntVal&, T* dst);

    // OffsetFn*() implement LAG and LEAD. Init() sets the default value (the last
    // constant parameter) as dst.
    template <typename T>
    static void offset_fn_init(doris_udf::FunctionContext*, T* dst);

    // Update() takes all the parameters to LEAD/LAG, including the integer offset and
    // the default value, neither which are needed by Update(). (The offset is already
    // used in the window for the analytic fn evaluation and the default value is set
    // in Init().
    template <typename T>
    static void offset_fn_update(doris_udf::FunctionContext*, const T& src,
        const doris_udf::BigIntVal&, const T&, T* dst);

    //  HLL value type calculate
    //  init sets buffer
    static void hll_union_agg_init(doris_udf::FunctionContext*, doris_udf::StringVal* slot);
    // fill all register accroading to hll set type
    static void hll_union_agg_update(doris_udf::FunctionContext*, const doris_udf::StringVal& src, 
                                     doris_udf::StringVal* dst);
    // merge the register value
    static void hll_union_agg_merge(
                          doris_udf::FunctionContext*,
                          const doris_udf::StringVal& src,
                          doris_udf::StringVal* dst);
    // return result
    static doris_udf::StringVal hll_union_agg_finalize(
                                            doris_udf::FunctionContext*,
                                            const doris_udf::StringVal& src);
    // calculate result
    static int64_t hll_algorithm(const doris_udf::StringVal& src);
    static void hll_union_parse_and_cal(HllSetResolver& resolver, StringVal* dst);
};

}

#endif

