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

//-----------------------------------------------------------------------------
//
//	Code examples that show how class RgbaInputFile and
//	class RgbaOutputFile can be used to read and write
//	OpenEXR image files with 16-bit floating-point red,
//	green, blue and alpha channels.
//
//-----------------------------------------------------------------------------

#include <ImfArray.h>
#include <ImfMatrixAttribute.h>
#include <ImfRgbaFile.h>
#include <ImfStringAttribute.h>

#include "drawImage.h"

#include <algorithm>
#include <iostream>

#include "namespaceAlias.h"
using namespace IMF;
using namespace std;
using namespace IMATH_NAMESPACE;

void
writeRgba1 (const char fileName[], const Rgba* pixels, int width, int height)
{
    //
    // Write an RGBA image using class RgbaOutputFile.
    //
    //	- open the file
    //	- describe the memory layout of the pixels
    //	- store the pixels in the file
    //

    RgbaOutputFile file (fileName, width, height, WRITE_RGBA);
    file.setFrameBuffer (pixels, 1, width);
    file.writePixels (height);
}

void
writeRgba2 (
    const char   fileName[],
    const Rgba*  pixels,
    int          width,
    int          height,
    const Box2i& dataWindow)
{
    //
    // Write an RGBA image using class RgbaOutputFile.
    // Don't store the whole image in the file, but
    // crop it according to the given data window.
    //
    //	- open the file
    //	- describe the memory layout of the pixels
    //	- store the pixels in the file
    //

    Box2i          displayWindow (V2i (0, 0), V2i (width - 1, height - 1));
    RgbaOutputFile file (fileName, displayWindow, dataWindow, WRITE_RGBA);
    file.setFrameBuffer (pixels, 1, width);
    file.writePixels (dataWindow.max.y - dataWindow.min.y + 1);
}

void
writeRgba3 (
    const char  fileName[],
    const Rgba* pixels,
    int         width,
    int         height,
    const char  comments[],
    const M44f& cameraTransform)
{
    //
    // Write an RGBA image using class RgbaOutputFile.
    // Store two extra attributes in the image header:
    // a string and a 4x4 transformation matrix.
    //
    //	- open the file
    //	- describe the memory layout of the pixels
    //	- store the pixels in the file
    //

    Header header (width, height);
    header.insert ("comments", StringAttribute (comments));
    header.insert ("cameraTransform", M44fAttribute (cameraTransform));

    RgbaOutputFile file (fileName, header, WRITE_RGBA);
    file.setFrameBuffer (pixels, 1, width);
    file.writePixels (height);
}

void
readRgba1 (
    const char fileName[], Array2D<Rgba>& pixels, int& width, int& height)
{
    //
    // Read an RGBA image using class RgbaInputFile:
    //
    //	- open the file
    //	- allocate memory for the pixels
    //	- describe the memory layout of the pixels
    //	- read the pixels from the file
    //

    RgbaInputFile file (fileName);
    Box2i         dw = file.dataWindow ();

    width  = dw.max.x - dw.min.x + 1;
    height = dw.max.y - dw.min.y + 1;
    pixels.resizeErase (height, width);

    file.setFrameBuffer (&pixels[0][0] - dw.min.x - dw.min.y * width, 1, width);
    file.readPixels (dw.min.y, dw.max.y);
}

void
readRgba2 (const char fileName[])
{
    //
    // Read an RGBA image using class RgbaInputFile.
    // Read the pixels, 10 scan lines at a time, and
    // store the pixel data in a buffer that is just
    // large enough to hold 10 scan lines worth of data.
    //
    //	- open the file
    //	- allocate memory for the pixels
    //	- for each block of 10 scan lines,
    //	  describe the memory layout of the pixels,
    //	  read the pixels from the file,
    //	  process the pixels and discard them
    //

    RgbaInputFile file (fileName);
    Box2i         dw = file.dataWindow ();

    int width = dw.max.x - dw.min.x + 1;

    Array2D<Rgba> pixels (10, width);

    while (dw.min.y <= dw.max.y)
    {
        file.setFrameBuffer (
            &pixels[0][0] - dw.min.x - dw.min.y * width, 1, width);

        file.readPixels (dw.min.y, min (dw.min.y + 9, dw.max.y));
        // processPixels (pixels)

        dw.min.y += 10;
    }
}

void
readHeader (const char fileName[])
{
    //
    // Read an image's header from a file, and if the header
    // contains comments and camera transformation attributes,
    // print the values of those attributes.
    //
    //	- open the file
    //	- get the file header
    //	- look for the attributes
    //

    RgbaInputFile file (fileName);

    const StringAttribute* comments =
        file.header ().findTypedAttribute<StringAttribute> ("comments");

    const M44fAttribute* cameraTransform =
        file.header ().findTypedAttribute<M44fAttribute> ("cameraTransform");

    if (comments) cout << "comments\n   " << comments->value () << endl;

    if (cameraTransform)
        cout << "cameraTransform\n" << cameraTransform->value () << flush;
}

void
rgbaInterfaceExamples ()
{
    cout << "\nRGBA images\n" << endl;
    cout << "drawing image" << endl;

    int w = 800;
    int h = 600;

    Array2D<Rgba> p (h, w);
    drawImage1 (p, w, h);

    cout << "writing entire image" << endl;

    writeRgba1 ("rgba1.exr", &p[0][0], w, h);

    cout << "writing cropped image" << endl;

    writeRgba2 (
        "rgba2.exr",
        &p[0][0],
        w,
        h,
        Box2i (V2i (w / 6, h / 6), V2i (w / 2, h / 2)));

    cout << "writing image with extra header attributes" << endl;

    writeRgba3 ("rgba3.exr", &p[0][0], w, h, "may contain peanuts", M44f ());

    cout << "reading rgba file" << endl;

    readRgba1 ("rgba2.exr", p, w, h);

    cout << "reading rgba file into 10-scanline buffer" << endl;

    readRgba2 ("rgba2.exr");

    cout << "reading extra file header attributes" << endl;

    readHeader ("rgba3.exr");
}
