/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, development version     *
*                (c) 2006-2017 INRIA, USTL, UJF, CNRS, MGH                    *
*                                                                             *
* This program is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU Lesser General Public License as published by    *
* the Free Software Foundation; either version 2.1 of the License, or (at     *
* your option) any later version.                                             *
*                                                                             *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
* for more details.                                                           *
*                                                                             *
* You should have received a copy of the GNU Lesser General Public License    *
* along with this program. If not, see <http://www.gnu.org/licenses/>.        *
*******************************************************************************
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/
#ifndef LINEARSUBSPACE_WeightTranslationEngine_H
#define LINEARSUBSPACE_WeightTranslationEngine_H


#include "LinearSubspace/config.h"

#include <sofa/helper/rmath.h>
#include <sofa/helper/OptionsGroup.h>
#include <sofa/helper/vector.h>
#include <sofa/helper/SVector.h>

#include <sofa/core/DataEngine.h>
#include <sofa/core/topology/BaseMeshTopology.h>

#include <algorithm>
#include <iostream>
#include <map>
#include <string>

#include <Eigen/Core>
#include <Eigen/Sparse>
#include <Eigen/Dense>


namespace Eigen {

template <typename RealT>
std::istream& operator>>( std::istream& in, Eigen::Matrix<RealT, Eigen::Dynamic, Eigen::Dynamic>& /*c*/ )
{
    msg_warning("Eigen::Matrix<RealT, Eigen::Dynamic, Eigen::Dynamic>>") << "Eigen::Matrix<RealT, Eigen::Dynamic, Eigen::Dynamic> is not yet readable";
    std::string bidon;
    in >> bidon;
    return in;
}
}



namespace sofa
{
namespace component
{
namespace engine
{

/**
    This Engine translates weights from the weight matrix computed in JacobsonWeightComputationEngine to weights used by LinearMapping.
*/
template <typename DataTypes>
class SOFA_LinearSubspace_API WeightTranslationEngine : public core::DataEngine
{

public:
    typedef core::DataEngine Inherited;
    SOFA_CLASS(SOFA_TEMPLATE(WeightTranslationEngine, DataTypes) , Inherited);

//    typedef typename DataTypes::Real Real;
    typedef DataTypes Real;
    typedef Eigen::Matrix<Real, Eigen::Dynamic, Eigen::Dynamic> DsMat;

    /// Input
    Data<DsMat> d_W;
    Data<uint> d_nbPosConstraints;

    /// Output
    Data<helper::vector<helper::SVector<uint>>> d_indices;
    Data<helper::vector<helper::SVector<Real>>> d_weights;


    virtual std::string getTemplateName() const    { return templateName(this); }
    static std::string templateName(const WeightTranslationEngine<DataTypes>* = NULL) { return "double"; }

    virtual void init()
    {
        setDirtyValue();
    }

    virtual void reinit()
    {
        update();
    }

    void update()
    {
        helper::ReadAccessor< Data<DsMat>> W_accessor(d_W);
        const DsMat& W = W_accessor.ref();

        const uint nbPosConstraints = d_nbPosConstraints.getValue();

        helper::WriteOnlyAccessor< Data<helper::vector<helper::SVector<uint>>> > indices_accessor(d_indices);
        helper::vector<helper::SVector<uint>>& indices = indices_accessor.wref();

        helper::WriteOnlyAccessor< Data<helper::vector<helper::SVector<Real>>> > weights_accessor(d_weights);
        helper::vector<helper::SVector<Real>>& weights = weights_accessor.wref();

        uint nbVerts = W.rows();
        uint nbParents = nbPosConstraints;//W.cols();

//        std::cout << "[WeightTranslationEngine]: START.\n nbVerts=" << nbVerts << ", nbParents" << nbParents << std::endl;

        indices.resize(nbVerts);
        weights.resize(nbVerts);

        for(uint i=0; i<nbVerts; i++)
        {
            indices[i].resize(nbParents);
            weights[i].resize(nbParents);
            for(uint j=0; j<nbParents; j++)
            {
                indices[i][j] = j;
                weights[i][j] = W(i, j);
            }
        }

//        std::cout << "[WeightTranslationEngine]: DONE.\n indices.size()=" << indices.size() << ", weights.size()" << weights.size() << std::endl;
    }


protected:
    WeightTranslationEngine() : Inherited()
      , d_W(initData(&d_W, DsMat(), "W", "Weight matrix"))
      , d_nbPosConstraints(initData(&d_nbPosConstraints, uint(), "nbPosConstraints", "Number of position constraint (as opposed to region constraints)."))
      , d_indices(initData(&d_indices, helper::vector<helper::SVector<uint>>(),"indices","for each weight, index of the related parent."))
      , d_weights(initData(&d_weights, helper::vector<helper::SVector<Real>>(),"weights","skinning weights"))
    {
        addInput(&d_W);
        addInput(&d_nbPosConstraints);
        addOutput(&d_indices);
        addOutput(&d_weights);
    }

    ~WeightTranslationEngine()
    {

    }

};




}
}
}


#endif
