#include "AdvModelSmallAngle.hh"
#include "AdvModelParam.hh"
#include "AdvModelParamSet.hh"
#include "AdvModelParticle.hh"

#include <cfloat>

/** default constructor */
AdvModelSmallAngle::AdvModelSmallAngle() {
    this->set("AdvModelSmallAngle", "smallangle", 0);
    efactor = m_paramset->NewParam();
    bkg = m_paramset->NewParam();

    efactor->SetVal(1.0);
    bkg->SetVal(0.0);

    efactor->SetScale(0.01);
    bkg->SetScale(1.0e-5);

    ifunc = USE_PARTICLE;
    istruct = NONE;
}

/** destructor */
AdvModelSmallAngle::~AdvModelSmallAngle() {
}

void AdvModelSmallAngle::SetFitParam(const Double *p) {
    m_paramset->SetParamValues ( p );
}

#ifdef USE_POINTER
/** evaluate the value of the function */
Double AdvModelSmallAngle::eval(const Double x, const Double *p) {
    Double d;
    m_paramset->SetParamValues ( p );
//    d = CalcIq ( x );
    return d;
}

#ifdef HAVE_DIFFERENTIAL_MEMBER
/** evaluate the value of the 1st derivative function */
Double AdvModelSmallAngle::der1st(const Double x, const Double *p) {
    std::cout << "AdvModelSmallAngle::der1st is not implemented" << std::endl;
    return DBL_MAX;
}

/** evaluate the value of the 2nd derivative function */
Double AdvModelSmallAngle::der2nd(const Double x, const Double *p) {
    std::cout << "AdvModelSmallAngle::der2nd is not implemented" << std::endl;
    return DBL_MAX;
}

/** evaluate the gradient of the function for parameters */
Double * AdvModelSmallAngle::gradient(const Double x, const Double *p) {
    std::cout << "AdvModelSmallAngle::gradient is not implemented" << std::endl;
    return NULL;
}

#endif //HAVE_DIFFERENTIAL_MEMBER
#endif //USE_POINTER

#ifdef USE_VECTOR
/** evaluate the value of the function */
Double AdvModelSmallAngle::eval(const Double x, const std::vector<Double> &p) {
    Double d = 0.0;
    m_paramset->SetParamValues ( p );
    // d = CalcIq ( x );
    return d;
}

#ifdef HAVE_DIFFERENTIAL_MEMBER
/** evaluate the value of the 1st derivative function */
Double AdvModelSmallAngle::der1st(const Double x, const std::vector<Double> &p) {
    std::cout << "AdvModelSmallAngle::der1st is not implemented" << std::endl;
    return DBL_MAX;
}

/** evaluate the value of the 2nd derivative function */
Double AdvModelSmallAngle::der2nd(const Double x, const std::vector<Double> &p) {
    std::cout << "AdvModelSmallAngle::der2nd is not implemented" << std::endl;
    return DBL_MAX;
}

/** evaluate the gradient of the function for parameters */
std::vector<Double> AdvModelSmallAngle::gradient(const Double x, const std::vector<Double> &p) {
    std::cout << "AdvModelSmallAngle::gradient is not implemented" << std::endl;
    std::vector<Double> v;
    v.clear();
    return v;
}

#endif //HAVE_DIFFERENTIAL_MEMBER
#endif //USE_VECTOR

void AdvModelSmallAngle::SetFuncParam(const char* fname) {

    // Read XML file
    FILE *fp;
    mxml_node_t *tree;
    fp = fopen(fname, "r");
    if (!fp) {
        std::printf("file open error %s\n", fname);
        return;
    }
#if (MXML_MAJOR_VERSION < 4)  // mxml version
    tree = mxmlLoadFile(NULL, fp, MXML_TEXT_CALLBACK);
#else
    tree = mxmlLoadFile(NULL, NULL, fp);
#endif
    fclose(fp);

    mxml_node_t *model_top;
    model_top = mxmlFindElement(tree, tree, "SimulationModel", "type", "smallangle", MXML_DESCEND);
    if (!model_top) {
        std::cout << fname << " is not adequate XML file." << std::endl;
        return;
    }

    mxml_node_t *func;
    const char* cp;
    func = mxmlFindElement(model_top, model_top, "Function", NULL, NULL, MXML_DESCEND);
    ifunc = USE_PARTICLE;
    if (func) {
      cp = mxmlElementGetAttr(func, "type");
      if (cp) ifunc = (Function) atoi(cp);
    }

    mxml_node_t *parts;
    parts = mxmlFindElement( model_top, model_top, "Particles", NULL, NULL, MXML_DESCEND);

    if (!parts && ifunc==USE_PARTICLE) {
        std::cout << "Cannot find [Particles] in XML." << std::endl;
        particles.resize(0);
        return;
    }

    //  clear particles
    if (!particles.empty()) {
        for (Int4 i = 0; i < (Int4)particles.size(); i++) {
            if (particles[i]) delete (particles[i]);
        }
        particles.resize(0);
    }

    if (parts) {
        cp = mxmlElementGetAttr(parts, "TotalNumber");
        if (!cp) {
            std::cout << "TotalNumber of Particles is not given." << std::endl;
            return;
        }
        Int4 nparticle = atoi(cp);

        if (nparticle < 0) {
            std::cout << "Negative particle number is given." << std::endl;
            return;
        }

        particles.resize(nparticle, NULL);
    }

    char id[10];
    mxml_node_t *node1;
    for (size_t i = 0; i < particles.size(); i++) {
        std::snprintf(id, sizeof(id), "%d", (Int4) i);
        node1 = mxmlFindElement(parts, parts, "Particle", "id", id, MXML_DESCEND);
        if (node1 == NULL) {
            std::cout << "Cannot find [Particle id=" << id << "] in XML." << std::endl;
            return;
        }
        cp = mxmlElementGetAttr(node1, "type");
        int nid = 0;
        if (cp)  nid = atoi(cp);
        particles[i] = CreateParticle ( this, (ParticleType) nid );
        if (particles[i])  particles[i] -> SetFuncParam(node1);
    }

    mxml_node_t *structure;
    structure = mxmlFindElement( model_top, model_top, "Interparticle", NULL, NULL, MXML_DESCEND);

    if (structure) {
        cp = mxmlElementGetAttr(structure, "type");
        if (!cp) {
            std::cout << "TotalNumber of Particles is not given." << std::endl;
            return;
        }
        istruct = (InterParticleType) atoi(cp);
    }
}
void AdvModelSmallAngle::GetFuncParam(const char* fname) {
}
std::vector<Double> AdvModelSmallAngle::GetLowerVals() {
  std::vector<Double> p;
  return p;
}
std::vector<Double> AdvModelSmallAngle::GetParamVals() {
  std::vector<Double> p;
  return p;
}
std::vector<Double> AdvModelSmallAngle::GetUpperVals() {
  std::vector<Double> p;
  return p;
}

