// 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.

#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <gtest/gtest.h>
#include "exprs/expr.h"
#include "runtime/primitive_type.h"
#include "runtime/row_batch.h"
#include "runtime/tuple_row.h"
#include "runtime/runtime_state.h"
#include "gen_cpp/PaloInternalService_types.h"
#include "gen_cpp/Types_types.h"
#include "gen_cpp/Exprs_types.h"
#include "exec/sort_exec_exprs.h"
#include "runtime/sorter.h"
#include "runtime/buffered_block_mgr.h"
#include "runtime/descriptors.h"
#include "common/object_pool.h"
#include "gen_cpp/Descriptors_types.h"
#include "gen_cpp/PlanNodes_types.h"
#include "exprs/expr.h"
#include "util/debug_util.h"

namespace doris {

class SorterTest : public testing::Test {
public:
    RowBatch* CreateRowBatch(int num_rows);
    ObjectPool* get_object_pool() {return _object_pool;}
    RuntimeState* get_runtime_state() {return _runtime_state;}
    SorterTest() {
        _object_pool = new ObjectPool();
        _runtime_state = new RuntimeState("SorterTest");
        _runtime_state->exec_env_ = &_exec_env;
        _runtime_state->create_block_mgr();
        {
            TExpr expr;
            {
                TExprNode node;
    
                node.node_type = TExprNodeType::SLOT_REF;
                node.type = ToTColumnTypeThrift(TPrimitiveType::BIGINT);
                node.num_children = 0;
                TSlotRef data;
                data.slot_id = 0;
                data.tuple_id = 0;
                node.__set_slot_ref(data);
                expr.nodes.push_back(node);
            }
            _sort_tuple_slot_expr.push_back(expr);
        }
        {
            TExpr expr;
            {
                TExprNode node;

                node.node_type = TExprNodeType::SLOT_REF;
                node.type = ToTColumnTypeThrift(TPrimitiveType::BIGINT);
                node.num_children = 0;
                TSlotRef data;
                data.slot_id = 1;
                data.tuple_id = 1;
                node.__set_slot_ref(data);
                expr.nodes.push_back(node);
            }
            _ordering_exprs.push_back(expr);
        }
        _is_asc_order.push_back(true);
        _nulls_first.push_back(true);

        
        {
            TTupleDescriptor tuple_desc;
            TDescriptorTable thrift_desc_tbl;
            {
                tuple_desc.__set_id(0);
                tuple_desc.__set_byteSize(8);
                tuple_desc.__set_numNullBytes(1);
                thrift_desc_tbl.tupleDescriptors.push_back(tuple_desc);
            }
            {
                tuple_desc.__set_id(1);
                tuple_desc.__set_byteSize(8);
                tuple_desc.__set_numNullBytes(1);
                thrift_desc_tbl.tupleDescriptors.push_back(tuple_desc);
            }
            
            TSlotDescriptor slot_desc; 
            {
                slot_desc.__set_id(0);
                slot_desc.__set_parent(0);
                slot_desc.__set_slotType(TPrimitiveType::BIGINT);
                slot_desc.__set_columnPos(0);
                slot_desc.__set_byteOffset(0);
                slot_desc.__set_nullIndicatorByte(0);
                slot_desc.__set_nullIndicatorBit(-1);
                slot_desc.__set_slotIdx(0);
                slot_desc.__set_isMaterialized(true);
                thrift_desc_tbl.slotDescriptors.push_back(slot_desc);
            }
            {
                slot_desc.__set_id(1);
                slot_desc.__set_parent(1);
                slot_desc.__set_slotType(TPrimitiveType::BIGINT);
                slot_desc.__set_columnPos(0);
                slot_desc.__set_byteOffset(0);
                slot_desc.__set_nullIndicatorByte(0);
                slot_desc.__set_nullIndicatorBit(-1);
                slot_desc.__set_slotIdx(0);
                slot_desc.__set_isMaterialized(true);
                thrift_desc_tbl.slotDescriptors.push_back(slot_desc);
            }
            Status status = DescriptorTbl::Create(_object_pool, thrift_desc_tbl, &_desc_tbl);
            DCHECK(status.ok());
            _runtime_state->set_desc_tbl(_desc_tbl);
        }
        {  
            std::vector<TTupleId> row_tuples;
            std::vector<bool> nullable_tuples;
            nullable_tuples.push_back(0);
            row_tuples.push_back(0);
            _child_row_desc =  new RowDescriptor(*_desc_tbl, row_tuples, nullable_tuples);
        }
        {  
            std::vector<TTupleId> row_tuples;
            std::vector<bool> nullable_tuples;
            nullable_tuples.push_back(1);
            row_tuples.push_back(1);
            _output_row_desc = new RowDescriptor(*_desc_tbl, row_tuples, nullable_tuples);
        }
        _runtime_profile = new RuntimeProfile(get_object_pool(), "sorter");
        

    }
    virtual ~SorterTest() {
        delete _child_row_desc;
        delete _output_row_desc;
        delete _object_pool;
        delete _runtime_state;
        delete _runtime_profile;
    }

protected:
    virtual void SetUp() {
    }

private:
    ExecEnv _exec_env;
    RuntimeState *_runtime_state;
    RowDescriptor *_child_row_desc;
    RowDescriptor *_output_row_desc;
    RuntimeProfile *_runtime_profile;
    std::vector<TExpr> _sort_tuple_slot_expr;
    std::vector<TExpr> _ordering_exprs; 
    std::vector<bool> _is_asc_order;
    std::vector<bool> _nulls_first;
    DescriptorTbl* _desc_tbl;
    ObjectPool *_object_pool;
};

TEST_F(SorterTest, init_sort_exec_exprs) {
    // empty  sort_tuple_slot_expr
    {
        SortExecExprs exec_exprs;
        Status status = exec_exprs.init(_ordering_exprs, NULL, get_object_pool());
        ASSERT_TRUE(status.ok());
    }
    // full sort_tuple_slot_expr
    {
        SortExecExprs exec_exprs;
        Status status = exec_exprs.init(_ordering_exprs,
            &_sort_tuple_slot_expr, get_object_pool());
        ASSERT_TRUE(status.ok());
    }
}

TEST_F(SorterTest, prepare_sort_exec_exprs) {
    {
        SortExecExprs exec_exprs;
        Status status = exec_exprs.init(_ordering_exprs, NULL, get_object_pool());
        ASSERT_TRUE(status.ok());
        status = exec_exprs.prepare(_runtime_state, *_child_row_desc, *_output_row_desc);
        ASSERT_TRUE(status.ok());
    }

    {
        SortExecExprs exec_exprs;
        Status status = exec_exprs.init(_ordering_exprs,
            &_sort_tuple_slot_expr, get_object_pool());
        ASSERT_TRUE(status.ok());
        status = exec_exprs.prepare(_runtime_state, *_child_row_desc, *_output_row_desc);
        ASSERT_TRUE(status.ok());
    }
}

RowBatch* SorterTest::CreateRowBatch(int num_rows) {
    RowBatch* batch = _object_pool->Add(new RowBatch(*_child_row_desc, num_rows));
    int64_t* tuple_mem = reinterpret_cast<int64_t*>(
      batch->tuple_data_pool()->Allocate(sizeof(int64_t) * num_rows));

    for (int i = 0; i < num_rows; ++i) {
        int idx = batch->AddRow();
        TupleRow* row = batch->GetRow(idx);
        *tuple_mem = i;
        row->SetTuple(0, reinterpret_cast<Tuple*>(tuple_mem));

        batch->CommitLastRow();
        tuple_mem++;
    }
    return batch;
}

TEST_F(SorterTest, sorter_run_asc) {
    SortExecExprs exec_exprs;
    Status status = exec_exprs.init(_ordering_exprs, &_sort_tuple_slot_expr, _object_pool);
    ASSERT_TRUE(status.ok());
    status = exec_exprs.prepare(_runtime_state, *_child_row_desc, *_output_row_desc);
    ASSERT_TRUE(status.ok());
        
    TupleRowComparator less_than(
        exec_exprs.lhs_ordering_expr_ctxs(), exec_exprs.rhs_ordering_expr_ctxs(),
        _is_asc_order, _nulls_first);
    Sorter * sorter = new Sorter(
          less_than, exec_exprs.sort_tuple_slot_expr_ctxs(),
          _child_row_desc,
          _runtime_profile, _runtime_state);
          // new RuntimeProfile(get_object_pool(), "sorter"), _runtime_state);

    int num_rows = 5;
    RowBatch* batch = CreateRowBatch(num_rows);
    status = sorter->add_batch(batch);
    ASSERT_TRUE(status.ok());
    status = sorter->add_batch(batch);
    ASSERT_TRUE(status.ok());
    sorter->input_done();

    RowBatch output_batch(*_child_row_desc, 2 * num_rows);
    bool eos;
    status = sorter->get_next(&output_batch, &eos);
    ASSERT_TRUE(status.ok());

    //for (int i = 0; i < num_rows; i ++) {
    //     TupleRow* row = output_batch.GetRow(i);
    //     std::cout << "output row: " << PrintRow(row, *_child_row_desc) << std::endl;   
    //}
    ASSERT_EQ("[(0)]", PrintRow(output_batch.GetRow(0), *_child_row_desc));
    ASSERT_EQ("[(0)]", PrintRow(output_batch.GetRow(1), *_child_row_desc));
    ASSERT_EQ("[(1)]", PrintRow(output_batch.GetRow(2), *_child_row_desc));
    ASSERT_EQ("[(1)]", PrintRow(output_batch.GetRow(3), *_child_row_desc));
    ASSERT_EQ("[(2)]", PrintRow(output_batch.GetRow(4), *_child_row_desc));
    ASSERT_EQ("[(2)]", PrintRow(output_batch.GetRow(5), *_child_row_desc));
    ASSERT_EQ("[(3)]", PrintRow(output_batch.GetRow(6), *_child_row_desc));
    ASSERT_EQ("[(3)]", PrintRow(output_batch.GetRow(7), *_child_row_desc));
    ASSERT_EQ("[(4)]", PrintRow(output_batch.GetRow(8), *_child_row_desc));
    ASSERT_EQ("[(4)]", PrintRow(output_batch.GetRow(9), *_child_row_desc));

    delete sorter;
}

/* reveser order : exceed 16 elment, we use quick sort*/
TEST_F(SorterTest, sorter_run_desc_with_quick_sort) {
    SortExecExprs exec_exprs;
    Status status = exec_exprs.init(_ordering_exprs, &_sort_tuple_slot_expr, _object_pool);
    ASSERT_TRUE(status.ok());
    status = exec_exprs.prepare(_runtime_state, *_child_row_desc, *_output_row_desc);
    ASSERT_TRUE(status.ok());

    _is_asc_order.clear();
    _is_asc_order.push_back(false);
    TupleRowComparator less_than(
        exec_exprs.lhs_ordering_expr_ctxs(), exec_exprs.rhs_ordering_expr_ctxs(),
        _is_asc_order, _nulls_first);
    Sorter * sorter = new Sorter(
          less_than, exec_exprs.sort_tuple_slot_expr_ctxs(),
          _child_row_desc,
          _runtime_profile, _runtime_state);
          // new RuntimeProfile(get_object_pool(), "sorter"), _runtime_state);

    int num_rows = 5;
    RowBatch* batch = CreateRowBatch(num_rows);
    for (int i = 0; i < 5; i++) {
        status = sorter->add_batch(batch);
        ASSERT_TRUE(status.ok());
    }

    sorter->input_done();

    RowBatch output_batch(*_child_row_desc, 2 * num_rows);
    bool eos;
    status = sorter->get_next(&output_batch, &eos);
    ASSERT_TRUE(status.ok());

    ASSERT_EQ("[(4)]", PrintRow(output_batch.GetRow(0), *_child_row_desc));
    ASSERT_EQ("[(4)]", PrintRow(output_batch.GetRow(1), *_child_row_desc));
    ASSERT_EQ("[(4)]", PrintRow(output_batch.GetRow(2), *_child_row_desc));
    ASSERT_EQ("[(4)]", PrintRow(output_batch.GetRow(3), *_child_row_desc));
    ASSERT_EQ("[(4)]", PrintRow(output_batch.GetRow(4), *_child_row_desc));
    ASSERT_EQ("[(3)]", PrintRow(output_batch.GetRow(5), *_child_row_desc));

    delete sorter;
}

TEST_F(SorterTest, sorter_run_desc) {
    SortExecExprs exec_exprs;
    Status status = exec_exprs.init(_ordering_exprs, &_sort_tuple_slot_expr, _object_pool);
    ASSERT_TRUE(status.ok());
    status = exec_exprs.prepare(_runtime_state, *_child_row_desc, *_output_row_desc);
    ASSERT_TRUE(status.ok());

    _is_asc_order.clear();
    _is_asc_order.push_back(false);
    TupleRowComparator less_than(
        exec_exprs.lhs_ordering_expr_ctxs(), exec_exprs.rhs_ordering_expr_ctxs(),
        _is_asc_order, _nulls_first);
    Sorter * sorter = new Sorter(
          less_than, exec_exprs.sort_tuple_slot_expr_ctxs(),
          _child_row_desc,
          _runtime_profile, _runtime_state);
          // new RuntimeProfile(get_object_pool(), "sorter"), _runtime_state);

    int num_rows = 5;
    RowBatch* batch = CreateRowBatch(num_rows);
    status = sorter->add_batch(batch);
    ASSERT_TRUE(status.ok());
    status = sorter->add_batch(batch);
    ASSERT_TRUE(status.ok());
    sorter->input_done();

    RowBatch output_batch(*_child_row_desc, 2 * num_rows);
    bool eos;
    status = sorter->get_next(&output_batch, &eos);
    ASSERT_TRUE(status.ok());


    // for (int i = 0; i < num_rows; i ++) {
    //     TupleRow* row = output_batch.GetRow(i);
    //     std::cout << "output row: " << PrintRow(row, *_child_row_desc) << std::endl;   
    //}
    ASSERT_EQ("[(4)]", PrintRow(output_batch.GetRow(0), *_child_row_desc));
    ASSERT_EQ("[(4)]", PrintRow(output_batch.GetRow(1), *_child_row_desc));
    ASSERT_EQ("[(3)]", PrintRow(output_batch.GetRow(2), *_child_row_desc));
    ASSERT_EQ("[(3)]", PrintRow(output_batch.GetRow(3), *_child_row_desc));
    ASSERT_EQ("[(2)]", PrintRow(output_batch.GetRow(4), *_child_row_desc));
    ASSERT_EQ("[(2)]", PrintRow(output_batch.GetRow(5), *_child_row_desc));
    ASSERT_EQ("[(1)]", PrintRow(output_batch.GetRow(6), *_child_row_desc));
    ASSERT_EQ("[(1)]", PrintRow(output_batch.GetRow(7), *_child_row_desc));
    ASSERT_EQ("[(0)]", PrintRow(output_batch.GetRow(8), *_child_row_desc));
    ASSERT_EQ("[(0)]", PrintRow(output_batch.GetRow(9), *_child_row_desc));

    delete sorter;
}
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    impala::CpuInfo::Init();
    return RUN_ALL_TESTS();
}

/* vim: set ts=4 sw=4 sts=4 tw=100 noet: */
