#ifndef AdvMultiDataOperationBase_h
#define AdvMultiDataOperationBase_h

#include "Header.hh"
#include "CppToPython.hh"
#include "HeaderBase.hh"
#include "ElementContainer.hh"
#include "ElementContainerArray.hh"
#include "ElementContainerMatrix.hh"

#include "AdvMessage.hh"
#include "AdvFuncBase.hh"
#include "AdvFuncParser.hh"
#include "AdvDomain.hh"
#include "AdvParamSet.hh"
#include "AdvMethod.hh"
#include "AdvMethodType.hh"
#include "AdvMultiDataMethodFactory.hh"

#include "AdvOperationType.hh"

/** 
 *  the base of operations 
 *
 *  @author TANIMORI Souichirou, AdvanceSoft Corp.
 *  @version 0.6
 */
class AdvMultiDataOperationBase : public AdvMessage {
    /* {since=2.1.14}*/

    private:
        /** the name of the class (for debug) */
        static const std::string className;

    private:
        /**  type of opearation */
        AdvOperationType operationType;

    protected:
    
        /**  source data */
        ElementContainerArray *source;
        /* {set=true, get=false}*/

        /**
         *  the pointer to result element container
         *   @version 0.0
         */
        ElementContainerArray *result;
        /* {pointer=true}*/


        /**  method */
        AdvMultiDataMethod *method;

        /** parameters for the method*/
        AdvParamSet param;

        /**  domain of the opearion */
        std::vector<AdvDomain> domains;

     protected:
        /**
         *  set the type of the opearaion
         *
         *  @param[in] type the type of the opearation
         */
        void setOperationType(AdvOperationType type) { this->operationType=type; };

        /**
         *  set tje type of opearaion
         *
         *  @param[in] type the type of the opearation
         */
        AdvOperationType getOpeartionType() { return this->operationType; } ;


    public:
    
        /**  default constructor */
        AdvMultiDataOperationBase();

        /**  constructor */
        AdvMultiDataOperationBase(ElementContainerArray *src);

        /**  constructor */
#ifndef SWIGPYTHON
        AdvMultiDataOperationBase(ElementContainerArray *src,       AdvMultiDataMethod     *method);
#endif
        AdvMultiDataOperationBase(ElementContainerArray *src, const AdvMethodType &methodType);
        AdvMultiDataOperationBase(ElementContainerArray *src, const std::string     &methodName);

        /**  constructor */
#ifndef SWIGPYTHON
        AdvMultiDataOperationBase(ElementContainerArray *src,       AdvMultiDataMethod     *method,     const Double xLower, const Double xUpper);
#endif
        AdvMultiDataOperationBase(ElementContainerArray *src, const AdvMethodType &methodType, const Double xLower, const Double xUpper);
        AdvMultiDataOperationBase(ElementContainerArray *src, const std::string     &methodName, const Double xLower, const Double xUpper);

        /**  constructor */
#ifndef SWIGPYTHON
        AdvMultiDataOperationBase(ElementContainerArray *src,       AdvMultiDataMethod     *method,     const UInt4 lower, const UInt4 upper);
#endif
        AdvMultiDataOperationBase(ElementContainerArray *src, const AdvMethodType &methodType, const UInt4 lower, const UInt4 upper);
        AdvMultiDataOperationBase(ElementContainerArray *src, const std::string     &methodName, const UInt4 lower, const UInt4 upper);
    
        /**  constructor with determing a AdvDomain for the operation */
        AdvMultiDataOperationBase(ElementContainerArray *src, const Double xLower, const Double xUpper);


        /**  destructor */
        virtual ~AdvMultiDataOperationBase();
    
        /**  set an element container as the source */
        void setSource(ElementContainerArray *src);

        /**  set a method */
#ifndef SWIGPYTHON
        void setMethod(AdvMultiDataMethod *method);
#endif

        /**  set a method */
        void setMethod(const AdvMethodType &type);

        /**  set a method */
        void setMethod(const std::string &method);
   
        /**  set member variables by given values */
        void setDomain(ElementContainerArray *src, const Double lower, const Double upper);
    
        /**  set the domain of the operarion to given parameters */
        void setDomain(const Double xLower, const Double xUpper);

        /**  set the domain for the i-th source data to given parameters */
        void setDomain(const UInt4 i, const Double xLower, const Double xUpper);

        /**  set the domain of the operarion to given parameters, dprecated */
        void setDomain(const UInt4 lower, const UInt4 upper);

        /**  set the domain for the i-th source data to given parameters */
        void setDomain(const UInt4 i, const UInt4 lower, const UInt4 upper);

        /**  set the domain of the operation by bin IDs */
        void setDomainByBinID(const UInt4 lower, const UInt4 upper);

        /**  set the domain of the i-th source data by bin IDs */
        void setDomainByBinID(const UInt4 i, const UInt4 lower, const UInt4 upper);

        /**  set the domain of the operation by bin bound IDs */
        void setDomainByBinBoundID(const UInt4 lower, const UInt4 upper);

        /**  set the domain of the operation by bin bound IDs */
        void setDomainByBinBoundID(const UInt4 i, const UInt4 lower, const UInt4 upper);

        /**  set the types of the domain bounds for the operarion */
        void setDomain(const AdvDomain::BoundsType type);

        /** get a list of all domains for the operation */
        std::vector<AdvDomain> getDomain();

        /** get the domain of the i-th source data */
        AdvDomain getDomain(UInt4 i);

        /** get a list of the lower bounds of the domains */
        std::vector<Double> getLowerBound();

        /** get the lower bounds of the domains for the i-th source data */
        Double getLowerBound(UInt4 i);

        /** get a list of the upper bounds of the domains */
        std::vector<Double> getUpperBound();

        /** get the upper bound of the domain for the i-th source data */
        Double getUpperBound(UInt4 i);

        /** get a list of indices for the lower bound of the domains */
        std::vector<UInt4> getLowerBoundID();

        /** get the index for the lower bound of the the domain for the i-th source data */
        UInt4 getLowerBoundID(UInt4 i);

        /** get a list of indices for the upper bounds of all domains */
        std::vector<UInt4>  getUpperBoundID();

        /** get the index for the upper bound of the domain for the i-th source data */
        UInt4  getUpperBoundID(UInt4 i);
    
        /**  set the opration specific paramters */
        void setDefaultParam();

        /**  set the opration specific paramters to the given values */
        void setParam(AdvParamSet param);

        /**  set a boolean paramter with key to the given value */
        void setParam(std::string key, const Bool value);

        /**  set a Int4 type paramter with key to the given value */
        void setParam(std::string key, const Int4 value);

        /**  set a UInt4 type paramter with key to the given value */
        void setParam(std::string key, const UInt4 value);

        /**  set a Double type paramter with key to the given value */
        void setParam(std::string key, const Double value);

        /**  set a Double type std::vector with key */
        void setParam(std::string key, std::vector<Double> value);

        /**  set a PyObject (a list of Double values) with key */
        void setParam(std::string key, PyObject *value);

        /**  set the i-th component of a vector with key to the given value */
        void setParam(std::string key, const UInt4 i, const Double value);

        /**  set a Double type matrix (std::vector< std::vector<Double> >) with key */
        void setParam(std::string key, std::vector< std::vector<Double> > value);

        /**  set the (i, j) component of a matrix with key to the given value */
        void setParam(std::string key, const UInt4 i, const UInt4 j, const Double value);

        /** set a AdvPeakData type parameter woth key */
        void setParam(std::string key, AdvPeakData value);

#ifndef SWIGPYTHON
        /** set a function list */
        void setParam(std::string key, std::vector<AdvFuncBase*> funcList);
#endif
        /** set a function list */
        void setParam(std::string key, std::string expr);

#ifndef SWIGPYTHON
        /** set a function Matrix */
        void setParam(std::string key, std::vector< std::vector<AdvFuncBase*> > funcMatrix);
#endif
        /** set a function Matrix */
        void setParam(std::string key, std::vector<std::string> expr);


        /**  chsek parameters for the given method */
        Bool checkParam();

        /**  get a set of parameters fitted for the method */
        AdvParamSet getParam();

        /**  get a set of fitted parameters */
        AdvParamSet getFittedParam();

        /**  chi squere */
        virtual Double chiSq()  = 0;

        /**  execute the operation */
        virtual void execute() = 0;
    
        /**  return the element container as a results for the operation. */
        virtual ElementContainerArray getResult();
        virtual ElementContainer getResult(UInt4 i);
    
};

#endif // AdvMultiDataOperationBase_h
