/*
 * 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/io/byte_array_input_stream.h"

#include <cassert>
#include <cstring>
#include <utility>

#include "fmt/format.h"

namespace paimon {
ByteArrayInputStream::ByteArrayInputStream(const char* buffer, int64_t length)
    : buffer_(buffer), length_(length), position_(0) {
    assert(buffer_);
    assert(length >= 0);
}

const char* ByteArrayInputStream::GetRawData() const {
    return buffer_ + position_;
}

Status ByteArrayInputStream::Seek(int64_t offset, SeekOrigin origin) {
    int64_t new_position = 0;
    switch (origin) {
        case SeekOrigin::FS_SEEK_SET: {
            new_position = offset;
            break;
        }
        case SeekOrigin::FS_SEEK_CUR: {
            new_position = position_ + offset;
            break;
        }
        case SeekOrigin::FS_SEEK_END: {
            new_position = length_ + offset;
            break;
        }
        default:
            return Status::Invalid(
                "invalid SeekOrigin, only support FS_SEEK_SET, FS_SEEK_CUR, and FS_SEEK_END");
    }
    if (new_position < 0 || new_position > length_) {
        return Status::Invalid(fmt::format("invalid seek, after seek, current pos {}, length {}",
                                           new_position, length_));
    }
    position_ = new_position;
    return Status::OK();
}

Result<int64_t> ByteArrayInputStream::Read(char* buffer, int64_t size) {
    if (size < 0 || size > length_ - position_) {
        return Status::Invalid(
            fmt::format("ByteArrayInputStream assert boundary failed: need length {}, current "
                        "position {}, exceed length {}",
                        size, position_, length_));
    }
    memcpy(buffer, buffer_ + position_, static_cast<size_t>(size));
    position_ += size;
    return size;
}

Result<int64_t> ByteArrayInputStream::Read(char* buffer, int64_t size, int64_t offset) {
    if (size < 0 || offset < 0 || offset > length_ || size > length_ - offset) {
        return Status::Invalid(
            fmt::format("ByteArrayInputStream boundary check failed: read size {}, offset {}, "
                        "stream length {}",
                        size, offset, length_));
    }
    memcpy(buffer, buffer_ + offset, static_cast<size_t>(size));
    return size;
}

void ByteArrayInputStream::ReadAsync(char* buffer, int64_t size, int64_t offset,
                                     std::function<void(Status)>&& callback) {
    Result<int64_t> read_size = Read(buffer, size, offset);
    Status status = Status::OK();
    if (read_size.ok() && read_size.value() != size) {
        status = Status::Invalid(fmt::format(
            "ByteArrayInputStream async read size {} != expected {}", read_size.value(), size));
    } else if (!read_size.ok()) {
        status = read_size.status();
    }
    callback(status);
}

Status ByteArrayInputStream::Close() {
    return Status::OK();
}

Result<std::string> ByteArrayInputStream::GetUri() const {
    return std::string();
}
}  // namespace paimon
