#include <SofaTest/Sofa_test.h>
#include <SofaTest/DataEngine_test.h>

#include "../BranchingCellIndicesFromPositions.h"
#include "../BranchingCellIndicesFromLabels.h"
#include "../BranchingCellOffsetsFromPositions.h"
#include "../BranchingCellOffsetsFromConnectLabels.h"
#include "../BranchingCellVolumes.h"
#include "../BranchingImageConverter.h"
#include "../MergeBranchingImages.h"
#include "../BranchingImageSubsampler.h"
#include "../ImageValuesFromPositions.h"
#include "../MergeImagesIntoBranching.h"
#include "../TransferFunction.h"
#include "../MeshLabeler.h"
#include "../ImageMeshIntersection.h"

#if SOFA_HAVE_FLEXIBLE
#include <Flexible/types/AffineTypes.h>
#include "../flexiblePlugin/mass/MassFromDensity.h"
#include "../flexiblePlugin/quadrature/ImageGaussPointSampler.h"
#endif

#include <SceneCreator/SceneCreator.h>
#include <SofaSimulationGraph/DAGSimulation.h>

namespace sofa {



/// a utility for BranchingImageDataEngine test
/// allocating all engine's input Data<Image>
template <typename DataEngineType>
struct BranchingImageDataEngine_test : public DataEngine_test<DataEngineType>
{
     typedef core::objectmodel::DDGNode DDGNode;
    typedef DDGNode::DDGLinkContainer DDGLinkContainer;

    // Root of the scene graph
    simulation::Node::SPtr root;


    virtual void init()
    {
        DataEngine_test<DataEngineType>::init();

        const DDGLinkContainer& parent_inputs = this->m_engineInput->DDGNode::getInputs();
        for( unsigned i=0, iend=parent_inputs.size() ; i<iend ; ++i )
        {
            core::objectmodel::BaseData* data = static_cast<core::objectmodel::BaseData*>(parent_inputs[i]);

            const defaulttype::AbstractTypeInfo *typeinfo = data->getValueTypeInfo();

            if( typeinfo->name().find("Image") != std::string::npos || typeinfo->name().find("BranchingImage") != std::string::npos )
            {
                if( !data->isSet() )
                {
                    defaulttype::BaseImage* img = static_cast<defaulttype::BaseImage*>( data->beginEditVoidPtr() );
    //                std::cerr<<data->getName()<<" is a Data<Image>\n";
                    // allocate input
                    img->setDimensions( defaulttype::BaseImage::imCoord(1,1,1,1,1) );
                    data->endEditVoidPtr();
                }
            }
        }


        if( this->root ) modeling::initScene(this->root);
    }


    void openScene( const std::string& fileName )
    {
        this->root = modeling::clearScene();
        this->root = down_cast<sofa::simulation::Node>( sofa::simulation::getSimulation()->load(fileName.c_str()).get() );
    }

};

using namespace defaulttype;
using namespace component::engine;

typedef testing::Types<
  TestDataEngine< BranchingCellIndicesFromPositions<ImageUI,BranchingImageUC> >
, TestDataEngine< BranchingCellIndicesFromLabels<BranchingImageUC> >
, TestDataEngine< BranchingCellOffsetsFromPositions<BranchingImageUC> >
, TestDataEngine< BranchingCellOffsetsFromConnectLabels<BranchingImageUC> >
, TestDataEngine< BranchingCellVolumes<ImageUI,BranchingImageUC> >
, TestDataEngine< ImageToBranchingImageConverter<unsigned char> >
, TestDataEngine< BranchingImageSubsampler<BranchingImageUC> >
, TestDataEngine< ImageValuesFromPositions<BranchingImageUC> >
, TestDataEngine< MergeBranchingImages<BranchingImageUC> >
, TestDataEngine< MergeImagesIntoBranching<ImageUC,BranchingImageUC> >
, TestDataEngine< TransferFunction<BranchingImageUC,BranchingImageUC> >
, TestDataEngine< MeshLabeler<BranchingImageUC> >
, TestDataEngine< MeshLabeler<ImageUC> >
, TestDataEngine< ImageMeshIntersection<BranchingImageUC> >
, TestDataEngine< ImageMeshIntersection<ImageUC> >
#if SOFA_HAVE_FLEXIBLE
, TestDataEngine< MassFromDensity<Affine3Types,BranchingImageUC> >
, TestDataEngine< ImageGaussPointSampler<BranchingImageD,BranchingImageUC> >
#endif
> TestTypes; // the types to instanciate.

/////// A BIG MESS TO MAKE A SPECIFIC TEST FOR EACH ENGINE THAT REQUIRES A SCENE

/// standard test does not need a scene and so does nothing particular
template<class TestDataEngineType>
struct SpecificTest
{
    static void run( TestDataEngineType* ) {}
};

/// specific scene for MassFromDensity
#if SOFA_HAVE_FLEXIBLE
template<>
struct SpecificTest<BranchingImageDataEngine_test< TestDataEngine< MassFromDensity<Affine3Types,BranchingImageUC> > > >
{
    typedef BranchingImageDataEngine_test< TestDataEngine< MassFromDensity<Affine3Types,BranchingImageUC> > > TestDataEngineType;
    static void run( TestDataEngineType* tested )
    {
        tested->openScene( std::string(BRANCHINGIMAGE_TEST_SCENES_DIR) + "/Engine1.scn" );

        simulation::Node::SPtr childNode = tested->root->getChild("mass");

        childNode->addObject( tested->m_engine );
        childNode->addObject( tested->m_engineInput );

        tested->m_engineInput->image.setParent( "@density.outputImage" );
        tested->m_engineInput->transform.setParent( "@../image.transform" );
    }
};



/// specific scene for ImageGaussPointSampler
template<>
struct SpecificTest<BranchingImageDataEngine_test< TestDataEngine< ImageGaussPointSampler<BranchingImageD,BranchingImageUC> > > >
{
    typedef BranchingImageDataEngine_test< TestDataEngine< ImageGaussPointSampler<BranchingImageD,BranchingImageUC> > > TestDataEngineType;
    static void run( TestDataEngineType* tested )
    {
        tested->openScene( std::string(BRANCHINGIMAGE_TEST_SCENES_DIR) + "/Engine1.scn" );

        simulation::Node::SPtr childNode = tested->root->getChild("child");

        childNode->addObject( tested->m_engine );
        childNode->addObject( tested->m_engineInput );

        tested->m_engineInput->f_index.setParent("@../SF.indices");
        tested->m_engineInput->f_w.setParent("@../SF.weights");
        tested->m_engineInput->f_transform.setParent("@../SF.transform");
        tested->m_engine->targetNumber.setValue(10);
        tested->m_engine->f_method.beginWriteOnly()->setSelectedItem(2); tested->m_engine->f_method.endEdit();
        tested->m_engine->f_order.setValue(1);
        tested->m_engineInput->f_mask.forceSet(); // not to be allocated by ImageDataEngine_test
    }
};
#endif


// ========= Tests to run for each instanciated type
TYPED_TEST_CASE( BranchingImageDataEngine_test, TestTypes );

// test number of call to DataEngine::update
TYPED_TEST( BranchingImageDataEngine_test, basic_test )
{
    SpecificTest<TestFixture>::run( this );

    this->run_basic_test();
}




}// namespace sofa
