// 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_RUNTIME_DPP_SINK_INTERNAL_H
#define DORIS_BE_RUNTIME_DPP_SINK_INTERNAL_H

#include <vector>
#include <string>

#include "common/status.h"
#include "gen_cpp/Types_types.h"
#include "runtime/raw_value.h"
#include "runtime/primitive_type.h"
#include "util/hash_util.hpp"

namespace doris {

class ExprContext;
class MemTracker;
class ObjectPool;
class RuntimeState;
class RowDescriptor;
class TRollupSchema;
class TPartitionKey;
class TPartitionRange;
class TRangePartition;

class RollupSchema {
public:
    RollupSchema();

    ~RollupSchema();

    static Status from_thrift(ObjectPool* pool,
                              const TRollupSchema& t_schema,
                              RollupSchema* schema);

    Status prepare(RuntimeState* state, const RowDescriptor& row_desc, MemTracker* mem_tracker);

    Status open(RuntimeState* state);

    Status close(RuntimeState* state);

    const std::string& keys_type() const {
        return _keys_type;
    }

    const std::vector<ExprContext*>& keys() const {
        return _key_ctxs;
    }

    const std::vector<ExprContext*>& values() const {
        return _value_ctxs;
    }

    const std::vector<TAggregationType::type>& value_ops() const {
        return _value_ops;
    }

private:
    std::string _keys_type;
    std::vector<ExprContext*> _key_ctxs;
    std::vector<ExprContext*> _value_ctxs;
    std::vector<TAggregationType::type> _value_ops;
};

class PartRangeKey {
public:
    PartRangeKey() {
    }

    ~PartRangeKey() {
    }

    static const PartRangeKey& pos_infinite() {
        return _s_pos_infinite;
    }

    static const PartRangeKey& neg_infinite() {
        return _s_neg_infinite;
    }

    static Status from_thrift(ObjectPool* pool,
            const TPartitionKey& t_key,
            PartRangeKey* key);

    static Status from_value(
            PrimitiveType type,
            void* value,
            PartRangeKey* key);

    bool operator==(const PartRangeKey& other) const {
        if (_sign != other._sign) {
            return false;
        }
        if (_sign != 0) {
            return true;
        }

        return RawValue::compare(_key, other._key, TypeDescriptor(_type)) == 0;
    }

    bool operator!=(const PartRangeKey& other) const {
        return !(*this == other);
    }

    bool operator<(const PartRangeKey& other) const {
        if (_sign < other._sign) {
            return true;
        } else if (_sign > other._sign) {
            return false;
        }
        if (_sign != 0) {
            return false;
        }
        return RawValue::compare(_key, other._key, TypeDescriptor(_type)) < 0;
    }

    bool operator<=(const PartRangeKey& other) const {
        return *this < other || *this == other;
    }

    bool operator>(const PartRangeKey& other) const {
        return !(*this <= other);
    }

    bool operator>=(const PartRangeKey& other) const {
        return !(*this < other);
    }

    std::string debug_string() const {
        std::stringstream debug_str;
        debug_str << "sign: " << _sign << "; "
                << "type: " << type_to_string(_type) << "; "
                << "key: " << _str_key;
        return debug_str.str();
    }

private:
    // Used for static variables
    PartRangeKey(int sign) : _sign(sign) { }
    // When sign is less than 0, that key represent negative infinite
    // When sign is greater than 0, that key represent positive infinite
    // When sign is equal 0, that key represent normal key
    int _sign;
    PrimitiveType _type;
    void* _key;
    std::string _str_key;

    static PartRangeKey _s_pos_infinite;
    static PartRangeKey _s_neg_infinite;
};

class PartRange {
public:
    PartRange() {
    }

    ~PartRange() {
    }

    static const PartRange& all() {
        return _s_all_range;
    }

    static Status from_thrift(ObjectPool* pool,
                              const TPartitionRange& t_part_range,
                              PartRange* range);

    // return -1 : 'this' range is on the left of 'key'
    // return 0 : 'this' range contains 'key'
    // return 1 : 'this' range is on the right of 'key'
    int compare_key(const PartRangeKey& key) const {
        if (_start_key > key) {
            return 1;
        } else if (_start_key == key) {
            if (!_include_start_key) {
                return 1;
            } else {
                return 0;
            }
        } else if (_end_key < key) {
            return -1;
        } else if (_end_key == key) {
            if (!_include_end_key) {
                return -1;
            }
        }
        return 0;
    }

    // NOTE: operator "<" and ">" is reciprocal for PartRange, but is not always exist.
    // e.g. [1, 3) is neither '<' nor '=' nor '>' [2, 4)
    bool operator<(const PartRange& other) const {
        VLOG_ROW << "in PartRange::operator<: " << std::endl
            << "this: " << debug_string() << std::endl
            << "other: " << other.debug_string() << std::endl;
        if (_end_key < other._start_key) {
            VLOG_ROW << "in PartRange::operator<: result: " << true << std::endl;
            return true;
        } else if (_end_key == other._start_key) {
            if ((_include_end_key && other._include_start_key) == false) {
                VLOG_ROW << "in PartRange::operator<: result: " << true << std::endl;
                return true;
            }
            VLOG_ROW << "in PartRange::operator<. "
                    << "first.end=other.end. but include judge failed." << std::endl;
        }

        VLOG_ROW << "in PartRange::operator<: result: " << false << std::endl;
        return false;
    }

    std::string debug_string() const{
        std::stringstream debug_str;
        debug_str << "start_key: [" << _start_key.debug_string() << "]; "
                << "end_key: [" << _end_key.debug_string() << "]; "
                << "include_start: " << _include_start_key << "; "
                << "include_end: " << _include_end_key << ";";
        return debug_str.str();
    }
private:
    PartRange(const PartRangeKey& start_key, const PartRangeKey& end_key,
              bool include_start, bool include_end) :
            _start_key(start_key),
            _end_key(end_key),
            _include_start_key(include_start),
            _include_end_key(include_end) {
    }

    PartRangeKey _start_key;
    PartRangeKey _end_key;
    bool _include_start_key;
    bool _include_end_key;

    static PartRange _s_all_range;
};

class PartitionInfo {
public:
    PartitionInfo() : _id(-1), _distributed_bucket(0) {
    }

    static Status from_thrift(ObjectPool* pool,
                              const TRangePartition& t_partition,
                              PartitionInfo* partition);

    Status prepare(RuntimeState* state, const RowDescriptor& row_desc, MemTracker*);

    Status open(RuntimeState* state);

    Status close(RuntimeState* state);

    int64_t id() const {
        return _id;
    }

    const std::vector<ExprContext*>& distributed_expr_ctxs() const {
        return _distributed_expr_ctxs;
    }

    int distributed_bucket() const {
        return _distributed_bucket;
    }

    const PartRange& range() const {
        return _range;
    }
private:
    int64_t _id;
    PartRange _range;
    // Information used to distribute data
    // distribute exprs
    std::vector<ExprContext*> _distributed_expr_ctxs;
    int32_t _distributed_bucket;
};

// which tablet this batch belong to
// TODO(zc): remove rollup
struct TabletDesc {
    int64_t partition_id;
    uint32_t bucket_id;

    bool operator==(const TabletDesc& other) const {
        return (bucket_id == other.bucket_id) && (partition_id == other.partition_id);
    }
};

}

namespace std {

// TODO(zc)
template<>
struct hash<doris::TabletDesc> {
    std::size_t operator()(const doris::TabletDesc& desc) const {
        uint32_t seed = 0;
        seed = doris::HashUtil::crc_hash(&desc.partition_id, sizeof(desc.partition_id), seed);
        seed = doris::HashUtil::crc_hash(&desc.bucket_id, sizeof(desc.bucket_id), seed);
        return seed;
    }
};

}

#endif
