#include <ImageDev/ImageDev.h>
#include <ioformat/IOFormat.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 not done
        if ( isInitialized() == false )
            imagedev::init();

        // Open a grayscale image from a tif file
        auto imageInput = readImage( std::string( IMAGEDEVDATA_IMAGES_FOLDER ) + "objects.tif" );

        // Threshold and label the binary input
        auto imageBin = thresholdingByCriterion( imageInput,
                                                 ThresholdingByCriterion::ComparisonCriterion::GREATER_THAN_OR_EQUAL_TO,
                                                 40 );
        auto imageLab = labeling2d( imageBin, Labeling2d::LABEL_8_BIT, Labeling2d::CONNECTIVITY_8 );

        // Calibrate this image to match 1 pixel to 1.4 mm
        Vector3d spacing{ 1.4, 1.4, 1 };
        imageLab->setSpatialSpacing( spacing );
        imageLab->setSpatialUnit( "mm" );

        // Define the analysis features to be computed
        AnalysisMsr::Ptr analysis = std::make_shared< AnalysisMsr >();
        analysis->select( NativeMeasurements::area2d );
        analysis->select( NativeMeasurements::inverseCircularity2d );
        analysis->select( NativeMeasurements::feretDiameter2d );

        // Launch the feature extraction on the segmented image
        labelAnalysis( imageLab, imageInput, analysis );

        // Print the analysis table header
        std::string lineToPrint( "Label\t" );
        for ( const auto& measure : analysis->getMeasurements() )
        {
            // Build and print the table header
            if ( measure->shape().size() == 1 )
                // The measurement is a scalar value
                lineToPrint += measure->name() + "(" + measure->information().physicalUnit() + ")\t";
            else if ( measure->shape().size() == 2 )
                // The measurement is an array, loop on it
                for ( size_t j = 0; j < measure->shape()[1]; ++j )
                    lineToPrint += measure->name() + "[" + std::to_string( j ) + "](" +
                                   measure->information().physicalUnit() + ")\t";
        }
        std::cout << lineToPrint << std::endl;

        VectorXu64 index;
        // Print all measurement results for each label
        for ( int i = 0; i < analysis->labelCount(); ++i )
        {
            lineToPrint = std::to_string( i + 1 ) + "\t";
            for ( const auto& measure : analysis->getMeasurements() )
            {
                index = measure->shape();
                index[0] = i;
                if ( measure->shape().size() == 1 )
                    // The measurement is a scalar value
                    lineToPrint += std::to_string( measure->toDouble( index ) ) + "\t";
                else if ( measure->shape().size() == 2 )
                    // The measurement is an array, loop on it
                    for ( size_t j = 0; j < measure->shape()[1]; ++j )
                    {
                        index[1] = j;
                        lineToPrint += std::to_string( measure->toDouble( index ) ) + "\t";
                    }
            }
            std::cout << lineToPrint << std::endl;
        }

        // Export the analysis in a dataframe and save it in a csv file
        auto dataframe = analysis->toDataFrame();
        writeView( dataframe, "T04_03_analysis.csv" );

        std::cout << "This example ran successfully." << std::endl;
    }
    catch ( const imagedev::Exception& error )
    {
        // Print potential exception in the standard output
        std::cerr << "T04_03_MeasurementBrowsing 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;
}
