#ifndef SASSECTORAVERAGEBYANGLE
#define SASSECTORAVERAGEBYANGLE

#include <math.h>
#include "Header.hh"
#include "OperatorBase.hh"
#include "ElementContainer.hh"
#include "ElementContainerArray.hh"
#include "ElementContainerMatrix.hh"
#include "HeaderBase.hh"
#include "GslHistogram.hh"

//////////////////////////////////
// Sector Average by given Angles on SAS
/////////////////////////////////

//! Function to
/*!
 *
 * import Manyo.SAS as ms
 * sc = ms.SectorAverageByAngle()
 * sc.SetTargetBank( -1 )
 *
 *
 */

class SectorAverageByAngle :
    public OperatorBase< ElementContainerMatrix, ElementContainer >
{
private:

protected:
    std::vector< std::vector<Double> > _SectorAngleInfo; /**<  [index][<azim_min, azim_max, pol_min, pol_max>]  */
    std::vector< std::vector<Double> > _LambdaRangeList; /**<  [bankId][<lamda_min, lamda_max>]  min==max==0.0 for all bank */

    std::vector<Int4> _TargetBankIdList;            /**<  Id list to be used for sector average **/

    bool isUsedOnlySB;                         /**<  If parameter is set on small angle bank mode  */

    std::vector<Int4> _SmallAngleBankIds;           /**<  Id list of banks defined as small angle bank  */
    UInt4 _NumOfBanks;
    std::vector<Int4> _BankIdList;
    std::vector<Int4> _hTableOfBanks;
    std::vector<std::string> _BankNameList;
    std::vector<Double*> _qRangePointers;


    std::vector< std::vector<Double> > _QLambRangeToIq;  /**<  Lambda value and Q range to be used for Iq averaging */
    std::vector< std::vector< std::vector<Double> > > _QLimRangeToIqOfBanks; /**<  Q limitation on a lambda for each Bank [bankId][num_of_limitation][<lambda,Qmin,Qmax>]    */

    std::string _MessageTag;
    void Initialize();
    std::vector<GslHistogram*> *_qInt;
    std::vector<GslHistogram*> *_qErr;
    std::vector<GslHistogram*> *_qCnt;
    void Increment( ElementContainer* ec, Int4 bank_id, UInt4 multh, bool isdebug=false );
    void ClearHist();
    void SetECM( ElementContainerMatrix* ecm );

public:
    SectorAverageByAngle();
        //!< Constructor
        /*!<
         */
    SectorAverageByAngle( ElementContainerMatrix* ecm );
        //!< Constructor
        /*!<
         *   @param ecm ( ElementContainerMatrix )
         */
    ~SectorAverageByAngle();
        //!< Destructor
        /*!<
         */

    void SetTargetBank( Int4 bank_id );
    void SetTargetBank( std::vector<Int4> bank_id_list );
        //!< Sets Bank Id to do sector average and put them into one I(Q)
        /*!< If this is set, average program use only this (these) bank to make one I(Q).
         *   If bank_id is < 0, all banks in data are used separately.
         *   void SetTargetBank( Int4 bank_id );
         *   void SetTargetBank( std::vector<Int4> bank_id_list );
         *
         *   @param bank_id (Int4) Bank Id, when only this bank is used.
         *   @param bank_id_list (std::vector<Int4>) list of Bank Id
         *   @return None
         */
    void AddTargetBank( Int4 bank_id );
        //!< Adds Bank Id to do sector average
        /*!< If this is set, average program use this bank.
         *   If bank_id is < 0, bank list is clear.
         *   void AddTargetBank( Int4 bank_id );
         *
         *   @param bank_id (Int4) Bank Id to be added
         *   @return None
         */
    void AddSectAngle( Double azim_min, Double azim_max, Double pol_min=0.0, Double pol_max=0.0 );
        //!< Adds range of a sector to calculate average.
        /*!< When both azim_min and azim_max are 0.0, all area is used.
         *   When both pol_min and pol_max are 0.0, all range of polar angle are used.
         *   void AddSectAngle( Double azim_min, Double azim_max, Double pol_min, Double pol_max )
         *   void AddSectAngle( Double azim_min, Double azim_max )
         *
         *   @param azim_min (Double) min of range for Azimuth angle
         *   @param azim_max (Double) max of range for Azimuth angle
         *   @param pol_min (Double)  min of range for Polar angle ( possible omitted )
         *   @param pol_min (Double)  max of range for Polar angle ( possible omitted )
         *   @return None
         */
    void SetSectAngles( std::vector<Double> angle_vect );
        //!< Adds list of a sector range to calculate average.
        /*!< angle_vect must be given like below
         *   angle_vect = [ az1_min, az1_max, pol1_min, pol1_max, az2_min, az2_max, ...]
         *   void SetSectAngles( std::vector<Double> angle_vect )
         *
         *   @param angle_vect (std::vector<Double>) std::vector for angles for sector average
         *   @return None
         */
    void ShowSectors();
        //!< Shows list of sector ranges on display
        /*!<
         *   void ShowSectors()
         *
         *   @param None
         *   @return None
         */
    void RemoveSector( Int4 index );
        //!< Remeves a sector from the list of sector ranges
        /*!< index is given automatically at AddSectAngle or SetSectAngles
         *   void RemoveSector( Int4 index )
         *
         *   @param index (Int4)
         *   @return None
         */
    void ClearAllSectors();
        //!< Deletes all list of sector ranges
        /*!<
         *   void ClearAllSectors()
         *
         *   @param None
         *   @return None
         */
    void AddSectByAzimAngleOnSAbank( Double azim_min, Double azim_max, Double r_min=0.0, Double r_max=0.0 );
        //!< Adds range of a sector to calculate average on Small Angle Banks
        /*!< If once this is set, SetTargetBank( _SmallAngleBankIds ) is executed.
         *
         *   void AddSectByAzimAngleOnSAbank( Double azim_min, Double azim_max )
         *
         *   @param azim_min (Double) min of range for Azimuth angle
         *   @param azim_max (Double) max of range for Azimuth angle
         *   @param r_min (Double) min value of length of beam center
         *   @param r_max (Double) max value of length of beam center
         *   @return None
         */
    void SetSectByAzimAngleOnSAbank( std::vector<Double> azim_vect );
        //!< Sets range list for sectors to calculate average on Small Angle Banks
        /*!< If once this is set, SetTargetBank( _SmallAngleBankIds ) is executed.
         *   angle_vect must be given like below
         *   angle_vect = [ az1_min, az1_max, az2_min, az2_max, az3_min, ...]
         *   void AddSectByAzimAngleOnSAbank( Double azim_min, Double azim_max )
         *
         *   @param azim_vect (std::vector<Double>) std::vector of range for Azimuth angle
         *   @return None
         */
    void SetLambdaRange( Double lam_min, Double lam_max, Int4 bank_id=-1 );
        //!< Sets range of lambda to be used on averaging
        /*!<
         *   void SetLambdaRange( Double lam_min, Double lam_max, Int4 bank_id=-1 )
         *
         *   @param lam_min (Double) min value of lambda range
         *   @param lam_max (Double) max value of lambda range
         *   @return None
         */
    void SetQRange( UInt4 BinType, Double Qmin, Double Qmax, Double deltaQ, Int4 bank_id=-1 );
    void SetQRange( UInt4 BinType, std::vector<Double> qinfo, Int4 bank_id=-1 );
    void SetQRange( std::vector<Double> qlist, Int4 bank_id=-1 );
        //!< Sets Q-range for each bank
        /*!<
         *   void SetQRange( Double Qmin, Double Qmax, Double deltaQ, Int4 bank_id=-1 )
         *
         *   @param BinType (UInt4) 0..deltaQ const, 1..deltaQ over Q const
         *   @param Qmin    (Double) min value of Q range
         *   @param Qmax    (Double) max value of Qrange
         *   @param deltaQ  (Double) width value of Qrange
         *   @param qinfo   (std::vector<Double>) Q range setting [Q0, Q01width, Q1, Q12width, Q2, Q23with, Q3, ...]
         *   @param qlist   (std::vector<Double>) Q range list    [Q0, Q1, Q2, ... ]
         *   @return None
         */
    void _MakeQHistogram( std::vector<Double> Qrange, Int4 bank_id );
    bool Execute();
        //!< Excecutes sector average
        /*!<
         *   bool Execute();
         *
         *   @param None
         *   @retval true
         *   @retval false
         */
    ElementContainer PutIq( Int4 bank_id=-1 );
        //!< Puts sector average of a bank
        /*!<
         *   ElementContainer PutIq( Int4 bank_id=-1 )
         *
         *   @param bank_id (Int4) if -1, return PutAverage()
         *   @return ElementContaienr
         */
    UInt4 PutNumOfIq(){ return _NumOfBanks;}
        //!< Puts the number of banks
        /*!<
         *   UInt4 PutNumOfIq()
         *
         *   @param None
         *   @return UInt4
         */
    ElementContainer PutAverage();
        //!< Puts sector average of all banks
        /*!< This requires that Q range in all bank are same.
         *   ElementContainer PutAverage();
         *
         *   @param None
         *   @return ElementContaienr
         */
    void ClearQLambdaRangeToIq();
    void AddQLambdaRangeToIq( Double lambda, Double Qmin, Double Qmax );
    void ShowQLambdaRangeToIq();
    bool isInQLambdaToIq( Double lamb, Double q_val );
    std::vector<Double> _getLinearApproximationQLamb( Double lamb, UInt4 index );

    void AddQLambdaRangeToIq( UInt4 bankId, Double lambda, Double Qmin, Double Qmax );
    bool isInQLambdaToIq( UInt4 bankId, Double lamb, Double q_val );
    std::vector<Double> _getLinearApproximationQLamb( UInt4 bankId, Double lamb, UInt4 index );
    void ClearQLimRangeToIqOfBanks(UInt4 bankId);
    void ClearQLimRangeToIqOfBanks();
};
#endif
