#ifndef Manyo_develop_operation_AdvDomain_h
#define Manyo_develop_operation_AdvDomain_h

#include <cassert>
#include <string>
#include <stdexcept>

#include "gsl/gsl_vector.h"

#include "Header.hh"
#include "ElementContainer.hh"
#include "AdvMessage.hh"

//#include "BoundsType.hh"

/*! 
 *  a domain for a operation
 *
 *  \author TANIMORI Souichirou, AdvanceSoft Corp.
 *  \version 0.0
 *  \since   2.1.14
 */
class AdvDomain : public AdvMessage {

    private:
        static const std::string className; // = std::string("AdvDomain");

    public:
        enum BoundsType {
            CC, /*!< the domain is a close - close section */
            CO, /*!< the domain is a close - open  section */
            OC, /*!< the domain is a open  - close section */
            OO  /*!< the domain is a open  - open  */
        };

    private:
        /*!  the index of the lower bound of the bins */
        UInt4 lowerBound;

        /*! the index of the upper bound of bins */
        UInt4 upperBound;

        /*! type of the domain, one of CLOSE_CLOSE, CLOSE_OPEN, OPEN_CLOSE, OPEN, OPEN */
        BoundsType type;

        /*! the bounds of the bins */
        std::vector<Double> srcBin;
        /* {MultiplicityType=std::vector, MultiplicityType=std::vector, reference=true}*/

    //private:
    public:
        /*!  the index of the nearest bin bound in the source from the given value */
        UInt4 nearest(const Double x) const ;

        /*!  the index of the bin that contains the given value */
        UInt4 section_search(const Double x) const ;

    public:
        /*!  default constructor */
        AdvDomain();

        /*! constructor */
        AdvDomain(ElementContainer &src);

        /*!  constructor */
        AdvDomain(ElementContainer &src, const Double xLower, const Double xUpper);

        /*!  constructor */
        AdvDomain(ElementContainer &src, const UInt4 lower, const UInt4 upper);

        /*!  constructor */
        AdvDomain(ElementContainer &src, const Double xLower, const Double xUpper, const BoundsType type);

        /*!  constructor */
        AdvDomain(ElementContainer &src, const UInt4 lower, const UInt4 upper, const BoundsType type);

        /*!  copy constructor */
        AdvDomain(const AdvDomain &domain);

        /*!  destructor */
        ~AdvDomain();

        /*!  operator = */
        AdvDomain operator = (const AdvDomain &domain) ;

        /*!  set the source by  bin bounds that an element container contains */
        void setSource(ElementContainer &src)  ;

        /*!  set the range by two indices of the bins */
        void setBinRange(const UInt4 lower, const UInt4 upper) ;

        /*!  set the range by two indices of the bin bounds */
        void setBinBoundRange(const UInt4 lower, const UInt4 upper) ;

        /*!  set the range by two indices of bin bin bounds, dprecated */
        void setRange(const UInt4 lower, const UInt4 upper) ;


        /*!  set the range by the values of lower and upper bounds */
        void setRange(const Double xLower, const Double xUpper) ;

        /*!  set the type of the domain */
        void setType(const BoundsType type);

        /*!  set the domain */
        void set(ElementContainer &src, const Double xLower, const Double xUpper);

        /*!  set the domain */
        void set(ElementContainer &src, const UInt4 lower, const UInt4 upper);

        /*!  set the domain */
        void set(ElementContainer &src, const Double xLower, const Double xUpper, const BoundsType type);

        /*!  set the domain */
        void set(ElementContainer &src, const UInt4 lower, const UInt4 upper, const BoundsType type);

        /*!  set lower bound by the index of  the bin */
        void setLowerBoundID(const UInt4 lower);

        /*!  set the upper bound of the index of the bin */
        void setUpperBoundID(const UInt4 upper);

        /*!  set the lower bound of the domain */
        void setLowerBound(const Double xLower);

        /*!  set the upper bound of the domain */
        void setUpperBound(const Double xUpper);



        /*!  get bin bounds in the domain form the source*/
        std::vector<Double> *getBin();

        /*!  create a list of the dividing point in each bin. Dp[i-l] = c*x[i] + (1-c)*x[i+1]. c is the division ratio.
         *   @param[in] c division ratio
         */
        std::vector<Double>* createDividingPoints(const Double c);

        /*!  create the enter of each bin in the domain */
        std::vector<Double> *createXC();

        /*! get a list of the lower bounds of bins */
        std::vector<Double>* getXL();

        /*! get a list of the upper bounds of bins */
        std::vector<Double>* getXU();

        /*!  create the enter of each bin in the domain as a gsl std::vector */
        gsl_vector *createXCAsGslVector();

        /*!  get bin bounds in the domain from the source as a gsl std::vector */
        gsl_vector *getBinAsGslVector();

        /*!  create a list of the dividing points of bins as gsl_vector */
        gsl_vector* createDividingPointsAsGslVector(const Double c);

        /*! get a list of the lower bounds of bins */
        gsl_vector* getXLAsGslVector();

        /*! get a list of the upper bounds of bins */
        gsl_vector* getXUAsGslVector();


        /*! get the number of bins included in the domain */
        UInt4 getNumberOfBin() const ;

        /*! get the index of the lower bound in the domain */
        UInt4 getLowerBoundID() const ;

        /*!  the index of the upper bin bound */
        UInt4 getUpperBoundID() const ;

        /*! get the index of the lower bin in the domain */
        UInt4 getLowerBinID() const ;

        /*! get the index of the upper bin in the domain */
        UInt4 getUpperBinID() const ;

        /*! get the indecies of the lower and upper bins */
        std::vector<Int4> *getBinIDs() const ;

        /*! get the index of the lower bin bound in the domain */
        UInt4 getLowerBinBoundID() const ;

        /*! get the index of the upper bin bound in the domain */
        UInt4 getUpperBinBoundID() const ;

        /*!  get the lower and upper Bounds of the domain */
        std::vector<Int4> *getBinBoundIDs() const ;

        /*!  the value of the lower bin bound */
        Double getLowerBound() const ;

        /*!  the value of the upper bin bound */
        Double getUpperBound() const ;

        /*! width of the domain abs(upperBound - lowerBound) */
        Double getWidth() const ;

        /*! the both bounds of the domain */
        std::vector<Double> getBinBounds() const ;

        /*!  get type of the domain */
        BoundsType getType() const ;

        /*!  add the infomation of the domain to the destination */
        void addToHeaderOfResult(const std::string &Key, ElementContainer &dest);
};

#endif // Manyo_develop_operation_AdvDomain_h

