/*
 * 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 "paimon/utils/roaring_bitmap32.h"

#include <cassert>
#include <memory>
#include <utility>

#include "paimon/fs/file_system.h"
#include "paimon/io/byte_array_input_stream.h"
#include "paimon/result.h"
#include "roaring.hh"  // NOLINT(build/include_subdir)

namespace paimon {
namespace {
roaring::Roaring::const_iterator& GetIterator(void* iter) {
    return *(static_cast<roaring::Roaring::const_iterator*>(iter));
}
roaring::Roaring& GetRoaringBitmap(void* bitmap) {
    return *(static_cast<roaring::Roaring*>(bitmap));
}
}  // namespace

RoaringBitmap32::Iterator::Iterator(const RoaringBitmap32& bitmap) {
    iterator_ =
        new roaring::Roaring::const_iterator(GetRoaringBitmap(bitmap.roaring_bitmap_).begin());
}

RoaringBitmap32::Iterator::~Iterator() {
    if (iterator_) {
        delete static_cast<roaring::Roaring::const_iterator*>(iterator_);
    }
}

RoaringBitmap32::Iterator::Iterator(const RoaringBitmap32::Iterator& other) noexcept {
    *this = other;
}
RoaringBitmap32::Iterator& RoaringBitmap32::Iterator::operator=(
    const RoaringBitmap32::Iterator& other) noexcept {
    if (&other == this) {
        return *this;
    }
    if (!iterator_) {
        iterator_ = new roaring::Roaring::const_iterator(GetIterator(other.iterator_));
    } else {
        GetIterator(iterator_) = GetIterator(other.iterator_);
    }
    return *this;
}

RoaringBitmap32::Iterator::Iterator(RoaringBitmap32::Iterator&& other) noexcept {
    *this = std::move(other);
}

RoaringBitmap32::Iterator& RoaringBitmap32::Iterator::operator=(
    RoaringBitmap32::Iterator&& other) noexcept {
    if (&other == this) {
        return *this;
    }
    if (iterator_) {
        delete static_cast<roaring::Roaring::const_iterator*>(iterator_);
    }
    iterator_ = other.iterator_;
    other.iterator_ = nullptr;
    return *this;
}

int32_t RoaringBitmap32::Iterator::operator*() const {
    return *GetIterator(iterator_);
}
RoaringBitmap32::Iterator& RoaringBitmap32::Iterator::operator++() {
    ++GetIterator(iterator_);
    return *this;
}
bool RoaringBitmap32::Iterator::operator==(const Iterator& other) const {
    if (&other == this) {
        return true;
    }
    assert(iterator_ && other.iterator_);
    return GetIterator(iterator_) == GetIterator(other.iterator_);
}
bool RoaringBitmap32::Iterator::operator!=(const Iterator& other) const {
    return !(*this == other);
}

RoaringBitmap32::RoaringBitmap32() {
    roaring_bitmap_ = new roaring::Roaring();
}
RoaringBitmap32::~RoaringBitmap32() {
    if (roaring_bitmap_) {
        delete static_cast<roaring::Roaring*>(roaring_bitmap_);
    }
}

RoaringBitmap32::RoaringBitmap32(const RoaringBitmap32& other) noexcept {
    *this = other;
}
RoaringBitmap32& RoaringBitmap32::operator=(const RoaringBitmap32& other) noexcept {
    if (&other == this) {
        return *this;
    }
    if (!roaring_bitmap_) {
        roaring_bitmap_ = new roaring::Roaring(GetRoaringBitmap(other.roaring_bitmap_));
    } else {
        GetRoaringBitmap(roaring_bitmap_) = GetRoaringBitmap(other.roaring_bitmap_);
    }
    return *this;
}

RoaringBitmap32::RoaringBitmap32(RoaringBitmap32&& other) noexcept {
    *this = std::move(other);
}

RoaringBitmap32& RoaringBitmap32::operator=(RoaringBitmap32&& other) noexcept {
    if (&other == this) {
        return *this;
    }
    if (roaring_bitmap_) {
        delete static_cast<roaring::Roaring*>(roaring_bitmap_);
    }
    roaring_bitmap_ = other.roaring_bitmap_;
    other.roaring_bitmap_ = nullptr;
    return *this;
}

RoaringBitmap32& RoaringBitmap32::operator|=(const RoaringBitmap32& other) {
    GetRoaringBitmap(roaring_bitmap_) |= GetRoaringBitmap(other.roaring_bitmap_);
    return *this;
}
RoaringBitmap32& RoaringBitmap32::operator&=(const RoaringBitmap32& other) {
    GetRoaringBitmap(roaring_bitmap_) &= GetRoaringBitmap(other.roaring_bitmap_);
    return *this;
}
RoaringBitmap32& RoaringBitmap32::operator-=(const RoaringBitmap32& other) {
    GetRoaringBitmap(roaring_bitmap_) -= GetRoaringBitmap(other.roaring_bitmap_);
    return *this;
}

void RoaringBitmap32::Add(int32_t x) {
    GetRoaringBitmap(roaring_bitmap_).add(x);
}

void RoaringBitmap32::AddRange(int32_t min, int32_t max) {
    GetRoaringBitmap(roaring_bitmap_).addRange(min, max);
}

void RoaringBitmap32::RemoveRange(int32_t min, int32_t max) {
    GetRoaringBitmap(roaring_bitmap_).removeRange(min, max);
}

bool RoaringBitmap32::ContainsAny(int32_t min, int32_t max) const {
    auto iter = EqualOrLarger(min);
    if (iter != End() && *iter < max) {
        return true;
    }
    return false;
}

bool RoaringBitmap32::CheckedAdd(int32_t x) {
    if (Contains(x)) {
        return false;
    }
    Add(x);
    return true;
}

int32_t RoaringBitmap32::Cardinality() const {
    return GetRoaringBitmap(roaring_bitmap_).cardinality();
}

bool RoaringBitmap32::Contains(int32_t x) const {
    return GetRoaringBitmap(roaring_bitmap_).contains(x);
}

bool RoaringBitmap32::IsEmpty() const {
    return GetRoaringBitmap(roaring_bitmap_).isEmpty();
}

size_t RoaringBitmap32::GetSizeInBytes() const {
    return GetRoaringBitmap(roaring_bitmap_).getSizeInBytes();
}

void RoaringBitmap32::Flip(int32_t min, int32_t max) {
    GetRoaringBitmap(roaring_bitmap_).flip(min, max);
}

bool RoaringBitmap32::operator==(const RoaringBitmap32& other) const noexcept {
    if (this == &other) {
        return true;
    }
    assert(roaring_bitmap_ && other.roaring_bitmap_);
    return GetRoaringBitmap(roaring_bitmap_) == GetRoaringBitmap(other.roaring_bitmap_);
}

PAIMON_UNIQUE_PTR<Bytes> RoaringBitmap32::Serialize(MemoryPool* pool) const {
    GetRoaringBitmap(roaring_bitmap_).runOptimize();
    auto& bitmap = GetRoaringBitmap(roaring_bitmap_);
    // Use default pool if no pool is provided
    if (pool == nullptr) {
        pool = GetDefaultPool().get();
    }
    auto bytes = Bytes::AllocateBytes(bitmap.getSizeInBytes(), pool);
    bitmap.write(bytes->data());
    return bytes;
}

Status RoaringBitmap32::Deserialize(ByteArrayInputStream* input_stream) {
    const char* data = input_stream->GetRawData();
    PAIMON_ASSIGN_OR_RAISE(int64_t pos, input_stream->GetPos());
    PAIMON_ASSIGN_OR_RAISE(int64_t total_length, input_stream->Length());
    try {
        GetRoaringBitmap(roaring_bitmap_) =
            roaring::Roaring::readSafe(data, /*maxbytes=*/total_length - pos);
    } catch (...) {
        return Status::Invalid("catch exception in Deserialize() of RoaringBitmap32");
    }
    return input_stream->Seek(GetRoaringBitmap(roaring_bitmap_).getSizeInBytes(),
                              SeekOrigin::FS_SEEK_CUR);
}

Status RoaringBitmap32::Deserialize(const char* begin, size_t length) {
    try {
        GetRoaringBitmap(roaring_bitmap_) = roaring::Roaring::readSafe(begin, length);
    } catch (...) {
        return Status::Invalid("catch exception in Deserialize() of RoaringBitmap32");
    }
    return Status::OK();
}

std::string RoaringBitmap32::ToString() const {
    return GetRoaringBitmap(roaring_bitmap_).toString();
}

RoaringBitmap32 RoaringBitmap32::And(const RoaringBitmap32& lhs, const RoaringBitmap32& rhs) {
    RoaringBitmap32 res;
    GetRoaringBitmap(res.roaring_bitmap_) =
        (GetRoaringBitmap(lhs.roaring_bitmap_) & GetRoaringBitmap(rhs.roaring_bitmap_));
    return res;
}

RoaringBitmap32 RoaringBitmap32::Or(const RoaringBitmap32& lhs, const RoaringBitmap32& rhs) {
    RoaringBitmap32 res;
    GetRoaringBitmap(res.roaring_bitmap_) =
        (GetRoaringBitmap(lhs.roaring_bitmap_) | GetRoaringBitmap(rhs.roaring_bitmap_));
    return res;
}

RoaringBitmap32 RoaringBitmap32::AndNot(const RoaringBitmap32& lhs, const RoaringBitmap32& rhs) {
    RoaringBitmap32 res;
    GetRoaringBitmap(res.roaring_bitmap_) =
        (GetRoaringBitmap(lhs.roaring_bitmap_) - GetRoaringBitmap(rhs.roaring_bitmap_));
    return res;
}

RoaringBitmap32 RoaringBitmap32::From(const std::vector<int32_t>& values) {
    RoaringBitmap32 res;
    for (const auto& value : values) {
        res.Add(value);
    }
    return res;
}

RoaringBitmap32 RoaringBitmap32::FastUnion(const std::vector<const RoaringBitmap32*>& inputs) {
    std::vector<roaring::Roaring*> roaring_inputs;
    roaring_inputs.reserve(inputs.size());
    for (const auto* roaring : inputs) {
        roaring_inputs.push_back(&GetRoaringBitmap(roaring->roaring_bitmap_));
    }
    RoaringBitmap32 res;
    GetRoaringBitmap(res.roaring_bitmap_) = roaring::Roaring::fastunion(
        roaring_inputs.size(), const_cast<const roaring::Roaring**>(roaring_inputs.data()));
    return res;
}

RoaringBitmap32 RoaringBitmap32::FastUnion(const std::vector<RoaringBitmap32>& inputs) {
    std::vector<const RoaringBitmap32*> inputs_ptr;
    inputs_ptr.reserve(inputs.size());
    for (const auto& bitmap : inputs) {
        inputs_ptr.push_back(&bitmap);
    }
    return FastUnion(inputs_ptr);
}
RoaringBitmap32::Iterator RoaringBitmap32::Begin() const {
    return RoaringBitmap32::Iterator(*this);
}

RoaringBitmap32::Iterator RoaringBitmap32::End() const {
    RoaringBitmap32::Iterator iter(*this);
    GetIterator(iter.iterator_) = GetRoaringBitmap(roaring_bitmap_).end();
    return iter;
}

RoaringBitmap32::Iterator RoaringBitmap32::EqualOrLarger(int32_t key) const {
    RoaringBitmap32::Iterator iter(*this);
    GetIterator(iter.iterator_).equalorlarger(key);
    return iter;
}

std::optional<int32_t> RoaringBitmap32::NextValue(int32_t x) const {
    auto iter = EqualOrLarger(x);
    if (iter == End()) {
        return std::nullopt;
    }
    return *iter;
}

std::optional<int32_t> RoaringBitmap32::PreviousValue(int32_t x) const {
    if (IsEmpty()) {
        return std::nullopt;
    }

    auto& bitmap = GetRoaringBitmap(roaring_bitmap_);
    if (x <= static_cast<int32_t>(bitmap.minimum())) {
        return std::nullopt;
    }

    const uint64_t rank = bitmap.rank(static_cast<uint32_t>(x - 1));
    if (rank == 0) {
        return std::nullopt;
    }

    uint32_t value = 0;
    [[maybe_unused]] bool found = bitmap.select(static_cast<uint32_t>(rank - 1), &value);
    assert(found);
    return static_cast<int32_t>(value);
}

}  // namespace paimon
