#ifndef __JTOOLS__JMULTIGRID__
#define __JTOOLS__JMULTIGRID__

#include "JTools/JAbstractMultiMap.hh"
#include "JTools/JGrid.hh"


/**
 * \author mdejong
 */

namespace JTOOLS {}
namespace JPP { using namespace JTOOLS; }

namespace JTOOLS {


  /**
   * Simple data structure for an abstract multidimensional map of equidistant elements.
   *
   * This class implements the JAbstractMultiMap interface.
   */
  template<unsigned int N, class JAbscissa_t>
  struct JMultiGrid :
    public virtual JAbstractMultiMap<N, JAbscissa_t>,
    public JMultiGrid<N - 1, JAbscissa_t>
  {
    typedef JAbscissa_t                                          abscissa_type;
    typedef JMultiKey<N - 1, abscissa_type>                      key_type;

    using JMultiGrid<N - 1, JAbscissa_t>::operator();


    /**
     * Default constructor.
     */
    JMultiGrid() :
      JMultiGrid<N - 1, JAbscissa_t>()
    {}


    /**
     * Constructor.
     *
     * \param  bounds        bounds
     */
    JMultiGrid(const JGrid<abscissa_type>& bounds) :
      JMultiGrid<N - 1, JAbscissa_t>(bounds)
    {
      this->bounds = bounds;
    }


    /**
     * Set bounds.
     *
     * \param  index         index of dimension
     * \param  bounds        bounds
     * \return               true if correctly set; else false
     */
    bool set(unsigned int index, const JGrid<abscissa_type>& bounds)
    {
      if        (index == N - 1) {

	this->bounds = bounds;

      } else if (index <  N - 1) {

	return static_cast<JMultiGrid<N - 1, abscissa_type>&>(*this).set(index, bounds);
      }
      
      return false;
    }


    /**
     * Get abscissa values as a function of given key.
     * 
     * \param  key           key
     * \return               abscissa values
     */
    virtual const JGrid<abscissa_type>& operator()(const key_type& key) const override 
    {
      return bounds;
    }

  protected:
    JGrid<JAbscissa_t> bounds;
  };


  /**
   * Terminator class of recursive class JMultiGrid.
   * This class provides for dummy implementations of interface methods.
   */
  template<class JAbscissa_t>
  struct JMultiGrid<0, JAbscissa_t>
  {
  protected:

    typedef JAbscissa_t                                          abscissa_type;

    friend class JMultiGrid<1, JAbscissa_t>;


    /**
     * Default constructor.
     */
    JMultiGrid() 
    {}


    /**
     * Constructor.
     *
     * \param  bounds        bounds
     */
    JMultiGrid(const JGrid<abscissa_type>& bounds)
    {}


    /**
     * This method does nothing.
     *
     * \param  index         index
     * \param  bounds        bounds
     * \return               false
     */
    bool set(unsigned int index, const JGrid<abscissa_type>& bounds) const
    {
      return false;
    }


    /**
     * Dummy operator.
     */
    void operator()() const
    {}
  };


  /**
   * Helper method for JMultiGrid.
   *
   * \param  bounds          bounds
   * \return                 multidimensional grid
   */
  template<unsigned int N, class JAbscissa_t>
  inline JMultiGrid<N, JAbscissa_t> make_multigrid(const JGrid<JAbscissa_t>& bounds)
  {
    return JMultiGrid<N, JAbscissa_t>(bounds);
  }
}

#endif