// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// 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 _FASTCDR_CDRBUFFER_H_
#define _FASTCDR_CDRBUFFER_H_

#include "fastcdr_dll.h"
#include <stdint.h>
#include <cstdio>
#include <string.h>
#include <cstddef>
#include <utility>

inline uint32_t size_to_uint32(
        size_t val)
{
  #if defined(_WIN32) || !defined(FASTCDR_ARM32)
    // On 64 bit platforms and all Windows architectures (because of C4267), explicitly cast.
    return static_cast<uint32_t>(val);
  #else
    // Skip useless cast on 32-bit builds.
    return val;
  #endif // if defined(_WIN32) || !defined(FASTCDR_ARM32)
}

namespace eprosima {
namespace fastcdr {
/*!
 * @brief This class implements the iterator used to go through a FastBuffer.
 */
class Cdr_DllAPI _FastBuffer_iterator
{
public:

    /*!
     * @brief Default constructor.
     * The iterator points any position.
     */
    _FastBuffer_iterator()
        : m_buffer(NULL)
        , m_currentPosition(NULL)
    {
    }

    /*!
     * @brief Constructor.
     * The iterator points to the indicated position.
     * @param buffer Pointer to the raw buffer.
     * @param index Position of the raw buffer where the iterator will point.
     */
    explicit _FastBuffer_iterator(
            char* buffer,
            size_t index)
        : m_buffer(buffer)
        , m_currentPosition(&m_buffer[index])
    {
    }

    /*!
     * @brief This operator changes the iterator's raw buffer.
     * This operator makes the iterator point to the same position but in another raw buffer.
     * The new raw buffer is the same than the source iterator's.
     * @param iterator The source iterator. The iterator will use the source iterator's raw buffer after this operation.
     */
    inline
    void operator <<(
            const _FastBuffer_iterator& iterator)
    {
        ptrdiff_t diff = m_currentPosition - m_buffer;
        m_buffer = iterator.m_buffer;
        m_currentPosition = m_buffer + diff;
    }

    /*!
     * @brief This operator changes the position where the iterator points.
     * This operator takes the index of the source iterator, but the iterator continues using its raw buffer.
     * @param iterator The source iterator. The iterator will use the source's iterator index to point to its own raw buffer.
     */
    inline
    void operator >>(
            const _FastBuffer_iterator& iterator)
    {
        ptrdiff_t diff = iterator.m_currentPosition - iterator.m_buffer;
        m_currentPosition = m_buffer + diff;
    }

    /*!
     * @brief This operator copies a data in the raw buffer.
     * The copy uses the size of the data type.
     * @param data Data to be copied. Cannot be NULL.
     */
    template<typename _T>
    inline
    void operator <<(
            const _T& data)
    {
        memcpy(m_currentPosition, &data, sizeof(_T));
    }

    /*!
     * @brief This operator copies data from the raw buffer to a variable.
     * The copy uses the size of the data type.
     * @param data Data to be filled.
     */
    template<typename _T>
    inline
    void operator >>(
            _T& data)
    {
        memcpy(&data, m_currentPosition, sizeof(_T));
    }

    /*!
     * @brief This function copies a buffer into the raw buffer.
     * @param src The source buffer.
     * @param size The number of bytes to be copied.
     */
    inline
    void memcopy(
            const void* src,
            const size_t size)
    {
        if (size > 0)
        {
            memcpy(m_currentPosition, src, size);
        }
    }

    /*!
     * @brief This function copies from the raw buffer to a external buffer.
     * @param dst The destination buffer.
     * @param size The size of bytes to be copied.
     */
    inline
    void rmemcopy(
            void* dst,
            const size_t size)
    {
        if (size > 0)
        {
            memcpy(dst, m_currentPosition, size);
        }
    }

    /*!
     * @brief This function increments the position where the iterator points.
     * @param numBytes Number of bytes the iterator moves the position.
     */
    inline
    void operator +=(
            size_t numBytes)
    {
        m_currentPosition += numBytes;
    }

    /*!
     * @brief This operator returns the subtraction of the current interator's position and the source iterator's position.
     * @param it Source iterator whose position is subtracted to the current iterator's position.
     * @return The result of subtract the current iterator's position and the source iterator's position.
     */
    inline
    size_t operator -(
            const _FastBuffer_iterator& it) const
    {
        return static_cast<size_t>(m_currentPosition - it.m_currentPosition);
    }

    /*!
     * @brief This function increments the iterator in one the position.
     * @return The current iterator.
     */
    inline
    _FastBuffer_iterator operator ++()
    {
        ++m_currentPosition;
        return *this;
    }

    /*!
     * @brief This function increments the iterator in one the position.
     * @return The current iterator.
     */
    inline
    _FastBuffer_iterator operator ++(
            int)
    {
        _FastBuffer_iterator tmp = *this;
        ++*this;
        return tmp;
    }

    /*!
     * @brief This function returns the current position in the raw buffer.
     * @return The current position in the raw buffer.
     */
    inline
    char* operator &()
    {
        return m_currentPosition;
    }

private:

    //! Pointer to the raw buffer.
    char* m_buffer;

    //! Current position in the raw buffer.
    char* m_currentPosition;
};

/*!
 * @brief This class represents a stream of bytes that contains (or will contain)
 * serialized data. This class is used by the serializers to serialize
 * or deserialize using their representation.
 * @ingroup FASTCDRAPIREFERENCE
 */
class Cdr_DllAPI FastBuffer
{
public:

    typedef _FastBuffer_iterator iterator;

    /*!
     * @brief This constructor creates an internal stream and assigns it to the eprosima::fastcdr::FastBuffers object.
     * The user can obtain this internal stream using the function eprosima::fastcdr::FastBuffers::getBuffer(). Be careful because this internal stream
     * is deleted in the destruction of the eprosima::fastcdr::FastBuffers object.
     */
    FastBuffer();

    /*!
     * @brief This constructor assigns the user's stream of bytes to the eprosima::fastcdr::FastBuffers object.
     * The user's stream will be used to serialize.
     *
     * @param buffer The user's buffer that will be used. This buffer is not deallocated in the object's destruction. Cannot be NULL.
     * @param bufferSize The length of user's buffer.
     */
    FastBuffer(
            char* const buffer,
            const size_t bufferSize);

    //! Move constructor
    FastBuffer(
            FastBuffer&& fbuffer)
        : m_buffer(nullptr)
        , m_bufferSize(0)
        , m_internalBuffer(true)
    {
        std::swap(m_buffer, fbuffer.m_buffer);
        std::swap(m_bufferSize, fbuffer.m_bufferSize);
        std::swap(m_internalBuffer, fbuffer.m_internalBuffer);
    }

    //! Move assignment
    FastBuffer& operator =(
            FastBuffer&& fbuffer)
    {
        std::swap(m_buffer, fbuffer.m_buffer);
        std::swap(m_bufferSize, fbuffer.m_bufferSize);
        std::swap(m_internalBuffer, fbuffer.m_internalBuffer);
        return *this;
    }

    /*!
     * @brief Default destructor.
     */
    virtual ~FastBuffer();

    /*!
     * @brief This function returns the stream that the eprosima::fastcdr::FastBuffers uses to serialize data.
     * @return The stream used by eprosima::fastcdr::FastBuffers to serialize data.
     */
    inline char* getBuffer() const
    {
        return m_buffer;
    }

    /*!
     * @brief This function returns the size of the allocated memory of the stream that the eprosima::fastcdr::FastBuffers uses to serialize data.
     * @return The size of the allocated memory of the stream used by the eprosima::fastcdr::FastBuffers to serialize data.
     */
    inline size_t getBufferSize() const
    {
        return m_bufferSize;
    }

    /*!
     * @brief This function returns a iterator that points to the begining of the stream.
     * @return The new iterator.
     */
    inline
    iterator begin()
    {
        return (iterator(m_buffer, 0));
    }

    /*!
     * @brief This function returns a iterator that points to the end of the stream.
     * @return The new iterator.
     */
    inline
    iterator end()
    {
        return (iterator(m_buffer, m_bufferSize));
    }

    /*!
     * @brief This function reserves memory for the internal raw buffer. It will only do so if the buffer is not yet allocated and is not externally set.
     * @param size The size of the memory to be allocated.
     * @return True if the allocation suceeded. False if the raw buffer was set externally or is already allocated.
     */
    bool reserve(
            size_t size);

    /*!
     * @brief This function resizes the raw buffer. It will call the user's defined function for this purpose.
     * @param minSizeInc The minimun growth expected of the current raw buffer.
     * @return True if the operation works. False if it does not.
     */
    bool resize(
            size_t minSizeInc);

private:

    FastBuffer(
            const FastBuffer&) = delete;

    FastBuffer& operator =(
            const FastBuffer&) = delete;

    //! @brief Pointer to the stream of bytes that contains the serialized data.
    char* m_buffer;

    //! @brief The total size of the user's buffer.
    size_t m_bufferSize;

    //! @brief This variable indicates if the managed buffer is internal or is from the user.
    bool m_internalBuffer;
};
}     //namespace fastcdr
} //namespace eprosima

#endif // _FASTCDR_FASTCDRBUFFER_H_
