// Copyright (C) 2016 EDF
// All Rights Reserved
// This code is published under the GNU Lesser General Public License (GNU LGPL)
#include <vector>
#include <array>
#include <iostream>
#include <Eigen/Dense>
#include "StOpt/core/utils/types.h"
#include "StOpt/core/grids/InterpolatorSpectral.h"

using namespace std;
using namespace Eigen;

namespace StOpt
{

// Only upper part is filled in
ArrayXd localConstMatrixCalculation(const ArrayXi &p_simToCell,
                                    const int  &p_nbCell)
{
    int nbSimul =  p_simToCell.size();
    // initialization
    ArrayXd matReg = ArrayXd::Zero(p_nbCell);

    for (int is = 0; is < nbSimul ; ++is)
    {
        // cell number
        int ncell = p_simToCell(is) ;
        matReg(ncell) += 1;
    }

    // check if matrix is null
    for (int iReg = 0; iReg < matReg.size(); ++iReg)
        if (matReg(iReg) < 0.5)  // here use constant because matReg is integer
            matReg(iReg) = 1;

    // normalization
    matReg /= nbSimul ;
    return matReg;

}

ArrayXXd localConstSecondMemberCalculation(const ArrayXi &p_simToCell,
        const int  &p_nbCell,
        const ArrayXXd &p_fToRegress)
{
    int nbSimul = p_simToCell.size();
    // number of function to regress
    int iSecMem = p_fToRegress.rows();

    ArrayXXd secMember = ArrayXXd::Zero(p_fToRegress.rows(), p_nbCell);
    for (int is = 0; is < nbSimul ; ++is)
    {
        int nCell = p_simToCell(is);
        // nest on second members
        for (int nsm = 0 ; nsm < iSecMem ; ++nsm)
        {
            double xtemp = p_fToRegress(nsm, is) ;
            // second member of the regression problem
            secMember(nsm, nCell) += xtemp ;
        }
    }
    // normalization
    secMember /= nbSimul;
    return secMember;
}

ArrayXXd localConstReconstruction(const ArrayXi &p_simToCell,
                                  const ArrayXXd   &p_foncBasisCoef)
{
    int nbSimul = p_simToCell.size();
    // initialization
    ArrayXXd solution(p_foncBasisCoef.rows(), nbSimul)  ;

    for (int is = 0; is < nbSimul ; ++is)
    {
        int nCell = p_simToCell(is) ;
        for (int isecMem = 0; isecMem < p_foncBasisCoef.rows(); ++isecMem)
            solution(isecMem, is) = p_foncBasisCoef(isecMem, nCell) ;
    }
    return solution;
}




ArrayXd  localConstReconstructionOnePoint(const ArrayXd &p_oneParticle,
        const vector< shared_ptr< ArrayXd > >   &p_mesh1D,
        const ArrayXXd   &p_foncBasisCoef)
{
    // Values of the functon basis and position of the  particle in the mesh
    int iCell = 0 ;
    int idecCell = 1;
    for (int id = 0 ; id < p_oneParticle.size() ; id++)
    {
        int iMesh = 1 ;
        while ((p_oneParticle(id) > (*p_mesh1D[id])(iMesh)) && (iMesh < p_mesh1D[id]->size() - 1)) iMesh++;
        iCell += (iMesh - 1) * idecCell;
        idecCell *= p_mesh1D[id]->size() - 1;
    }
    // reconstruction
    ArrayXd solution(p_foncBasisCoef.rows());
    for (int isecMem = 0; isecMem < solution.size(); ++isecMem)
        solution(isecMem) = p_foncBasisCoef(isecMem, iCell) ;
    return solution;
}

double  localConstReconsOnePointSimStock(const ArrayXd &p_oneParticle, const ArrayXd &p_stock,
        const std::vector< std::shared_ptr<InterpolatorSpectral> > &p_interpBaseFunc,
        const std::vector< std::shared_ptr< ArrayXd >  > &p_mesh1D)
{
    // Values of the functon basis and position of the  particle in the mesh
    int iCell = 0 ;
    int idecCell = 1;
    for (int id = 0 ; id < p_oneParticle.size() ; id++)
    {
        int iMesh = 1 ;
        while ((p_oneParticle(id) > (*p_mesh1D[id])(iMesh)) && (iMesh < p_mesh1D[id]->size() - 1)) iMesh++;
        iCell += (iMesh - 1) * idecCell;
        idecCell *= p_mesh1D[id]->size() - 1;
    }
    // reconstruction
    return  p_interpBaseFunc[iCell]->apply(p_stock);
}

}
