//
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) Contributors to the OpenEXR Project.
//

#ifndef INCLUDED_IMF_ARRAY_H
#define INCLUDED_IMF_ARRAY_H

#include "ImfForward.h"

//-------------------------------------------------------------------------
//
// class Array
// class Array2D
//
// "Arrays of T" whose sizes are not known at compile time.
// When an array goes out of scope, its elements are automatically
// deleted.
//
// Usage example:
//
//	struct C
//	{
//	    C ()		{std::cout << "C::C  (" << this << ")\n";};
//	    virtual ~C ()	{std::cout << "C::~C (" << this << ")\n";};
//	};
//
//	int
//	main ()
//	{
//	    Array <C> a(3);
//
//	    C &b = a[1];
//	    const C &c = a[1];
//	    C *d = a + 2;
//	    const C *e = a;
//
//	    return 0;
//	}
//
//-------------------------------------------------------------------------

OPENEXR_IMF_INTERNAL_NAMESPACE_HEADER_ENTER

template <class T> class IMF_EXPORT_TEMPLATE_TYPE Array
{
public:
    //-----------------------------
    // Constructors and destructors
    //-----------------------------

    Array ()
    {
        _data = 0;
        _size = 0;
    }
    Array (long size)
    {
        _data = new T[size];
        _size = size;
    }
    ~Array () { delete[] _data; }

    //-----------------------------
    // Access to the array elements
    //-----------------------------

    operator T* () { return _data; }
    operator const T* () const { return _data; }

    //------------------------------------------------------
    // Resize and clear the array (the contents of the array
    // are not preserved across the resize operation).
    //
    // resizeEraseUnsafe() is more memory efficient than
    // resizeErase() because it deletes the old memory block
    // before allocating a new one, but if allocating the
    // new block throws an exception, resizeEraseUnsafe()
    // leaves the array in an unusable state.
    //
    //------------------------------------------------------

    void resizeErase (long size);
    void resizeEraseUnsafe (long size);

    //-------------------------------
    // Return the size of this array.
    //-------------------------------

    long size () const { return _size; }

private:
    Array (const Array&)            = delete;
    Array& operator= (const Array&) = delete;
    Array (Array&&)                 = delete;
    Array& operator= (Array&&)      = delete;

    long _size;
    T*   _data;
};

template <class T> class IMF_EXPORT_TEMPLATE_TYPE Array2D
{
public:
    //-----------------------------
    // Constructors and destructors
    //-----------------------------

    Array2D ();                       // empty array, 0 by 0 elements
    Array2D (long sizeX, long sizeY); // sizeX by sizeY elements
    ~Array2D ();

    //-----------------------------
    // Access to the array elements
    //-----------------------------

    T*       operator[] (long x);
    const T* operator[] (long x) const;

    //------------------------------------------------------
    // Resize and clear the array (the contents of the array
    // are not preserved across the resize operation).
    //
    // resizeEraseUnsafe() is more memory efficient than
    // resizeErase() because it deletes the old memory block
    // before allocating a new one, but if allocating the
    // new block throws an exception, resizeEraseUnsafe()
    // leaves the array in an unusable state.
    //
    //------------------------------------------------------

    void resizeErase (long sizeX, long sizeY);
    void resizeEraseUnsafe (long sizeX, long sizeY);

    //-------------------------------
    // Return the size of this array.
    //-------------------------------

    long height () const { return _sizeX; }
    long width () const { return _sizeY; }

private:
    Array2D (const Array2D&)            = delete;
    Array2D& operator= (const Array2D&) = delete;
    Array2D (Array2D&&)                 = delete;
    Array2D& operator= (Array2D&&)      = delete;

    long _sizeX;
    long _sizeY;
    T*   _data;
};

//---------------
// Implementation
//---------------

template <class T>
inline void
Array<T>::resizeErase (long size)
{
    T* tmp = new T[size];
    delete[] _data;
    _size = size;
    _data = tmp;
}

template <class T>
inline void
Array<T>::resizeEraseUnsafe (long size)
{
    delete[] _data;
    _data = 0;
    _size = 0;
    _data = new T[size];
    _size = size;
}

template <class T>
inline Array2D<T>::Array2D () : _sizeX (0), _sizeY (0), _data (0)
{
    // empty
}

template <class T>
inline Array2D<T>::Array2D (long sizeX, long sizeY)
    : _sizeX (sizeX), _sizeY (sizeY), _data (new T[sizeX * sizeY])
{
    // empty
}

template <class T> inline Array2D<T>::~Array2D ()
{
    delete[] _data;
}

template <class T>
inline T*
Array2D<T>::operator[] (long x)
{
    return _data + x * _sizeY;
}

template <class T>
inline const T*
Array2D<T>::operator[] (long x) const
{
    return _data + x * _sizeY;
}

template <class T>
inline void
Array2D<T>::resizeErase (long sizeX, long sizeY)
{
    T* tmp = new T[sizeX * sizeY];
    delete[] _data;
    _sizeX = sizeX;
    _sizeY = sizeY;
    _data  = tmp;
}

template <class T>
inline void
Array2D<T>::resizeEraseUnsafe (long sizeX, long sizeY)
{
    delete[] _data;
    _data  = 0;
    _sizeX = 0;
    _sizeY = 0;
    _data  = new T[sizeX * sizeY];
    _sizeX = sizeX;
    _sizeY = sizeY;
}

OPENEXR_IMF_INTERNAL_NAMESPACE_HEADER_EXIT

#endif
