/*
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed 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 <vector>
#include <string>
#include "hdf_sbuf_ipc.h"
#include "hdf_sbuf_impl.h"
#include "hdf_log.h"
#include "hdf_remote_adapter.h"

using namespace OHOS;

static void MParcelImplInterfaceAssign(struct HdfSBufImpl *inf);

struct SBufMParcelImpl {
    SBufMParcelImpl(OHOS::MessageParcel *parcel, bool owned = true): realParcel_(parcel), owned_(owned)
    {
        MParcelImplInterfaceAssign(&infImpl);
    }
    ~SBufMParcelImpl()
    {
        if (owned_ && realParcel_ != nullptr) {
            delete realParcel_;
            realParcel_ = nullptr;
        }
    }
    struct HdfSBufImpl infImpl;
    OHOS::MessageParcel *realParcel_;
    std::vector<std::u16string> str16Pool_;
    bool owned_;
};

static MessageParcel *MParcelCast(struct HdfSBufImpl *impl)
{
    SBufMParcelImpl *sbufImpl = reinterpret_cast<SBufMParcelImpl *>(impl);
    return sbufImpl->realParcel_;
}

static bool SbufMParcelImplWriteBuffer(struct HdfSBufImpl *sbuf, const uint8_t *data, uint32_t writeSize)
{
    if (sbuf == nullptr) {
        return false;
    }
    auto parcel = MParcelCast(sbuf);
    if (!parcel->WriteUint32(writeSize)) {
        return false;
    }
    return parcel->WriteUnpadBuffer((const void *)data, writeSize);
}

static bool SbufMParcelImplWriteUnpadBuffer(struct HdfSBufImpl *sbuf, const uint8_t *data, uint32_t writeSize)
{
    if (sbuf == nullptr) {
        return false;
    }
    return MParcelCast(sbuf)->WriteUnpadBuffer(static_cast<const void *>(data), writeSize);
}

static bool SbufMParcelImplWriteUint64(struct HdfSBufImpl *sbuf, uint64_t value)
{
    if (sbuf == nullptr) {
        return false;
    }
    return MParcelCast(sbuf)->WriteUint64(value);
}

static bool SbufMParcelImplWriteUint32(struct HdfSBufImpl *sbuf, uint32_t value)
{
    if (sbuf == nullptr) {
        return false;
    }
    return MParcelCast(sbuf)->WriteUint32(value);
}

static bool SbufMParcelImplWriteUint16(struct HdfSBufImpl *sbuf, uint16_t value)
{
    if (sbuf == nullptr) {
        return false;
    }
    return MParcelCast(sbuf)->WriteUint16(value);
}

static bool SbufMParcelImplWriteUint8(struct HdfSBufImpl *sbuf, uint8_t value)
{
    if (sbuf == nullptr) {
        return false;
    }
    return MParcelCast(sbuf)->WriteUint8(value);
}

static bool SbufMParcelImplWriteInt64(struct HdfSBufImpl *sbuf, int64_t value)
{
    if (sbuf == nullptr) {
        return false;
    }
    return MParcelCast(sbuf)->WriteInt64(value);
}

static bool SbufMParcelImplWriteInt32(struct HdfSBufImpl *sbuf, int32_t value)
{
    if (sbuf == nullptr) {
        return false;
    }
    return MParcelCast(sbuf)->WriteInt32(value);
}

static bool SbufMParcelImplWriteInt16(struct HdfSBufImpl *sbuf, int16_t value)
{
    if (sbuf == nullptr) {
        return false;
    }
    return MParcelCast(sbuf)->WriteInt16(value);
}

static bool SbufMParcelImplWriteInt8(struct HdfSBufImpl *sbuf, int8_t value)
{
    if (sbuf == nullptr) {
        return false;
    }
    return MParcelCast(sbuf)->WriteInt8(value);
}

static bool SbufMParcelImplWriteString(struct HdfSBufImpl *sbuf, const char *value)
{
    if (sbuf == nullptr) {
        return false;
    }
    return MParcelCast(sbuf)->WriteCString(value);
}

static bool SbufMParcelImplWriteString16(struct HdfSBufImpl *sbuf, const char16_t *value, uint32_t size)
{
    if (sbuf == nullptr) {
        return false;
    }
    return MParcelCast(sbuf)->WriteString16WithLength(value, size);
}

static bool SbufMParcelImplWriteFileDescriptor(struct HdfSBufImpl *sbuf, int fd)
{
    if (sbuf == nullptr) {
        return false;
    }
    return MParcelCast(sbuf)->WriteFileDescriptor(fd);
}

static bool SbufMParcelImplWriteDouble(struct HdfSBufImpl *sbuf, double value)
{
    if (sbuf == nullptr) {
        return false;
    }
    return MParcelCast(sbuf)->WriteDouble(value);
}

static bool SbufMParcelImplWriteFloat(struct HdfSBufImpl *sbuf, float value)
{
    if (sbuf == nullptr) {
        return false;
    }
    return MParcelCast(sbuf)->WriteFloat(value);
}

static bool SbufMParcelImplReadFloat(struct HdfSBufImpl *sbuf, float *value)
{
    if (sbuf == nullptr || value == nullptr) {
        return false;
    }
    float v = 0;
    bool ret = MParcelCast(sbuf)->ReadFloat(v);
    *value = v;
    return ret;
}

static bool SbufMParcelImplReadDouble(struct HdfSBufImpl *sbuf, double *value)
{
    if (sbuf == nullptr || value == nullptr) {
        return false;
    }
    double v = 0;
    bool ret = MParcelCast(sbuf)->ReadDouble(v);
    *value = v;
    return ret;
}

static int SbufMParcelImplReadFileDescriptor(struct HdfSBufImpl *sbuf)
{
    if (sbuf == nullptr) {
        return false;
    }
    return MParcelCast(sbuf)->ReadFileDescriptor();
}

static bool SbufMParcelImplReadBuffer(struct HdfSBufImpl *sbuf, const uint8_t **data, uint32_t *readSize)
{
    if (sbuf == nullptr || data == nullptr || readSize == nullptr) {
        return false;
    }
    MessageParcel *parcel = MParcelCast(sbuf);
    *readSize = parcel->ReadUint32();
    *data = parcel->ReadUnpadBuffer(*readSize);
    return *data != nullptr;
}

static const uint8_t *SbufMParcelImplReadUnpadBuffer(struct HdfSBufImpl *sbuf, size_t length)
{
    if (sbuf == nullptr) {
        return nullptr;
    }
    return MParcelCast(sbuf)->ReadUnpadBuffer(length);
}

static bool SbufMParcelImplReadUint64(struct HdfSBufImpl *sbuf, uint64_t *value)
{
    if (sbuf == nullptr || value == nullptr) {
        return false;
    }
    uint64_t v = 0;
    bool ret = MParcelCast(sbuf)->ReadUint64(v);
    *value = v;
    return ret;
}

static bool SbufMParcelImplReadUint32(struct HdfSBufImpl *sbuf, uint32_t *value)
{
    if (sbuf == nullptr || value == nullptr) {
        return false;
    }
    uint32_t v = 0;
    bool ret = MParcelCast(sbuf)->ReadUint32(v);
    *value = v;
    return ret;
}

static bool SbufMParcelImplReadUint16(struct HdfSBufImpl *sbuf, uint16_t *value)
{
    if (sbuf == nullptr || value == nullptr) {
        return false;
    }
    uint16_t v = 0;
    bool ret = MParcelCast(sbuf)->ReadUint16(v);
    *value = v;
    return ret;
}

static bool SbufMParcelImplReadUint8(struct HdfSBufImpl *sbuf, uint8_t *value)
{
    if (sbuf == nullptr || value == nullptr) {
        return false;
    }
    uint8_t v = 0;
    bool ret = MParcelCast(sbuf)->ReadUint8(v);
    *value = v;
    return ret;
}

static bool SbufMParcelImplReadInt64(struct HdfSBufImpl *sbuf, int64_t *value)
{
    if (sbuf == nullptr || value == nullptr) {
        return false;
    }
    int64_t v = 0;
    bool ret = MParcelCast(sbuf)->ReadInt64(v);
    *value = v;
    return ret;
}

static bool SbufMParcelImplReadInt32(struct HdfSBufImpl *sbuf, int32_t *value)
{
    if (sbuf == nullptr || value == nullptr) {
        return false;
    }
    int32_t v = 0;
    bool ret = MParcelCast(sbuf)->ReadInt32(v);
    *value = v;
    return ret;
}

static bool SbufMParcelImplReadInt16(struct HdfSBufImpl *sbuf, int16_t *value)
{
    if (sbuf == nullptr || value == nullptr) {
        return false;
    }
    int16_t v = 0;
    bool ret = MParcelCast(sbuf)->ReadInt16(v);
    *value = v;
    return ret;
}

static bool SbufMParcelImplReadInt8(struct HdfSBufImpl *sbuf, int8_t *value)
{
    if (sbuf == nullptr || value == nullptr) {
        return false;
    }
    int8_t v = 0;
    bool ret = MParcelCast(sbuf)->ReadInt8(v);
    *value = v;
    return ret;
}

static const char *SbufMParcelImplReadString(struct HdfSBufImpl *sbuf)
{
    if (sbuf == nullptr) {
        return nullptr;
    }
    return MParcelCast(sbuf)->ReadCString();
}

static const char16_t *SbufMParcelImplReadString16(struct HdfSBufImpl *sbuf)
{
    if (sbuf == nullptr) {
        return nullptr;
    }
    SBufMParcelImpl *sbufImpl = reinterpret_cast<SBufMParcelImpl *>(sbuf);
    sbufImpl->str16Pool_.emplace_back(sbufImpl->realParcel_->ReadString16());
    return sbufImpl->str16Pool_.rbegin()->c_str();
}

static int32_t SbufMParcelImplWriteRemoteService(struct HdfSBufImpl *sbuf, const struct HdfRemoteService *service)
{
    if (sbuf == nullptr || service == nullptr) {
        return HDF_ERR_INVALID_PARAM;
    }
    MessageParcel *parcel = MParcelCast(sbuf);
    const struct HdfRemoteServiceHolder *holder = reinterpret_cast<const struct HdfRemoteServiceHolder *>(service);
    if (holder != nullptr) {
        return parcel->WriteRemoteObject(holder->remote_) ? HDF_SUCCESS : HDF_FAILURE;
    }
    return HDF_FAILURE;
}

static struct HdfRemoteService *SbufMParcelImplReadRemoteService(struct HdfSBufImpl *sbuf)
{
    if (sbuf == nullptr) {
        return nullptr;
    }
    auto remote = MParcelCast(sbuf)->ReadRemoteObject();
    if (remote == nullptr) {
        HDF_LOGE("%{public}s: read remote object fail", __func__);
        return nullptr;
    }
    return HdfRemoteAdapterBind(remote);
}

static const uint8_t *SbufMParcelImplGetData(const struct HdfSBufImpl *sbuf)
{
    if (sbuf == nullptr) {
        return nullptr;
    }
    return reinterpret_cast<const uint8_t *>(MParcelCast(const_cast<struct HdfSBufImpl *>(sbuf))->GetData());
}

static void SbufMParcelImplFlush(struct HdfSBufImpl *sbuf)
{
    if (sbuf == nullptr) {
        return;
    }
    return MParcelCast(sbuf)->FlushBuffer();
}

static size_t SbufMParcelImplGetCapacity(const struct HdfSBufImpl *sbuf)
{
    if (sbuf == nullptr) {
        return HDF_FAILURE;
    }

    return MParcelCast(const_cast<struct HdfSBufImpl *>(sbuf))->GetDataCapacity();
}

static size_t SbufMParcelImplGetDataSize(const struct HdfSBufImpl *sbuf)
{
    if (sbuf == nullptr) {
        return HDF_FAILURE;
    }
    return MParcelCast(const_cast<struct HdfSBufImpl *>(sbuf))->GetDataSize();
}

static void SbufMParcelImplSetDataSize(struct HdfSBufImpl *sbuf, size_t size)
{
    if (sbuf == nullptr) {
        return;
    }
    MParcelCast(sbuf)->SetDataSize(size);
}

static void SbufMParcelImplRecycle(struct HdfSBufImpl *sbuf)
{
    if (sbuf == nullptr) {
        return;
    }
    SBufMParcelImpl *sbufImpl = reinterpret_cast<SBufMParcelImpl *>(sbuf);
    delete sbufImpl;
}

static void MParcelImplInterfaceAssign(struct HdfSBufImpl *inf)
{
    inf->writeBuffer = SbufMParcelImplWriteBuffer;
    inf->writeUnpadBuffer = SbufMParcelImplWriteUnpadBuffer;
    inf->writeUint64 = SbufMParcelImplWriteUint64;
    inf->writeUint32 = SbufMParcelImplWriteUint32;
    inf->writeUint16 = SbufMParcelImplWriteUint16;
    inf->writeUint8 = SbufMParcelImplWriteUint8;
    inf->writeInt64 = SbufMParcelImplWriteInt64;
    inf->writeInt32 = SbufMParcelImplWriteInt32;
    inf->writeInt16 = SbufMParcelImplWriteInt16;
    inf->writeInt8 = SbufMParcelImplWriteInt8;
    inf->writeString = SbufMParcelImplWriteString;
    inf->writeString16 = SbufMParcelImplWriteString16;
    inf->writeFileDescriptor = SbufMParcelImplWriteFileDescriptor;
    inf->writeFloat = SbufMParcelImplWriteFloat;
    inf->writeDouble = SbufMParcelImplWriteDouble;
    inf->readDouble = SbufMParcelImplReadDouble;
    inf->readFloat = SbufMParcelImplReadFloat;
    inf->readFileDescriptor = SbufMParcelImplReadFileDescriptor;
    inf->readBuffer = SbufMParcelImplReadBuffer;
    inf->readUnpadBuffer = SbufMParcelImplReadUnpadBuffer;
    inf->readUint64 = SbufMParcelImplReadUint64;
    inf->readUint32 = SbufMParcelImplReadUint32;
    inf->readUint16 = SbufMParcelImplReadUint16;
    inf->readUint8 = SbufMParcelImplReadUint8;
    inf->readInt64 = SbufMParcelImplReadInt64;
    inf->readInt32 = SbufMParcelImplReadInt32;
    inf->readInt16 = SbufMParcelImplReadInt16;
    inf->readInt8 = SbufMParcelImplReadInt8;
    inf->readString = SbufMParcelImplReadString;
    inf->readString16 = SbufMParcelImplReadString16;
    inf->writeRemoteService = SbufMParcelImplWriteRemoteService;
    inf->readRemoteService = SbufMParcelImplReadRemoteService;
    inf->getData = SbufMParcelImplGetData;
    inf->flush = SbufMParcelImplFlush;
    inf->getCapacity = SbufMParcelImplGetCapacity;
    inf->getDataSize = SbufMParcelImplGetDataSize;
    inf->setDataSize = SbufMParcelImplSetDataSize;
    inf->recycle = SbufMParcelImplRecycle;
    inf->transDataOwnership = nullptr;
    inf->copy = nullptr;
    inf->move = nullptr;
}

extern "C" struct HdfSBufImpl *SbufObtainIpc(size_t capacity)
{
    (void)capacity;
    struct SBufMParcelImpl *sbuf = new SBufMParcelImpl(new MessageParcel());
    return &sbuf->infImpl;
}

class SbufAllocator : public Allocator {
public:
    void *Realloc(void *data, size_t newSize) override
    {
        (void)data;
        (void)newSize;
        return nullptr;
    }

    void *Alloc(size_t size) override
    {
        (void)size;
        return nullptr;
    }

    void Dealloc(void *data) override
    {
        (void)data;
    }
};

extern "C" struct HdfSBufImpl *SbufBindIpc(uintptr_t base, size_t size)
{
    struct SBufMParcelImpl *sbuf = new SBufMParcelImpl(new MessageParcel(new SbufAllocator()));
    sbuf->realParcel_->ParseFrom(base, size);
    return &sbuf->infImpl;
}

struct HdfSBuf *ParcelToSbuf(OHOS::MessageParcel *parcel)
{
    struct SBufMParcelImpl *sbuf = new SBufMParcelImpl(parcel, false);
    return HdfSbufTypedObtainInplace(SBUF_IPC, &sbuf->infImpl);
}

int32_t SbufToParcel(struct HdfSBuf *sbuf, OHOS::MessageParcel **parcel)
{
    if (sbuf == nullptr || parcel == nullptr) {
        return HDF_ERR_INVALID_PARAM;
    }
    struct SBufMParcelImpl *impl = reinterpret_cast<SBufMParcelImpl *>(HdfSbufGetImpl(sbuf));
    if (impl == nullptr) {
        return HDF_ERR_INVALID_OBJECT;
    }

    *parcel = impl->realParcel_;
    return HDF_SUCCESS;
}