///////////////////////////////////////////////////////////////////////////////
// Name:        wx/buffer.h
// Purpose:     auto buffer classes: buffers which automatically free memory
// Author:      Vadim Zeitlin
// Created:     12.04.99
// Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////

#ifndef _WX_BUFFER_H
#define _WX_BUFFER_H

#include "wx/defs.h"
#include "wx/wxcrtbase.h"

#if wxUSE_STD_IOSTREAM
    #include "wx/iosfwrap.h"
#endif

#include <stdlib.h>             // malloc() and free()

class WXDLLIMPEXP_FWD_BASE wxCStrData;

// ----------------------------------------------------------------------------
// Special classes for (wide) character strings: they use malloc/free instead
// of new/delete
// ----------------------------------------------------------------------------

// helpers used by wxCharTypeBuffer
namespace wxPrivate
{

struct UntypedBufferData
{
    enum Kind
    {
        Owned,
        NonOwned
    };

    UntypedBufferData(void *str, size_t len, Kind kind = Owned)
        : m_str(str), m_length(len), m_ref(1), m_owned(kind == Owned) {}

    ~UntypedBufferData()
    {
        if ( m_owned )
            free(m_str);
    }

    void *m_str;
    size_t m_length;

    // "short" to have sizeof(Data)=12 on 32bit archs
    unsigned short m_ref;

    bool m_owned;
};

// NB: this is defined in string.cpp and not the (non-existent) buffer.cpp
WXDLLIMPEXP_BASE UntypedBufferData * GetUntypedNullData();

// Implementation of stream insertion operators: they can't be inline because
// we don't have full std::ostream declaration here.
#if wxUSE_STD_IOSTREAM
WXDLLIMPEXP_BASE std::ostream& OutputCharBuffer(std::ostream&, const char* s);
WXDLLIMPEXP_BASE std::ostream& OutputWCharBuffer(std::ostream&, const wchar_t* ws);
#if defined(HAVE_WOSTREAM)
WXDLLIMPEXP_BASE std::wostream& OutputWCharBuffer(std::wostream&, const wchar_t* ws);
#endif // defined(HAVE_WOSTREAM)
#endif // wxUSE_STD_IOSTREAM

} // namespace wxPrivate


// Template used as CRTP base class for wxScopedCharTypeBuffer in order to
// define overloaded operator<<() for it.
//
// By default we don't define any operators, but we do define them for the
// usual char and wchar_t specializations below.
template <typename T, typename Buffer>
struct wxScopedCharTypeBufferStreamSupport
{
};

// Suppress the warning about declaring a non-template friend because this is
// exactly what we want to do here.
wxGCC_ONLY_WARNING_SUPPRESS(non-template-friend)

template <typename Buffer>
struct wxScopedCharTypeBufferStreamSupport<char, Buffer>
{
#if wxUSE_STD_IOSTREAM
  friend std::ostream& operator<<(std::ostream& oss, const Buffer& buf)
  {
      return wxPrivate::OutputCharBuffer(oss, buf.data());
  }
#endif // wxUSE_STD_IOSTREAM
};

template <typename Buffer>
struct wxScopedCharTypeBufferStreamSupport<wchar_t, Buffer>
{
#if wxUSE_STD_IOSTREAM
  friend std::ostream& operator<<(std::ostream& oss, const Buffer& buf)
  {
      return wxPrivate::OutputWCharBuffer(oss, buf.data());
  }
#if defined(HAVE_WOSTREAM)
  friend std::wostream& operator<<(std::wostream& woss, const Buffer& buf)
  {
      return wxPrivate::OutputWCharBuffer(woss, buf.data());
  }
#endif // defined(HAVE_WOSTREAM)
#endif // wxUSE_STD_IOSTREAM
};

wxGCC_ONLY_WARNING_RESTORE(non-template-friend)


// Reference-counted character buffer for storing string data. The buffer
// is only valid for as long as the "parent" object that provided the data
// is valid; see wxCharTypeBuffer<T> for persistent variant.
template <typename T>
class wxScopedCharTypeBuffer
    : wxScopedCharTypeBufferStreamSupport<T, wxScopedCharTypeBuffer<T>>
{
public:
    typedef T CharType;

    wxScopedCharTypeBuffer()
    {
        m_data = GetNullData();
    }

    // Creates "non-owned" buffer, i.e. 'str' is not owned by the buffer
    // and doesn't get freed by dtor. Used e.g. to point to wxString's internal
    // storage.
    static
    const wxScopedCharTypeBuffer CreateNonOwned(const CharType *str,
                                                size_t len = wxNO_LEN)
    {
        if ( len == wxNO_LEN )
            len = wxStrlen(str);

        wxScopedCharTypeBuffer buf;
        if ( str )
            buf.m_data = new Data(const_cast<CharType*>(str), len, Data::NonOwned);
        return buf;
    }

    // Creates "owned" buffer, i.e. takes over ownership of 'str' and frees it
    // in dtor (if ref.count reaches 0).
    static
    const wxScopedCharTypeBuffer CreateOwned(CharType *str,
                                             size_t len = wxNO_LEN )
    {
        if ( len == wxNO_LEN )
            len = wxStrlen(str);

        wxScopedCharTypeBuffer buf;
        if ( str )
            buf.m_data = new Data(str, len);
        return buf;
    }

    wxScopedCharTypeBuffer(const wxScopedCharTypeBuffer& src)
    {
        m_data = src.m_data;
        IncRef();
    }

    wxScopedCharTypeBuffer& operator=(const wxScopedCharTypeBuffer& src)
    {
        if ( &src == this )
            return *this;

        DecRef();
        m_data = src.m_data;
        IncRef();

        return *this;
    }

    ~wxScopedCharTypeBuffer()
    {
        DecRef();
    }

    // NB: this method is only const for backward compatibility. It used to
    //     be needed for auto_ptr-like semantics of the copy ctor, but now
    //     that ref-counting is used, it's not really needed.
    CharType *release() const
    {
        if ( m_data == GetNullData() )
            return nullptr;

        wxASSERT_MSG( m_data->m_owned, wxT("can't release non-owned buffer") );
        wxASSERT_MSG( m_data->m_ref == 1, wxT("can't release shared buffer") );

        CharType * const p = m_data->Get();

        wxScopedCharTypeBuffer *self = const_cast<wxScopedCharTypeBuffer*>(this);
        self->m_data->Set(nullptr, 0);
        self->DecRef();

        return p;
    }

    void reset()
    {
        DecRef();
    }

    // For some reasong clang-tidy gives a warning about using freed memory
    // here even when this is not at all the case, seemingly because it doesn't
    // follow reference counting logic, i.e. it assumes that it's possible to
    // delete the data even when it's still referenced.
    //
    // Suppress the warning as it's extremely annoying to get it for every use
    // of wxCharBuffer.
    //
    // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDelete)
    CharType *data() { return m_data->Get(); }
    // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDelete)
    const CharType *data() const { return  m_data->Get(); }
    operator const CharType *() const { return data(); }
    CharType operator[](size_t n) const { return data()[n]; }

    size_t length() const { return m_data->m_length; }

protected:
    // reference-counted data
    struct Data : public wxPrivate::UntypedBufferData
    {
        Data(CharType *str, size_t len, Kind kind = Owned)
            : wxPrivate::UntypedBufferData(str, len, kind)
        {
        }

        CharType *Get() const { return static_cast<CharType *>(m_str); }
        void Set(CharType *str, size_t len)
        {
            m_str = str;
            m_length = len;
        }
    };

    // placeholder for null string, to simplify this code
    static Data *GetNullData()
    {
        return static_cast<Data *>(wxPrivate::GetUntypedNullData());
    }

    void IncRef()
    {
        if ( m_data == GetNullData() ) // exception, not ref-counted
            return;
        m_data->m_ref++;
    }

    void DecRef()
    {
        if ( m_data == GetNullData() ) // exception, not ref-counted
            return;
        if ( --m_data->m_ref == 0 )
            delete m_data;
        m_data = GetNullData();
    }

    // sets this object to a be copy of 'other'; if 'src' is non-owned,
    // a deep copy is made and 'this' will contain new instance of the data
    void MakeOwnedCopyOf(const wxScopedCharTypeBuffer& src)
    {
        this->DecRef();

        if ( src.m_data == this->GetNullData() )
        {
            this->m_data = this->GetNullData();
        }
        else if ( src.m_data->m_owned )
        {
            this->m_data = src.m_data;
            this->IncRef();
        }
        else
        {
            // if the scoped buffer had non-owned data, we have to make
            // a copy here, because src.m_data->m_str is valid only for as long
            // as 'src' exists
            this->m_data = new Data
                               (
                                   StrCopy(src.data(), src.length()),
                                   src.length()
                               );
        }
    }

    static CharType *StrCopy(const CharType *src, size_t len)
    {
        CharType *dst = (CharType*)malloc(sizeof(CharType) * (len + 1));
        if ( dst )
            memcpy(dst, src, sizeof(CharType) * (len + 1));
        return dst;
    }

protected:
    Data *m_data;
};

typedef wxScopedCharTypeBuffer<char> wxScopedCharBuffer;
typedef wxScopedCharTypeBuffer<wchar_t> wxScopedWCharBuffer;


// this buffer class always stores data in "owned" (persistent) manner
template <typename T>
class wxCharTypeBuffer : public wxScopedCharTypeBuffer<T>
{
protected:
    typedef typename wxScopedCharTypeBuffer<T>::Data Data;

public:
    typedef T CharType;

    wxCharTypeBuffer(const CharType *str = nullptr, size_t len = wxNO_LEN)
    {
        if ( str )
        {
            if ( len == wxNO_LEN )
                len = wxStrlen(str);
            this->m_data = new Data(this->StrCopy(str, len), len);
        }
        else
        {
            this->m_data = this->GetNullData();
        }
    }

    wxCharTypeBuffer(size_t len)
    {
        CharType* const str = (CharType *)malloc((len + 1)*sizeof(CharType));
        if ( str )
        {
            str[len] = (CharType)0;

            // There is a potential memory leak here if new throws because it
            // fails to allocate Data, we ought to use new(nothrow) here, but
            // this might fail to compile under some platforms so until this
            // can be fully tested, just live with this (rather unlikely, as
            // Data is a small object) potential leak.
            this->m_data = new Data(str, len);
        }
        else
        {
            this->m_data = this->GetNullData();
        }
    }

    wxCharTypeBuffer(const wxCharTypeBuffer& src)
        : wxScopedCharTypeBuffer<T>(src) {}

    wxCharTypeBuffer& operator=(const CharType *str)
    {
        this->DecRef();

        if ( str )
            this->m_data = new Data(wxStrdup(str), wxStrlen(str));
        return *this;
    }

    wxCharTypeBuffer& operator=(const wxCharTypeBuffer& src)
    {
        wxScopedCharTypeBuffer<T>::operator=(src);
        return *this;
    }

    wxCharTypeBuffer(const wxScopedCharTypeBuffer<T>& src)
    {
        this->MakeOwnedCopyOf(src);
    }

    wxCharTypeBuffer& operator=(const wxScopedCharTypeBuffer<T>& src)
    {
        MakeOwnedCopyOf(src);
        return *this;
    }

    bool extend(size_t len)
    {
        wxASSERT_MSG( this->m_data->m_owned, "cannot extend non-owned buffer" );
        wxASSERT_MSG( this->m_data->m_ref == 1, "can't extend shared buffer" );

        CharType *str =
            (CharType *)realloc(this->data(), (len + 1) * sizeof(CharType));
        if ( !str )
            return false;

        // For consistency with the ctor taking just the length, NUL-terminate
        // the buffer.
        str[len] = (CharType)0;

        if ( this->m_data == this->GetNullData() )
        {
            this->m_data = new Data(str, len);
        }
        else
        {
            this->m_data->Set(str, len);
            this->m_data->m_owned = true;
        }

        return true;
    }

    void shrink(size_t len)
    {
        wxASSERT_MSG( this->m_data->m_owned, "cannot shrink non-owned buffer" );
        wxASSERT_MSG( this->m_data->m_ref == 1, "can't shrink shared buffer" );

        wxASSERT( len <= this->length() );

        this->m_data->m_length = len;
        this->data()[len] = 0;
    }
};

class wxCharBuffer : public wxCharTypeBuffer<char>
{
public:
    typedef wxCharTypeBuffer<char> wxCharTypeBufferBase;
    typedef wxScopedCharTypeBuffer<char> wxScopedCharTypeBufferBase;

    wxCharBuffer(const wxCharTypeBufferBase& buf)
        : wxCharTypeBufferBase(buf) {}
    wxCharBuffer(const wxScopedCharTypeBufferBase& buf)
        : wxCharTypeBufferBase(buf) {}

    wxCharBuffer(const CharType *str = nullptr) : wxCharTypeBufferBase(str) {}
    wxCharBuffer(size_t len) : wxCharTypeBufferBase(len) {}

    wxCharBuffer(const wxCStrData& cstr);

#if wxUSE_STD_IOSTREAM
    // Define this to disambiguate between converting to the base class or to
    // wxString when using operator<<() with the objects of this class.
    friend WXDLLIMPEXP_BASE
    std::ostream& operator<<(std::ostream& oss, const wxCharBuffer& buf);
#endif // wxUSE_STD_IOSTREAM
};

class wxWCharBuffer : public wxCharTypeBuffer<wchar_t>
{
public:
    typedef wxCharTypeBuffer<wchar_t> wxCharTypeBufferBase;
    typedef wxScopedCharTypeBuffer<wchar_t> wxScopedCharTypeBufferBase;

    wxWCharBuffer(const wxCharTypeBufferBase& buf)
        : wxCharTypeBufferBase(buf) {}
    wxWCharBuffer(const wxScopedCharTypeBufferBase& buf)
        : wxCharTypeBufferBase(buf) {}

    wxWCharBuffer(const CharType *str = nullptr) : wxCharTypeBufferBase(str) {}
    wxWCharBuffer(size_t len) : wxCharTypeBufferBase(len) {}

    wxWCharBuffer(const wxCStrData& cstr);

#if wxUSE_STD_IOSTREAM
    // See wxCharBuffer for why this is needed.
    friend WXDLLIMPEXP_BASE
    std::ostream& operator<<(std::ostream& oss, const wxWCharBuffer& buf);
#if defined(HAVE_WOSTREAM)
    friend WXDLLIMPEXP_BASE
    std::wostream& operator<<(std::wostream& woss, const wxWCharBuffer& buf);
#endif // defined(HAVE_WOSTREAM)
#endif // wxUSE_STD_IOSTREAM
};

// wxCharTypeBuffer<T> implicitly convertible to T*, for char and wchar_t,
// implemented slightly differently because in one case we must be
// constructible from a buffer and in another -- from a raw pointer.

class wxWritableCharBuffer : public wxScopedCharTypeBuffer<char>
{
public:
    explicit wxWritableCharBuffer(const wxScopedCharTypeBuffer<char>& src)
        : wxScopedCharTypeBuffer<char>(src) {}

    operator char*() { return data(); }
};

class wxWritableWCharBuffer : public wxCharTypeBuffer<wchar_t>
{
public:
    explicit wxWritableWCharBuffer(const CharType *str)
        : wxCharTypeBuffer<wchar_t>(str) {}

    operator wchar_t*() { return data(); }
};


// Compatibility defines, don't use them in the new code.
#define wxWxCharBuffer wxWCharBuffer

#define wxMB2WXbuf wxWCharBuffer
#define wxWX2MBbuf wxCharBuffer
#if wxUSE_UNICODE_WCHAR
    #define wxWC2WXbuf wxChar*
    #define wxWX2WCbuf wxChar*
#else /* wxUSE_UNICODE_UTF8 */
    #define wxWC2WXbuf wxWCharBuffer
    #define wxWX2WCbuf wxWCharBuffer
#endif

// ----------------------------------------------------------------------------
// A class for holding growable data buffers (not necessarily strings)
// ----------------------------------------------------------------------------

// This class manages the actual data buffer pointer and is ref-counted.
class wxMemoryBufferData
{
public:
    // the initial size and also the size added by ResizeIfNeeded()
    enum { DefBufSize = 1024 };

    friend class wxMemoryBuffer;

    // everything is private as it can only be used by wxMemoryBuffer
private:
    wxMemoryBufferData(size_t size = wxMemoryBufferData::DefBufSize)
        : m_data(size ? malloc(size) : nullptr), m_size(size), m_len(0), m_ref(0)
    {
    }
    ~wxMemoryBufferData() { free(m_data); }


    void ResizeIfNeeded(size_t newSize)
    {
        if (newSize > m_size)
        {
            void* const data = realloc(m_data, newSize + wxMemoryBufferData::DefBufSize);
            if ( !data )
            {
                // It's better to crash immediately dereferencing a null
                // pointer in the function calling us than overflowing the
                // buffer which couldn't be made big enough.
                free(release());
                return;
            }

            m_data = data;
            m_size = newSize + wxMemoryBufferData::DefBufSize;
        }
    }

    void IncRef() { m_ref += 1; }
    void DecRef()
    {
        m_ref -= 1;
        if (m_ref == 0)  // are there no more references?
            delete this;
    }

    void *release()
    {
        if ( m_data == nullptr )
            return nullptr;

        wxASSERT_MSG( m_ref == 1, "can't release shared buffer" );

        void *p = m_data;
        m_data = nullptr;
        m_len =
        m_size = 0;

        return p;
    }


    // the buffer containing the data
    void  *m_data;

    // the size of the buffer
    size_t m_size;

    // the amount of data currently in the buffer
    size_t m_len;

    // the reference count
    size_t m_ref;

    wxDECLARE_NO_COPY_CLASS(wxMemoryBufferData);
};


class wxMemoryBuffer
{
public:
    // ctor and dtor
    wxMemoryBuffer(size_t size = wxMemoryBufferData::DefBufSize)
    {
        m_bufdata = new wxMemoryBufferData(size);
        m_bufdata->IncRef();
    }

    ~wxMemoryBuffer() { m_bufdata->DecRef(); }


    // copy and assignment
    wxMemoryBuffer(const wxMemoryBuffer& src)
        : m_bufdata(src.m_bufdata)
    {
        m_bufdata->IncRef();
    }

    wxMemoryBuffer& operator=(const wxMemoryBuffer& src)
    {
        if (&src != this)
        {
            m_bufdata->DecRef();
            m_bufdata = src.m_bufdata;
            m_bufdata->IncRef();
        }
        return *this;
    }


    // Accessors
    void  *GetData() const    { return m_bufdata->m_data; }
    size_t GetBufSize() const { return m_bufdata->m_size; }
    size_t GetDataLen() const { return m_bufdata->m_len; }

    bool IsEmpty() const { return GetDataLen() == 0; }

    void   SetBufSize(size_t size) { m_bufdata->ResizeIfNeeded(size); }
    void   SetDataLen(size_t len)
    {
        wxASSERT(len <= m_bufdata->m_size);
        m_bufdata->m_len = len;
    }

    void Clear() { SetDataLen(0); }

    // Ensure the buffer is big enough and return a pointer to it
    void *GetWriteBuf(size_t sizeNeeded)
    {
        m_bufdata->ResizeIfNeeded(sizeNeeded);
        return m_bufdata->m_data;
    }

    // Update the length after the write
    void  UngetWriteBuf(size_t sizeUsed) { SetDataLen(sizeUsed); }

    // Like the above, but appends to the buffer
    void *GetAppendBuf(size_t sizeNeeded)
    {
        m_bufdata->ResizeIfNeeded(m_bufdata->m_len + sizeNeeded);
        return (char*)m_bufdata->m_data + m_bufdata->m_len;
    }

    // Update the length after the append
    void  UngetAppendBuf(size_t sizeUsed)
    {
        SetDataLen(m_bufdata->m_len + sizeUsed);
    }

    // Other ways to append to the buffer
    void  AppendByte(char data)
    {
        wxCHECK_RET( m_bufdata->m_data, wxT("invalid wxMemoryBuffer") );

        m_bufdata->ResizeIfNeeded(m_bufdata->m_len + 1);
        *(((char*)m_bufdata->m_data) + m_bufdata->m_len) = data;
        m_bufdata->m_len += 1;
    }

    void  AppendData(const void *data, size_t len)
    {
        memcpy(GetAppendBuf(len), data, len);
        UngetAppendBuf(len);
    }

    operator const char *() const { return (const char*)GetData(); }

    // gives up ownership of data, returns the pointer; after this call,
    // data isn't freed by the buffer and its content is resent to empty
    void *release()
    {
        return m_bufdata->release();
    }

private:
    wxMemoryBufferData*  m_bufdata;
};

// ----------------------------------------------------------------------------
// template class for any kind of data
// ----------------------------------------------------------------------------

// TODO

#endif // _WX_BUFFER_H
