// Copyright (C) 2021 EDF
// All Rights Reserved
// This code is published under the GNU Lesser General Public License (GNU LGPL)
#ifndef CAPA_H
#define CAPA_H
#include <utility>
#include <vector>
#include <array>
#include <Eigen/Dense>

/* \file Capa.h
 * \brief define some capacity (injection, withdrawal) in energy
 *        on one time step
 *        as  function of the date number and the storage level
 *        Equal step on storage level avoid search in data
 */

class Capa
{
private:

    // bool is it , time  independant
    bool  m_bTimeDependent ;

    ///\brief storage data / data[iStep] gives back data (dataLoc)  at date iStep.
    /// Each dataLoc is a pair
    ///   - first element is an array of size 2 : (minimal storage level ML (SML), step in energy ( EStep))
    ///   - second is an array eigen  A and  A(i) is the capacity at storage level SML + i EStep
    ///   .
    std::vector< std::pair< std::array<double, 2>, Eigen::ArrayXd > > m_data;

    /// \brief storage data / data[iStep] gives back data (dataLoc)  at date iStep
    /// dataLoc is a 2 dimensional array
    ///     - dataLoc[:,0] are the stock values
    ///     - dataLoc[:,1] are the capacities associated to each stock point
    std::vector< Eigen::ArrayXXd >  m_dataReduc  ; // fist could removed

    std::vector<int> m_fromDateToData ; /// from a date (integer) , give position in m_data to get data

    ///  GAnache
    /// Grid
    Eigen::ArrayXd m_factorMul; // depend on time step
    Eigen::ArrayXd m_xGrid;
    Eigen::ArrayXd m_valOnGrid;


public:

    /// \brief Constructor : one data for each optimization date
    Capa(const std::vector< std::pair< std::array<double, 2>, Eigen::ArrayXd > > &p_data);

    /// \brief Constructor : one data for each optimization date
    Capa(const std::vector< Eigen::ArrayXXd > &p_data);

    /// \brief second constructor : add a correspondance  between a date and a position in data
    /// \param p_dateToPos   array such that
    ///                       - for date 0 to  p_dateToPos[0] (excluded), tunnel caracteristic is p_data[0]
    ///                       - for date p_dateToPos[i] to p_dateToPos[i+1] excluded  , tunnel caracteristic is p_data[i+1]
    Capa(const std::vector<int>    &p_dateToPos, const std::vector< std::pair< std::array<double, 2>, Eigen::ArrayXd > > &p_data);

    /// \brief second constructor : add a correspondance  between a date and a position in data
    /// \param p_dateToPos   array such that
    ///                       - for date 0 to  p_dateToPos[0] (excluded), tunnel caracteristic is p_data[0]
    ///                       - for date p_dateToPos[i] to p_dateToPos[i+1] excluded  , tunnel caracteristic is p_data[i+1]
    Capa(const std::vector<int>    &p_dateToPos,  const std::vector< Eigen::ArrayXXd > &p_data);


    /// \brief constructor for time independant data
    /// \param p_factorMul   Factor to multiply to capacity (time step depedent)
    /// \param p_xGrid   Grid on storage level values
    /// \param p_valOnGrid Valeu on the grid
    Capa(const  Eigen::ArrayXd &p_factorMul, const Eigen::ArrayXd   &p_xGrid, const Eigen::ArrayXd &p_valOnGrid);

///  \brief get capacity at given time step number and storage level
///  \param  p_idate  date number
///  \param  p_level  niveau de stock
    double getValue(const int   &p_idate,  const double &p_level) const;


/// \brief utilitary to print a given capacity function
    void print(const int   &p_idate) const;

/// \brief utilitary
    inline double getStorageMin(const int   &p_idate) const
    {
        if (m_bTimeDependent)
        {
            if (m_data.size() > 0)
                return m_data[m_fromDateToData[p_idate]].first[0];
            else
                return m_dataReduc[m_fromDateToData[p_idate]](0, 0);
        }
        else
        {
            std::cout << " Not programmed" << std::endl ;
            abort();
        }
    }
    inline double getStep(const int   &p_idate) const
    {
        if (m_bTimeDependent)
        {
            if (m_data.size() > 0)
                return m_data[ m_fromDateToData[p_idate]].first[1];
            else
            {
                std::cout << "Step varies : it should'nt be called" << std::endl ;
                abort();
            }
        }
        else
        {
            std::cout << " Not programmed" << std::endl ;
            abort();
        }
    }
    inline int getNbPoints(const int   &p_idate) const
    {
        if (m_bTimeDependent)
        {
            if (m_data.size() > 0)
                return m_data[m_fromDateToData[p_idate]].second.size();
            else
                return m_dataReduc[m_fromDateToData[p_idate]].rows();
        }
        else
        {
            std::cout << " Not programmed" << std::endl ;
            abort();
        }
    }

    inline double getStorageMax(const int   &p_idate) const
    {
        if (m_bTimeDependent)
        {
            if (m_data.size() > 0)
                return getStorageMin(p_idate) + getStep(p_idate) * (getNbPoints(p_idate) - 1);
            else
                return m_dataReduc[m_fromDateToData[p_idate]](m_dataReduc[m_fromDateToData[p_idate]].rows() - 1, 0) ;
        }
        else
        {
            std::cout << " Not programmed" << std::endl ;
            abort();
        }
    }

};
#endif
