#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 cube in this array
        const uint64_t side = colCount / 2; // side in pixels of the cube to draw

        // Loop on image slices
        for ( uint64_t k = 0; k < sliCount; ++k )
        {
            // Loop on image rows
            for ( uint64_t i = 0; i < rowCount; ++i )
            {
                // Loop on image columns
                for ( uint64_t j = 0; j < colCount; ++j )
                {
                    if ( ( i >= ( rowCount - side ) / 2 ) && ( i <= ( rowCount + side ) / 2 ) &&
                         ( j >= ( colCount - side ) / 2 ) && ( j <= ( colCount + side ) / 2 ) &&
                         ( k >= ( sliCount - side ) / 2 ) && ( k <= ( sliCount + side ) / 2 ) )
                        imageData[k * rowCount * colCount + i * colCount + j] = 228; // Value inside the cube
                    else
                        imageData[k * rowCount * colCount + i * colCount + j] = 0; // Background value
                }
            }
        }

        // Create an image view of same dimensions
        VectorXu64 imageShape{ colCount, rowCount, sliCount };
        auto image = ImageViewFactory::allocate( imageShape, DataTypeId::UINT8 );
        image->setAxesInterpretation( ImageTypeId::VOLUME );

        // Define the region where to write the data
        VectorXu64 imageOrig{ 0, 0, 0 };
        RegionXu64 imageRegion{ imageOrig, imageShape };

        // Copy the data in the image view
        image->writeRegion( imageRegion, imageData.data() );

        // This image can now be processed by any ImageDev algorithm, for instance for building a color image
        auto imageGauss = gaussianFilter3d( image,
                                            GaussianFilter3d::FilterMode::SEPARABLE,
                                            { 10.0f, 10.0f, 10.0f },
                                            2.0f,
                                            GaussianFilter3d::OutputType::SAME_AS_INPUT,
                                            false );

        // Save the created image with IOFormat
        writeView( imageGauss, "T02_02_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;
}
