/*
 * Copyright (c) 2025 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.
 */

#ifndef DISTRIBUTEDDATAMGR_PASTEBOARD_TLV_WRITEABLE_H
#define DISTRIBUTEDDATAMGR_PASTEBOARD_TLV_WRITEABLE_H

#include "endian_converter.h"
#include "tlv_countable.h"

namespace OHOS::MiscServices {

bool IsRemoteEncode();

class WriteOnlyBuffer;

class TLVWriteable : public TLVCountable {
public:
    virtual ~TLVWriteable() = default;

    virtual bool EncodeTLV(WriteOnlyBuffer &buffer) const = 0;

    API_EXPORT bool Encode(std::vector<uint8_t> &buffer, bool isRemote = false) const;

    API_EXPORT bool Encode(size_t len, std::vector<uint8_t> &buffer, bool isRemote = false) const;

    API_EXPORT size_t Count(bool isRemote = false) const;
};

class WriteOnlyBuffer : public TLVBuffer {
public:
    explicit WriteOnlyBuffer(size_t len) : TLVBuffer(len), data_(len)
    {
    }

    template<typename T>
    bool Write(uint16_t type, const std::vector<T> &value)
    {
        if (!HasExpectBuffer(sizeof(TLVHead))) {
            return false;
        }
        auto tagCursor = cursor_;
        cursor_ += sizeof(TLVHead); // placeholder
        auto valueCursor = cursor_;
        bool ret = WriteValue(value);
        WriteHead(type, tagCursor, cursor_ - valueCursor);
        return ret;
    }

    template<typename T>
    bool Write(uint16_t type, const std::shared_ptr<T> &value)
    {
        if (value == nullptr) {
            return true;
        }
        return Write(type, *value);
    }

    bool Write(uint16_t type, std::monostate value);
    bool Write(uint16_t type, const void *value);
    bool Write(uint16_t type, bool value);
    bool Write(uint16_t type, double value);
    bool Write(uint16_t type, int8_t value);
    bool Write(uint16_t type, int16_t value);
    bool Write(uint16_t type, int32_t value);
    bool Write(uint16_t type, int64_t value);
    bool Write(uint16_t type, uint32_t value);
    bool Write(uint16_t type, const std::string &value);
    bool Write(uint16_t type, const Object &value);
    bool Write(uint16_t type, const AAFwk::Want &value);
    bool Write(uint16_t type, const Media::PixelMap &value);
    bool Write(uint16_t type, const RawMem &value);
    bool Write(uint16_t type, const TLVWriteable &value);
    bool Write(uint16_t type, const std::vector<uint8_t> &value);
    bool Write(uint16_t type, const std::map<std::string, std::vector<uint8_t>> &value);
    bool Write(uint16_t type, const Details &value);

    template<typename _InTp>
    bool WriteVariant(uint16_t type, uint32_t step, const _InTp &input);

    template<typename _InTp, typename _First, typename... _Rest>
    bool WriteVariant(uint16_t type, uint32_t step, const _InTp &input);

    template<typename... _Types>
    bool Write(uint16_t type, const std::variant<_Types...> &input);

    template<>
    bool Write(uint16_t type, const EntryValue &input);

private:
    void WriteHead(uint16_t type, size_t tagCursor, uint32_t len)
    {
        if (tagCursor + sizeof(TLVHead) > data_.size()) {
            return;
        }
        auto *tlvHead = reinterpret_cast<TLVHead *>(data_.data() + tagCursor);
        tlvHead->tag = HostToNet(type);
        tlvHead->len = HostToNet(len);
    }

    template<typename T>
    bool WriteBasic(uint16_t type, T value)
    {
        if (!HasExpectBuffer(sizeof(TLVHead) + sizeof(value))) {
            return false;
        }
        auto *tlvHead = reinterpret_cast<TLVHead *>(data_.data() + cursor_);
        tlvHead->tag = HostToNet(type);
        tlvHead->len = HostToNet(static_cast<uint32_t>(sizeof(value)));
        auto valueBuff = HostToNet(value);
        auto ret = memcpy_s(tlvHead->value, total_ - cursor_ - sizeof(TLVHead), &valueBuff, sizeof(value));
        if (ret != EOK) {
            return false;
        }
        cursor_ += sizeof(TLVHead) + sizeof(value);
        return true;
    }

    template<typename T>
    bool WriteValue(const std::vector<T> &value)
    {
        // items iterator
        bool ret = true;
        for (const T &item : value) {
            // V:item value
            ret = ret && Write(TAG_VECTOR_ITEM, item);
        }
        return ret;
    }

    friend class TLVWriteable;
    std::vector<uint8_t> data_;
};
} // namespace OHOS::MiscServices
#endif // DISTRIBUTEDDATAMGR_PASTEBOARD_TLV_WRITEABLE_H