#ifndef CONVOLUTION_BASE
#define CONVOLUTION_BASE

#include <cmath>
#include <map>
#include <utility>

#include "Header.hh"
#include "ElementContainer.hh"
#include "ElementContainerArray.hh"

#include "AdvExtVectorTool.hh"

#include "AdvMultiDataMethod.hh"

#include "AdvConvolutionConsts.hh"


class AdvConvolutionBase : virtual public AdvMultiDataMethod, virtual public AdvConvolutionConsts, virtual public AdvMessage {
    private:
        static const string className;

    protected:
        /** convolution type */
        AdvConvolutionConsts::ConvolutionType convType;

        /** the index for resolution, responce or transfer data in elementContainer array */
        UInt4 resId;

        /** threshold for relative error of data spacing (width) */
        Double widthThreshold;

        /**  bin length of data in ElementContainer  and a list of indices for ElementContainerArray
         */
        map<UInt4, vector<UInt4> >dataLengthMap;

        vector<Double> resBin, resY, resErr;

        vector< vector<Double> > srcBins, srcYs, srcErrs;

        vector< vector<Double> > resultBins, resultYs, resultErrs;


    protected:
        virtual void setDefaultConvolutionType(AdvParamSet& param) = 0;

        Bool checkBinWidth(const vector<Double>& bin, const Double widthTreshold);

        virtual Bool checkConvolutionType(AdvParamSet& paramSet) = 0;

        /** import resolution (responce or transfer) data */
        void importRes(ElementContainer* ec, AdvDomain& domain);

        /** import resolution (responce or transfer) data */
        void importSrc(ElementContainerArray& src, vector<AdvDomain>& domains);

        void exportRes(ElementContainer* src, ElementContainer& dest);
        void exportResult(ElementContainer* src, UInt4 id, ElementContainer& dest);

        /** export the results for the i-th. source data */
        void toElementContainer(ElementContainerArray& src, UInt4 i, ElementContainer& dest) const ;

    public:
        ////////////////    constructors and destructors    ////////////////
        /** constructor */
        AdvConvolutionBase();

        AdvConvolutionBase(const string name);

        /** destructor */
        ~AdvConvolutionBase();

        ////////////////    method properties    ////////////////

        /** always return false
         */
        Bool differentiable() const { return false; };

        /** always return false
         */
        Bool isMultiThreaded() const { return false; };

        ////////////////    control    ////////////////

        /** set parameters to the default values.
         *  @param[in] src the container that includes source data
         */
        AdvParamSet setDefaultParam(ElementContainerArray& src);

        /** check data and parameters.
         *  @param[in] src       souece data
         *  @param[in] domains   domains for the source data
         *  @param[in] paramSet  parameters for the method
         */
        Bool checkParam(ElementContainerArray& src, vector<AdvDomain>& domain, AdvParamSet& paramSet);

        /** import data and parameters.
         *  @param[in] src       souece data
         *  @param[in] domains   domains for the source data
         *  @param[in] paramSet  parameters for the method
         */
        void toInnerForm(ElementContainerArray& src, vector<AdvDomain>& domains, AdvParamSet& paramSet);

        /** evaluate */
        void eval() = 0;

        /** export the results to the specified container
         *  @param[in] src   source data
         *  @param[in] dest  the container to export the results
         */
        void toElementContainerArray(ElementContainerArray& src, ElementContainerArray& dest) const ;
};

#endif // CONVOLUTION_BASE
