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

#ifndef INCLUDED_IMF_DEEP_IMAGE_CHANNEL_H
#define INCLUDED_IMF_DEEP_IMAGE_CHANNEL_H

//----------------------------------------------------------------------------
//
//      class DeepImageChannel,
//      template class TypedDeepImageChannel<T>
//
//      For an explanation of images, levels and channels,
//      see the comments in header file Image.h.
//
//----------------------------------------------------------------------------

#include "ImfNamespace.h"
#include "ImfUtilExport.h"

#include "ImfImageChannel.h"
#include "ImfImageLevel.h"
#include "ImfSampleCountChannel.h"

#include "ImfDeepFrameBuffer.h"

OPENEXR_IMF_INTERNAL_NAMESPACE_HEADER_ENTER

class DeepImageLevel;
class SampleCountChannel;

//
// Image channels:
//
// A TypedDeepImageChannel<T> holds the pixel data for a single channel
// of one level of a deep image.  Each pixel in the channel contains an
// array of n samples of type T, where T is either half, float or
// unsigned int, and n is stored in a separate sample count channel.
// Sample storage is allocated only for pixels within the data window
// of the level.
//

class IMFUTIL_EXPORT_TYPE DeepImageChannel : public ImageChannel
{
public:
    //
    // Construct an OpenEXR frame buffer slice for this channel.
    // This function is needed reading an image from an OpenEXR
    // file and for saving an image in an OpenEXR file.
    //

    virtual DeepSlice slice () const = 0;

    //
    // Access to the image level to which this channel belongs.
    //

    IMFUTIL_EXPORT DeepImageLevel&       deepLevel ();
    IMFUTIL_EXPORT const DeepImageLevel& deepLevel () const;

    //
    // Access to the sample count channel for this deep channel.
    //

    IMFUTIL_EXPORT SampleCountChannel&       sampleCounts ();
    IMFUTIL_EXPORT const SampleCountChannel& sampleCounts () const;

protected:
    friend class DeepImageLevel;

    IMFUTIL_EXPORT DeepImageChannel (DeepImageLevel& level, bool pLinear);
    IMFUTIL_EXPORT virtual ~DeepImageChannel ();

    DeepImageChannel (const DeepImageChannel& other)            = delete;
    DeepImageChannel& operator= (const DeepImageChannel& other) = delete;
    DeepImageChannel (DeepImageChannel&& other)                 = delete;
    DeepImageChannel& operator= (DeepImageChannel&& other)      = delete;

    virtual void setSamplesToZero (
        size_t i, unsigned int oldNumSamples, unsigned int newNumSamples) = 0;

    virtual void moveSampleList (
        size_t       i,
        unsigned int oldNumSamples,
        unsigned int newNumSamples,
        size_t       newSampleListPosition) = 0;

    virtual void moveSamplesToNewBuffer (
        const unsigned int* oldNumSamples,
        const unsigned int* newNumSamples,
        const size_t*       newSampleListPositions) = 0;

    virtual void initializeSampleLists () = 0;

    IMFUTIL_EXPORT virtual void resize ();

    virtual void resetBasePointer () = 0;
};

template <class T>
class IMFUTIL_EXPORT_TEMPLATE_TYPE TypedDeepImageChannel
    : public DeepImageChannel
{
public:
    //
    // The OpenEXR pixel type of this channel (HALF, FLOAT or UINT).
    //

    virtual PixelType pixelType () const;

    //
    // Construct an OpenEXR frame buffer slice for this channel.
    // This function is needed reading an image from an OpenEXR
    // file and for saving an image in an OpenEXR file.
    //

    virtual DeepSlice slice () const;

    //
    // Access to the pixel at pixel space location (x, y), without bounds
    // checking.  Accessing a location outside the data window of the image
    // level results in undefined behavior.
    //
    // The pixel contains a pointer to an array of samples to type T.  The
    // number of samples in this array is sampleCounts().at(x,y).
    //

    T*       operator() (int x, int y);
    const T* operator() (int x, int y) const;

    //
    // Access to the pixel at pixel space location (x, y), with bounds
    // checking.  Accessing a location outside the data window of the
    // image level throws an Iex::ArgExc exception.
    //

    T*       at (int x, int y);
    const T* at (int x, int y) const;

    //
    // Faster access to all pixels in a single horizontal row of the
    // channel.  Access is not bounds checked; accessing out of bounds
    // rows or pixels results in undefined behavior.
    //
    // Rows are numbered from 0 to pixelsPerColumn()-1, and each row
    // contains pixelsPerRow() values.  The number of samples in
    // row(r)[i] is sampleCounts().row(r)[i].
    //

    T* const*       row (int r);
    const T* const* row (int r) const;

private:
    friend class DeepImageLevel;

    IMFUTIL_HIDDEN
    TypedDeepImageChannel (DeepImageLevel& level, bool pLinear);
    IMFUTIL_HIDDEN
    virtual ~TypedDeepImageChannel ();

    TypedDeepImageChannel (const TypedDeepImageChannel& other) = delete;
    TypedDeepImageChannel&
    operator= (const TypedDeepImageChannel& other)                   = delete;
    TypedDeepImageChannel (TypedDeepImageChannel&& other)            = delete;
    TypedDeepImageChannel& operator= (TypedDeepImageChannel&& other) = delete;

    IMFUTIL_HIDDEN
    virtual void setSamplesToZero (
        size_t i, unsigned int oldNumSamples, unsigned int newNumSamples);

    IMFUTIL_HIDDEN
    virtual void moveSampleList (
        size_t       i,
        unsigned int oldNumSamples,
        unsigned int newNumSamples,
        size_t       newSampleListPosition);

    IMFUTIL_HIDDEN
    virtual void moveSamplesToNewBuffer (
        const unsigned int* oldNumSamples,
        const unsigned int* newNumSamples,
        const size_t*       newSampleListPositions);

    IMFUTIL_HIDDEN
    virtual void initializeSampleLists ();

    IMFUTIL_HIDDEN
    virtual void resize ();

    IMFUTIL_HIDDEN
    virtual void resetBasePointer ();

    T** _sampleListPointers; // Array of pointers to per-pixel
                             //sample lists

    T** _base; // Base pointer for faster access
               // to entries in _sampleListPointers

    T* _sampleBuffer; // Contiguous memory block that
                      // contains all sample lists for
                      // this channel
};

//
// Channel typedefs for the pixel data types supported by OpenEXR.
//

typedef TypedDeepImageChannel<half>         DeepHalfChannel;
typedef TypedDeepImageChannel<float>        DeepFloatChannel;
typedef TypedDeepImageChannel<unsigned int> DeepUIntChannel;

//-----------------------------------------------------------------------------
// Implementation of templates and inline functions
//-----------------------------------------------------------------------------

template <class T>
inline T*
TypedDeepImageChannel<T>::operator() (int x, int y)
{
    return _base[y * pixelsPerRow () + x];
}

template <class T>
inline const T*
TypedDeepImageChannel<T>::operator() (int x, int y) const
{
    return _base[y * pixelsPerRow () + x];
}

template <class T>
inline T*
TypedDeepImageChannel<T>::at (int x, int y)
{
    boundsCheck (x, y);
    return _base[y * pixelsPerRow () + x];
}

template <class T>
inline const T*
TypedDeepImageChannel<T>::at (int x, int y) const
{
    boundsCheck (x, y);
    return _base[y * pixelsPerRow () + x];
}

template <class T>
inline T* const*
TypedDeepImageChannel<T>::row (int r)
{
    return _base + r * pixelsPerRow ();
}

template <class T>
inline const T* const*
TypedDeepImageChannel<T>::row (int r) const
{
    return _base + r * pixelsPerRow ();
}

#ifndef COMPILING_IMF_DEEP_IMAGE_CHANNEL
extern template class IMFUTIL_EXPORT_EXTERN_TEMPLATE
    TypedDeepImageChannel<half>;
extern template class IMFUTIL_EXPORT_EXTERN_TEMPLATE
    TypedDeepImageChannel<float>;
extern template class IMFUTIL_EXPORT_EXTERN_TEMPLATE
    TypedDeepImageChannel<unsigned int>;
#endif

OPENEXR_IMF_INTERNAL_NAMESPACE_HEADER_EXIT

#endif