/*
 * 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.
 */
#pragma once

#include <cstdint>
#include <memory>
#include <string_view>
#include <vector>

#include "arrow/array/array_base.h"
#include "arrow/util/bit_util.h"

namespace arrow {
class StructArray;
}  // namespace arrow

namespace paimon {
class MemoryPool;

/// Pre-cached column metadata for fast access without virtual function calls or checked_cast.
struct CachedColumnMeta {
    /// Null bitmap pointer. nullptr means no nulls (all valid).
    const uint8_t* null_bitmap = nullptr;
    /// Arrow array offset (for sliced arrays).
    int64_t array_offset = 0;
    /// For fixed-width types: raw pointer to values buffer (buffer[1]).
    /// For variable-length types (STRING/BINARY): raw pointer to data buffer (buffer[2]).
    const uint8_t* values_data = nullptr;
    /// For variable-length types (STRING/BINARY): raw pointer to offsets buffer (buffer[1]).
    const int32_t* offsets = nullptr;

    /// Fast null check: directly reads validity bitmap bit.
    inline bool IsNull(int64_t row_id) const {
        return null_bitmap != nullptr &&
               !arrow::bit_util::GetBit(null_bitmap, array_offset + row_id);
    }

    /// Fast fixed-width value access (INT8/INT16/INT32/INT64/FLOAT/DOUBLE/DATE32/TIMESTAMP).
    template <typename T>
    inline T GetFixed(int64_t row_id) const {
        return reinterpret_cast<const T*>(values_data)[array_offset + row_id];
    }

    /// Fast boolean value access (bit-packed in values buffer).
    inline bool GetBool(int64_t row_id) const {
        return arrow::bit_util::GetBit(values_data, array_offset + row_id);
    }

    /// Fast string_view access for non-dictionary STRING/BINARY columns.
    inline std::string_view GetVarLenView(int64_t row_id) const {
        int64_t idx = array_offset + row_id;
        int32_t start = offsets[idx];
        int32_t length = offsets[idx + 1] - start;
        return {reinterpret_cast<const char*>(values_data) + start, static_cast<size_t>(length)};
    }
};

struct ColumnarBatchContext {
    ColumnarBatchContext(const arrow::ArrayVector& array_vec_in,
                         const std::shared_ptr<MemoryPool>& pool_in)
        : pool(pool_in), array_vec(array_vec_in) {
        BuildCachedMeta();
    }

    std::shared_ptr<MemoryPool> pool;
    arrow::ArrayVector array_vec;
    /// Pre-cached metadata per column for fast access.
    std::vector<CachedColumnMeta> cached_meta;

 private:
    void BuildCachedMeta() {
        cached_meta.resize(array_vec.size());
        for (size_t i = 0; i < array_vec.size(); i++) {
            const auto* array = array_vec[i].get();
            auto& meta = cached_meta[i];
            meta.array_offset = array->offset();

            // Cache null bitmap
            if (array->null_count() != 0) {
                meta.null_bitmap = array->null_bitmap_data();
            }

            // Cache data pointers based on type
            const auto& array_data = array->data();
            switch (array->type_id()) {
                case arrow::Type::BOOL:
                case arrow::Type::INT8:
                case arrow::Type::INT16:
                case arrow::Type::INT32:
                case arrow::Type::DATE32:
                case arrow::Type::INT64:
                case arrow::Type::FLOAT:
                case arrow::Type::DOUBLE: {
                    // Fixed-width: values in buffer[1]
                    if (array_data->buffers.size() > 1 && array_data->buffers[1]) {
                        meta.values_data = array_data->buffers[1]->data();
                    }
                    break;
                }
                case arrow::Type::STRING:
                case arrow::Type::BINARY: {
                    // Variable-length: offsets in buffer[1], data in buffer[2]
                    if (array_data->buffers.size() > 2) {
                        if (array_data->buffers[1]) {
                            meta.offsets =
                                reinterpret_cast<const int32_t*>(array_data->buffers[1]->data());
                        }
                        if (array_data->buffers[2]) {
                            meta.values_data = array_data->buffers[2]->data();
                        }
                    }
                    break;
                }
                default:
                    // TIMESTAMP, DECIMAL, DICTIONARY, LIST, MAP, STRUCT — not cached, use array_vec
                    // fallback
                    break;
            }
        }
    }
};
}  // namespace paimon
