// Copyright (C) 2025 EDF
// All Rights Reserved
// This code is published under the GNU Lesser General Public License (GNU LGPL)
#ifndef GRIDADAPT1D_H
#define GRIDADAPT1D_H
#include <memory>
#include <vector>
#include <list>
#include <array>
#include <Eigen/Dense>
#include "StOpt/core/grids/GridAdaptBase.h"
#include "StOpt/core/grids/Mesh1D.h"

/** \file GridAdapt1D.h
 * \brief Defines a 1D grid with refinement possibility
 * \author  Pierre Gruet
 */

namespace StOpt
{
  class GridAdapt1D : public GridAdaptBase
  {
  private :
    
    std::list< std::pair<std::shared_ptr< Mesh1D> , std::shared_ptr< std::vector< Eigen::ArrayXi > > > >  m_meshes ; /// a grid is a collection of meshes and position  in grid
    //  std::vector< std::array<int,1> >  is a collection of (i) defining the position in the grid.  m_mesh is not necessary
    double m_xMin ; /// x min of grid
    double m_xMax ; /// x max of grid
   
    
  public:

    // default constructor
    GridAdapt1D(){}

    /// \brief Constructor of an uniform grid mesh
    /// \param p_xMin  minimal   grid x axis
    /// \param p_xMax  maximal   grid x axis
    /// \param p_nbMeshX number of mesh in x
    GridAdapt1D( const double & p_xMin , const double & p_xMax, const int & p_nbMeshX);

    /// \brief second constructor
    /// \param p_xMin  minimal   grid x axis
    /// \param p_xMax  maximal   grid x axis
    /// \param p_meshe  all the meshes
    /// \param p_points all the vertices 
     GridAdapt1D(const double & p_xMin , const double & p_xMax,
		 const std::list<  std::pair<std::shared_ptr< Mesh1D> , std::shared_ptr< std::vector< Eigen::ArrayXi > > > > & p_meshes,  const std::vector< Eigen::ArrayXd > & p_points );
    
    /// \brief third constructor without meshes
    /// \param p_xMin  minimal   grid x axis
    /// \param p_xMax  maximal   grid x axis
    /// \param p_points all the vertices 
    GridAdapt1D(const double & p_xMin , const double & p_xMax,
		 const std::vector< Eigen::ArrayXd > & p_points );
    
    /// \brief split a mesh in the list  : create new meshes and remove old mesh  form list
    void splitMesh( std::pair<std::shared_ptr< Mesh1D>, std::shared_ptr< std::vector< Eigen::ArrayXi > > >  & p_meshToSplit);

    /// \brief from  an hypercube : defines all meshes intersecting this hypercube
    /// \param p_xMin  minimal  hypercube x axis 
    /// \param p_xMax  maximal  hypercube x axis
    std::list< std::pair<std::shared_ptr< Mesh1D>, std::shared_ptr< std::vector< Eigen::ArrayXi > > > >  intersect(const double & p_xMin, const double & p_xMax) const;

    /// get dimension of the grid
    virtual int getDim() const {return 1;}

    // get back Meshes
    inline std::list< std::pair<std::shared_ptr< Mesh1D>, std::shared_ptr< std::vector< Eigen::ArrayXi > > > >  getMeshes() const { return m_meshes ;}

    /// for a point get the mesh associated
    /// \param p_x x axis
    std::shared_ptr< Mesh1D>  getMeshWithPoint(const double & p_x) const;

    /// get back x axis value for point given its number
    inline double getXCoord(const int & p_ipt) const { return m_points[p_ipt][0];}
    /// get back an array with coordinates
    inline Eigen::ArrayXd getCoord(const int & p_ipt) const { return  Eigen::ArrayXd::Map(m_points[p_ipt].data(), 1);}

    inline double getXMin() const { return m_xMin;}
    inline double getXMax() const { return m_xMax;}

    
  };
}
#endif
