#include <ImageDev/ImageDev.h>
#include <ioformat/IOFormat.h>
#include <iolink/view/ImageViewFactory.h>
#include <iolink/view/ImageViewProvider.h>
#include <string.h>

using namespace imagedev;
using namespace ioformat;
using namespace iolink;

int
main( int argc, char* argv[] )
{
    int status = 0;

    try
    {
        // ImageDev library initialization
        if ( imagedev::isInitialized() == false )
            imagedev::init();

        // Initialize an unsigned 8-bit array storing data of a 3D image
        const uint64_t rowCount = 220;
        const uint64_t colCount = 192;
        const uint64_t sliCount = 128;
        std::vector< uint8_t > imageData( rowCount * colCount * sliCount );

        // Define a synthetic sphere in this array
        const int squareRadius = ( sliCount / 2 - 10 ) * ( sliCount / 2 - 10 ); // Radius of the sphere to draw
        int distToCenter;

        // Loop on image slices
        for ( int k = 0; k < sliCount; ++k )
        {
            // Loop on image rows
            for ( int i = 0; i < rowCount; ++i )
            {
                // Loop on image columns
                for ( int j = 0; j < colCount; ++j )
                {
                    distToCenter = ( i - rowCount / 2 ) * ( i - rowCount / 2 ) +
                                   ( j - colCount / 2 ) * ( j - colCount / 2 ) +
                                   ( k - sliCount / 2 ) * ( k - sliCount / 2 );
                    if ( distToCenter <= squareRadius )
                        imageData[k * rowCount * colCount + i * colCount + j] = 200; // Value inside the sphere
                    else
                        imageData[k * rowCount * colCount + i * colCount + j] = 0; // Background value
                }
            }
        }

        // Create an image view of same dimensions directly from this buffer
        VectorXu64 imageShape{ colCount, rowCount, sliCount };
        auto image = ImageViewFactory::fromBuffer(
            imageShape, DataTypeId::UINT8, imageData.data(), rowCount * colCount * sliCount * sizeof( uint8_t ) );
        image->setAxesInterpretation( ImageTypeId::VOLUME );

        // This image can now be processed by any ImageDev algorithm, for instance to add a Gaussian noise inside
        auto imageNoise = randomGaussianImage3d(
            RandomGaussianImage3d::OutputType::SIGNED_INTEGER_8_BIT, colCount, rowCount, sliCount, 0.0f, 20.0f );
        auto imageOut =
            arithmeticOperationWithImage( image, imageNoise, ArithmeticOperationWithImage::ArithmeticOperator::ADD );

        // Save the created image with IOFormat
        writeView( imageOut, "T02_03_output.tif" );

        std::cout << "This example ran successfully." << std::endl;
    }
    catch ( const imagedev::Exception& error )
    {
        // Print potential exception in the standard output
        std::cerr << "ImageDev exception: " << error.what() << std::endl;
        status = -1;
    }

    // ImageDev library finalization
    imagedev::finish();

    // Check if we must ask for an enter key to close the program
    if ( !( ( argc == 2 ) && strcmp( argv[1], "--no-stop-at-end" ) == 0 ) )
        std::cout << "Press Enter key to close this window." << std::endl, getchar();

    return status;
}
