#ifndef SPECULAR_H
#define SPECULAR_H

#include "Header.hh"
#include <gsl/gsl_complex.h>
#include "AdvModel.hh"
#include "AdvFuncBase.hh"
#include "AdvParamSet.hh"
#include "ElementContainer.hh"

#ifndef LIBMXML4
#include <mxml.h>
#else
#include <libmxml4/mxml.h>
#define MXML_DESCEND MXML_DESCEND_ALL
#endif

#include <gsl/gsl_blas.h>
#include <gsl/gsl_complex_math.h>
#include <gsl/gsl_linalg.h>

class AdvModelLayer;
class AdvModelParam;

/**
 *  specular reflection
 */
class AdvModelSpecular : public AdvModel, public AdvFuncBase {

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

        /** destructor */
        ~AdvModelSpecular() ;


#ifdef USE_POINTER
        /** evaluate the value of the function */
        Double eval(const Double x, const Double *p) ;
#ifdef HAVE_DIFFERENTIAL_MEMBER
        /** evaluate the value of the 1st derivative function */
        Double der1st(const Double x, const Double *p) ;
        /** evaluate the value of the 2nd derivative function */
        Double der2nd(const Double x, const Double *p) ;
        /** evaluate the gradient of the function for parameters */
        Double *gradient(const Double x, const Double *p);
#endif //HAVE_DIFFERENTIAL_MEMBER
#endif //USE_POINTER

#ifdef USE_VECTOR
        /** evaluate the value of the function */
        Double eval(const Double x, const std::vector<Double> &p);
#ifdef HAVE_DIFFERENTIAL_MEMBER
        /** evaluate the value of the 1st derivative function */
        Double der1st(const Double x, const std::vector<Double> &p);
        /** evaluate the value of the 2nd derivative function */
        Double der2nd(const Double x, const std::vector<Double> &p);
        /** evaluate the gradient of the function for parameters */
        std::vector<Double> gradient(const Double x, const std::vector<Double> &p);
#endif //HAVE_DIFFERENTIAL_MEMBER
#endif //USE_VECTOR

        void SetFitParam (const Double* p);
        void SetFitParam (const std::vector<Double> p);


        /** 関数パラメータの設定 */
        void SetFuncParam ( const char* fname );
//        void SetFuncParam ( ParamSet& prm );

        /** 現在の関数パラメータの取得 */
        void GetFuncParam ( const char* fname );
//        void GetFuncParam ( ParamSet& prm );

        /** 反射率計算 */
        Double         ReflectAt ( const Double Q );
        std::vector<Double> ReflectAt ( const Double Q, Int4 flag );

        /** 反射率計算 計算対象q値をElementContainer einから */
        void CalcReflection ( ElementContainer& ein, ElementContainer& eout );
        void CalcReflection ( ElementContainer& , ElementContainer& , ElementContainer& );
        void CalcReflection ( ElementContainer& , ElementContainer& , ElementContainer& , ElementContainer& );

//        void CalcReflection ( ElementContainer& e );

        /** 反射率計算 偏極*/
//        void CalcReflection ( ElementContainer& e1, ElementContainer& e2, ElementContainer& e3 );

        std::vector<Double> GetParamVals();
        std::vector<Double> GetUpperVals();
        std::vector<Double> GetLowerVals();

        void toLogScale ( ElementContainer& el );

    enum mode {
        UnPol2UnPol,
        UpPol2UpPol,
        UpPol2DownPol,
        UpPol2UnPol,
        DownPol2DownPol,
        DownPol2UpPol,
        DownPol2UnPol,
    };

    private:
        /**  測定データの縦軸のスケール因子 */
        AdvModelParam*  efactor;

        /**  バックグラウンドノイズ Q依存性のない一定値 */
        AdvModelParam*  bkg;

        /**  ガウス関数を用いた畳み込み(平滑化)を行う場合の(正規分布の)分散 ⊿Q/Q */
        Double  deltaQ;

        /**  層構造 */
        std::vector<AdvModelLayer*> m_layers;

        void CheckParams();

        Bool m_change_param;

        enum mode  m_mode;

        Int4  logFlag;

        Complex gsl2Complex(gsl_complex c);
        gsl_complex Complex2gsl(Complex c);

        void CalcReflectionAt(Double kz, Double r[], Double t[]);
        // void SetParameterAttr( mxml_node_t *node, ModelParam *param ); 
        // void GetParameterAttr( mxml_node_t *node, ModelParam *param );

private:
        void DebugPrintMatrix ( gsl_matrix_complex *mb ) ;
};

#endif
