#include "AdvModelLayer.hh"
#include "AdvModelParam.hh"
#include "AdvModelParamSet.hh"

#include <gsl/gsl_complex_math.h>

AdvModelLayer::AdvModelLayer(AdvModel* mdl) {
    m_mdl = mdl;

    AdvModelParamSet* params = m_mdl->GetParamSet();

    m_width    = params->NewParam("layer_width");
    m_SLD_real = params->NewParam("real_part_of_SLD");
    m_SLD_imag = params->NewParam("imaginary_part_of_SLD");
    m_SLD_mag_real = params->NewParam("real_part_of_magnetic_SLD");
    m_SLD_mag_imag = params->NewParam("imaginary_part_of_magnetic_SLD");
    m_angle    = params->NewParam();

//    m_width->SetVal(0.0);
//    m_SLD_real->SetVal(0.0);
//    m_SLD_imag->SetVal(0.0);
//    m_SLD_mag_real->SetVal(0.0);
//    m_SLD_mag_imag->SetVal(0.0);
//    m_angle->SetVal(0.0);

    m_SLD_real->SetScale(1.0e-7);
    m_SLD_imag->SetScale(1.0e-7);
    m_SLD_mag_real->SetScale(1.0e-7);
    m_SLD_mag_imag->SetScale(1.0e-7);
}

AdvModelLayer::~AdvModelLayer() {
    AdvModelParamSet* params = m_mdl->GetParamSet();

    params->DeleteParam(m_width);
    params->DeleteParam(m_SLD_real);
    params->DeleteParam(m_SLD_imag);
    params->DeleteParam(m_SLD_mag_real);
    params->DeleteParam(m_SLD_mag_imag);
    params->DeleteParam(m_angle);
}

void AdvModelLayer::SetWidth(Double v) {
    m_width->SetVal(v);
}

Double AdvModelLayer::GetWidth() {
    return m_width->GetVal();
}

void AdvModelLayer::SetSLDreal(Double v) {
    m_SLD_real->SetVal(v);
}

void AdvModelLayer::SetSLDimag(Double v) {
    m_SLD_imag->SetVal(v);
}

Double AdvModelLayer::GetSLDreal() {
    return m_SLD_real->GetVal();
}

Double AdvModelLayer::GetSLDimag() {
    return m_SLD_imag->GetVal();
}

Complex AdvModelLayer::GetSLD() {
    return Complex(GetSLDreal(), GetSLDimag());
}

void AdvModelLayer::SetSLD_mag_real(Double v) {
    m_SLD_mag_real->SetVal(v);
}

void AdvModelLayer::SetSLD_mag_imag(Double v) {
    m_SLD_mag_imag->SetVal(v);
}

Double AdvModelLayer::GetSLD_mag_real() {
    return m_SLD_mag_real->GetVal();
}

Double AdvModelLayer::GetSLD_mag_imag() {
    return m_SLD_mag_imag->GetVal();
}

Complex AdvModelLayer::GetSLD_mag() {
    return Complex(GetSLD_mag_real(), GetSLD_mag_imag());
}

void AdvModelLayer::SetAngle(Double v) {
    m_angle->SetVal(v);
}

Double AdvModelLayer::GetAngle() {
    return m_angle->GetVal();
}

void AdvModelLayer::GetWavenumber(Double kz, Complex& kplus, Complex& kminus) {

    //  散乱長密度 b の係数 4π
    Complex bdash = 4.0 * PI * GetSLD();

    //  散乱長密度 b の係数 4π
    Complex bmag = 4.0 * PI * GetSLD_mag();

    //  波数 (複素数)　単位(Å^(-1))
    kplus  = sqrt( kz * kz - (bdash + bmag) );
    kminus = sqrt( kz * kz - (bdash - bmag) );
}

void AdvModelLayer::GetMatrix(Double kz, gsl_matrix_complex *m) {

    std::vector<std::vector<Complex> > mat;
    mat.resize(4);
    mat[0].resize(4);
    mat[1].resize(4);
    mat[2].resize(4);
    mat[3].resize(4);

    Complex a;
    Complex b;
    GetWavenumber(kz, a, b);

    Double dz = GetWidth();
    Complex cos_az = cos(a * dz);
    Complex cos_bz = cos(b * dz);
    Complex sin_az = sin(a * dz);
    Complex sin_bz = sin(b * dz);

    Double angle = GetAngle() * PI / 180.0; // degree -> radian

    //  中性子スピンに垂直な磁束密度成分が存在し、
    //  2つのスピン状態が混じる。
    Double c = (1.0 - cos(angle)) * 0.5;
    Double d = (1.0 + cos(angle)) * 0.5;
    Double e = sin(angle) * 0.5;

    mat[0][0] = d * cos_az + c * cos_bz;
    mat[0][1] = d / a * sin_az + c / b * sin_bz;
    mat[0][2] = -e * (cos_az - cos_bz);
    mat[0][3] = -e * (1.0 / a * sin_az - 1.0 / b * sin_bz);

    mat[1][0] = -a * d * sin_az - b * c * sin_bz;
    mat[1][1] = mat[0][0]; // = -d * cos_az + c * cos_bz;
    mat[1][2] = e * (a * sin_az - b * sin_bz);
    mat[1][3] = mat[0][2]; // = cos_az - cos_bz;

    mat[2][0] = mat[0][2]; // = cos_az - cos_bz;
    mat[2][1] = mat[0][3]; // = 1.0 / a * sin_az - 1.0 / b * sin_bz;
    mat[2][2] = c * cos_az + d * cos_bz;
    mat[2][3] = c / a * sin_az + d / b * sin_bz;

    mat[3][0] = mat[1][2]; // -a * sin_az + b * sin_bz;
    mat[3][1] = mat[0][2]; // = cos_az - cos_bz;
    mat[3][2] = -a * c * sin_az - b * d * sin_bz;
    mat[3][3] = mat[2][2]; // = c * cos_az - d * cos_bz;

    //  gsl_matrix 型へ代入
    for (Int4 i = 0; i < 4; i++) {
        for (Int4 j = 0; j < 4; j++) {
            gsl_matrix_complex_set(m, i, j, Complex2gsl(mat[i][j]));
        }
    }
}

gsl_complex AdvModelLayer::Complex2gsl(Complex c) {
    gsl_complex gc;
    GSL_SET_COMPLEX(&gc, c.real(), c.imag());
    return gc;
}

