// 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 "exec/select_node.h"
#include "exprs/expr.h"
#include "gen_cpp/PlanNodes_types.h"
#include "runtime/row_batch.h"
#include "runtime/runtime_state.h"
#include "runtime/raw_value.h"

namespace doris {

SelectNode::SelectNode(
    ObjectPool* pool, const TPlanNode& tnode, const DescriptorTbl& descs)
    : ExecNode(pool, tnode, descs),
      _child_row_batch(NULL),
      _child_row_idx(0),
      _child_eos(false) {
}

Status SelectNode::prepare(RuntimeState* state) {
    RETURN_IF_ERROR(ExecNode::prepare(state));
    _child_row_batch.reset(
        new RowBatch(child(0)->row_desc(), state->batch_size(), mem_tracker()));
    return Status::OK;
}

Status SelectNode::open(RuntimeState* state) {
    RETURN_IF_ERROR(exec_debug_action(TExecNodePhase::OPEN));
    RETURN_IF_ERROR(ExecNode::open(state));
    RETURN_IF_ERROR(child(0)->open(state));
    return Status::OK;
}

Status SelectNode::get_next(RuntimeState* state, RowBatch* row_batch, bool* eos) {
    RETURN_IF_ERROR(exec_debug_action(TExecNodePhase::GETNEXT));
    RETURN_IF_CANCELLED(state);
    SCOPED_TIMER(_runtime_profile->total_time_counter());

    if (reached_limit() || (_child_row_idx == _child_row_batch->num_rows() && _child_eos)) {
        // we're already done or we exhausted the last child batch and there won't be any
        // new ones
        _child_row_batch->transfer_resource_ownership(row_batch);
        *eos = true;
        return Status::OK;
    }
    *eos = false;

    // start (or continue) consuming row batches from child
    while (true) {
        RETURN_IF_CANCELLED(state);
        if (_child_row_idx == _child_row_batch->num_rows()) {
            // fetch next batch
            _child_row_idx = 0;
            _child_row_batch->transfer_resource_ownership(row_batch);
            _child_row_batch->reset();
            if (row_batch->at_capacity()) {
                return Status::OK;
            }
            RETURN_IF_ERROR(child(0)->get_next(state, _child_row_batch.get(), &_child_eos));
        }

        if (copy_rows(row_batch)) {
            *eos = reached_limit()
                   || (_child_row_idx == _child_row_batch->num_rows() && _child_eos);
            if (*eos) {
                _child_row_batch->transfer_resource_ownership(row_batch);
            }
            return Status::OK;
        }

        if (_child_eos) {
            // finished w/ last child row batch, and child eos is true
            _child_row_batch->transfer_resource_ownership(row_batch);
            *eos = true;
            return Status::OK;
        }
    }

    return Status::OK;
}

bool SelectNode::copy_rows(RowBatch* output_batch) {
    ExprContext** ctxs = &_conjunct_ctxs[0];
    int num_ctxs = _conjunct_ctxs.size();

    for (; _child_row_idx < _child_row_batch->num_rows(); ++_child_row_idx) {
        // Add a new row to output_batch
        int dst_row_idx = output_batch->add_row();

        if (dst_row_idx == RowBatch::INVALID_ROW_INDEX) {
            return true;
        }

        TupleRow* dst_row = output_batch->get_row(dst_row_idx);
        TupleRow* src_row = _child_row_batch->get_row(_child_row_idx);

        if (ExecNode::eval_conjuncts(ctxs, num_ctxs, src_row)) {
            output_batch->copy_row(src_row, dst_row);
            output_batch->commit_last_row();
            ++_num_rows_returned;
            COUNTER_SET(_rows_returned_counter, _num_rows_returned);

            if (reached_limit()) {
                return true;
            }
        }
    }

    if (VLOG_ROW_IS_ON) {
        for (int i = 0; i < output_batch->num_rows(); ++i) {
            TupleRow* row = output_batch->get_row(i);
            VLOG_ROW << "SelectNode input row: " << row->to_string(row_desc());
        }
    }

    return output_batch->is_full() || output_batch->at_resource_limit();
}

Status SelectNode::close(RuntimeState* state) {
    if (is_closed()) {
        return Status::OK;
    }
    _child_row_batch.reset();
    return ExecNode::close(state);
}

}
