//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Mini/MinimizerItems.cpp
//! @brief     Implements class MinimizerItem and children.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#include "GUI/Model/Mini/MinimizerItems.h"
#include "Base/Util/Assert.h"
#include "Fit/Kernel/MinimizerFactory.h"
#include "Fit/Suite/GSLLevenbergMarquardtMinimizer.h"
#include "Fit/Suite/GSLMultiMinimizer.h"
#include "Fit/Suite/GeneticMinimizer.h"
#include "Fit/Suite/Minuit2Minimizer.h"
#include "Fit/Suite/SimAnMinimizer.h"
#include "GUI/Model/Util/String.h"
#include "GUI/Model/Util/UtilXML.h"
#include "Sim/Fitting/ObjectiveMetric.h"
#include "Sim/Fitting/ObjectiveMetricUtil.h"
#include <QMap>

namespace {

namespace Tag {

const QString Tolerance("Tolerance");
const QString MaxIterations("MaxIterations");
const QString IterationsAtTemp("IterationsAtTemp");
const QString StepSize("StepSize");
const QString BoltzmannK("BoltzmannK");
const QString Tinit("Tinit");
const QString BoltzmannMu("BoltzmannMu");
const QString Tmin("Tmin");
const QString PopulationSize("PopulationSize");
const QString RandomSeed("RandomSeed");
const QString Algorithm("Algorithm");
const QString Strategy("Strategy");
const QString ErrorDef("ErrorDef");
const QString Precision("Precision");
const QString MaxFuncCalls("MaxFuncCalls");
const QString Minimizer("Minimizer");
const QString Metric("Metric");
const QString Norm("Norm");
const QString MinuitMinimizer("MinuitMinimizer");
const QString GSLMultiMinimizer("GSLMultiMinimizer");
const QString GeneticMinimizer("GeneticMinimizer");
const QString SimAnMinimizer("SimAnMinimizer");
const QString GSLLMAMinimizer("GSLLMAMinimizer");

} // namespace Tag

const QMap<QString, MinimizerType> minimizer_names_map = {
    {QString::fromStdString(MinimizerInfo::buildMinuit2Info().name()), MinimizerType::Minuit2},
    {QString::fromStdString(MinimizerInfo::buildGSLMultiMinInfo().name()),
     MinimizerType::GSLMultiMin},
    {QString::fromStdString(MinimizerInfo::buildGSLLMAInfo().name()), MinimizerType::GSLLMA},
    {QString::fromStdString(MinimizerInfo::buildGSLSimAnInfo().name()), MinimizerType::GSLSimAn},
    {QString::fromStdString(MinimizerInfo::buildGeneticInfo().name()), MinimizerType::Genetic}};

} // namespace

QString minimizerTypeToName(MinimizerType type)
{
    ASSERT(minimizer_names_map.values().contains(type));
    return minimizer_names_map.key(type);
}

namespace {

QMap<QString, QString> algorithm_minimizer_map; // TODO make this a class member

//! Returns list of algorithm names defined for given minimizer.
QStringList algorithmNames(const QString& minimizerType)
{
    std::vector<std::string> algos = MinimizerFactory::algorithmNames(minimizerType.toStdString());
    return GUI::Util::String::fromStdStrings(algos);
}

//! Returns list of algoritm descriptions defined for given minimizer.
QStringList algorithmDescriptions(const QString& minimizerType)
{
    std::vector<std::string> descr =
        MinimizerFactory::algorithmDescriptions(minimizerType.toStdString());
    return GUI::Util::String::fromStdStrings(descr);
}

//! Returns ComboProperty representing list of algorithms defined for given minimizerType.
ComboProperty algorithmCombo(const QString& minimizerType)
{
    auto result = ComboProperty::fromList(algorithmNames(minimizerType));
    result.setToolTips(algorithmDescriptions(minimizerType));
    return result;
}

QString add_algorithm_from_minimizer_to_list_and_map(MinimizerType minimizer_type,
                                                     QStringList& common_algorithms_list,
                                                     QStringList& common_algorithms_descriptions)
{
    QString minimizer_name = minimizerTypeToName(minimizer_type);
    QStringList algorithms = algorithmNames(minimizer_name);
    QStringList descriptions = algorithmDescriptions(minimizer_name);
    ASSERT(algorithms.size() == descriptions.size());

    for (int i = 0; i < algorithms.size(); i++) {
        common_algorithms_list.append(algorithms[i]);
        common_algorithms_descriptions.append(descriptions[i]);
        algorithm_minimizer_map.insert(algorithms[i], minimizer_name);
    }
    ASSERT(!algorithms.empty());
    return algorithms.first();
}

void create_algorithm_list_and_map(QString& default_common_algorithm, QStringList& algorithms_list,
                                   QStringList& algorithms_descriptions)
{
    // Group headers may not directly correspond to minimizers, so for them we don't use
    // descriptions from MinimizerInfo

    algorithms_list.clear();
    algorithms_descriptions.clear();

    // group 1
    // group header. Cannot be selected and have no mapped minimizer
    algorithms_list.append("Local optimization");
    algorithms_descriptions.append("");

    add_algorithm_from_minimizer_to_list_and_map(MinimizerType::Minuit2, algorithms_list,
                                                 algorithms_descriptions);

    add_algorithm_from_minimizer_to_list_and_map(MinimizerType::GSLMultiMin, algorithms_list,
                                                 algorithms_descriptions);

    add_algorithm_from_minimizer_to_list_and_map(MinimizerType::GSLLMA, algorithms_list,
                                                 algorithms_descriptions);

    // group 2
    algorithms_list.append("Global optimization");
    algorithms_descriptions.append("");

    // move "Scan" algorithm from the local group to the global
    qsizetype scan_index = algorithms_list.indexOf("Scan");
    ASSERT(scan_index >= 0);
    QString name = algorithms_list.takeAt(scan_index);
    QString descr = algorithms_descriptions.takeAt(scan_index);
    algorithms_list.append(name);
    algorithms_descriptions.append(descr);

    // other algorithms
    // choose genetic algorithm as defaut
    default_common_algorithm = add_algorithm_from_minimizer_to_list_and_map(
        MinimizerType::Genetic, algorithms_list, algorithms_descriptions);

    add_algorithm_from_minimizer_to_list_and_map(MinimizerType::GSLSimAn, algorithms_list,
                                                 algorithms_descriptions);
}

} // namespace


MinimizerContainerItem::MinimizerContainerItem()
    : m_MinuitMinimizer(std::make_unique<MinuitMinimizerItem>())
    , m_GSLMultiMinimizer(std::make_unique<GSLMultiMinimizerItem>())
    , m_GeneticMinimizer(std::make_unique<GeneticMinimizerItem>())
    , m_SimAnMinimizer(std::make_unique<SimAnMinimizerItem>())
    , m_GSLLMAMinimizer(std::make_unique<GSLLMAMinimizerItem>())
{
    QString default_common_algorithm;
    QStringList common_algorithms_list;
    QStringList common_algorithms_descriptions;
    QString default_minimizer = minimizerTypeToName(MinimizerType::Genetic);
    create_algorithm_list_and_map(default_common_algorithm, common_algorithms_list,
                                  common_algorithms_descriptions);
    m_algorithm = ComboProperty::fromList(common_algorithms_list);
    m_algorithm.setToolTips(common_algorithms_descriptions);
    m_algorithm.setCurrentValue(default_common_algorithm);

    m_minimizer = ComboProperty::fromList(minimizer_names_map.keys());
    m_minimizer.setCurrentValue(default_minimizer);

    m_metric = ComboProperty::fromStdVec(ObjectiveMetricUtil::metricNames());
    m_metric.setCurrentValue(QString::fromStdString(ObjectiveMetricUtil::defaultMetricName()));

    m_norm = ComboProperty::fromStdVec(ObjectiveMetricUtil::normNames());
    m_norm.setCurrentValue(QString::fromStdString(ObjectiveMetricUtil::defaultNormName()));
}

MinimizerContainerItem::~MinimizerContainerItem() = default;

MinuitMinimizerItem* MinimizerContainerItem::minimizerItemMinuit() const
{
    return m_MinuitMinimizer.get();
}

GSLMultiMinimizerItem* MinimizerContainerItem::minimizerItemGSLMulti() const
{
    return m_GSLMultiMinimizer.get();
}

GeneticMinimizerItem* MinimizerContainerItem::minimizerItemGenetic() const
{
    return m_GeneticMinimizer.get();
}

SimAnMinimizerItem* MinimizerContainerItem::minimizerItemSimAn() const
{
    return m_SimAnMinimizer.get();
}

GSLLMAMinimizerItem* MinimizerContainerItem::minimizerItemGSLLMA() const
{
    return m_GSLLMAMinimizer.get();
}

MinimizerItem* MinimizerContainerItem::currentMinimizerItem() const
{
    if (currentMinimizer() == minimizerTypeToName(MinimizerType::Minuit2))
        return minimizerItemMinuit();

    else if (currentMinimizer() == minimizerTypeToName(MinimizerType::GSLMultiMin))
        return minimizerItemGSLMulti();

    else if (currentMinimizer() == minimizerTypeToName(MinimizerType::Genetic))
        return minimizerItemGenetic();

    else if (currentMinimizer() == minimizerTypeToName(MinimizerType::GSLSimAn))
        return minimizerItemSimAn();

    else if (currentMinimizer() == minimizerTypeToName(MinimizerType::GSLLMA))
        return minimizerItemGSLLMA();
    else
        ASSERT_NEVER;

    return nullptr;
}

QString MinimizerContainerItem::currentMinimizer() const
{
    return m_minimizer.currentValue();
}

void MinimizerContainerItem::setCurrentMinimizer(const QString& name)
{
    m_minimizer.setCurrentValue(name);
}

bool MinimizerContainerItem::algorithmHasMinimizer(const QString& name)
{
    return algorithm_minimizer_map.contains(name);
}

void MinimizerContainerItem::setCurrentCommonAlgorithm(const QString& name)
{
    m_algorithm.setCurrentValue(name);

    ASSERT(algorithmHasMinimizer(name));
    setCurrentMinimizer(algorithm_minimizer_map.value(name));
    applyAlgorithmToMinimizer(name);
}
void MinimizerContainerItem::applyAlgorithmToMinimizer(const QString& name)
{
    // Minuit2
    if (currentMinimizer() == minimizerTypeToName(MinimizerType::Minuit2))
        minimizerItemMinuit()->setCurrentAlgorithm(name);

    // GSL MultiMin
    if (currentMinimizer() == minimizerTypeToName(MinimizerType::GSLMultiMin))
        minimizerItemGSLMulti()->setCurrentAlgorithm(name);

    // TMVA Genetic
    // do nothing

    // GSL Simulated Annealing
    // do nothing

    // GSL Levenberg-Marquardt
    // do nothing
}

QString MinimizerContainerItem::currentObjectiveMetric() const
{
    return m_metric.currentValue();
}

void MinimizerContainerItem::setCurrentObjectiveMetric(const QString& name)
{
    m_metric.setCurrentValue(name);
}
QString MinimizerContainerItem::currentNormFunction() const
{
    return m_norm.currentValue();
}

void MinimizerContainerItem::setCurrentNormFunction(const QString& name)
{
    m_norm.setCurrentValue(name);
}
std::unique_ptr<IMinimizer> MinimizerContainerItem::createMinimizer() const
{
    return currentMinimizerItem()->createMinimizer();
}

std::unique_ptr<ObjectiveMetric> MinimizerContainerItem::createMetric() const
{
    return ObjectiveMetricUtil::createMetric(currentObjectiveMetric().toStdString(),
                                             currentNormFunction().toStdString());
}

void MinimizerContainerItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeTaggedElement(w, Tag::Algorithm, m_algorithm);
    XML::writeTaggedElement(w, Tag::Minimizer, m_minimizer);
    XML::writeTaggedElement(w, Tag::Metric, m_metric);
    XML::writeTaggedElement(w, Tag::Norm, m_norm);
    XML::writeTaggedElement(w, Tag::MinuitMinimizer, *m_MinuitMinimizer);
    XML::writeTaggedElement(w, Tag::GSLMultiMinimizer, *m_GSLMultiMinimizer);
    XML::writeTaggedElement(w, Tag::GeneticMinimizer, *m_GeneticMinimizer);
    XML::writeTaggedElement(w, Tag::SimAnMinimizer, *m_SimAnMinimizer);
    XML::writeTaggedElement(w, Tag::GSLLMAMinimizer, *m_GSLLMAMinimizer);
}

void MinimizerContainerItem::readFrom(QXmlStreamReader* r)
{
    while (r->readNextStartElement()) {
        QString tag = r->name().toString();
        if (tag == Tag::Algorithm)
            XML::readTaggedElement(r, tag, m_algorithm);
        else if (tag == Tag::Minimizer)
            XML::readTaggedElement(r, tag, m_minimizer);
        else if (tag == Tag::Metric)
            XML::readTaggedElement(r, tag, m_metric);
        else if (tag == Tag::Norm)
            XML::readTaggedElement(r, tag, m_norm);
        else if (tag == Tag::MinuitMinimizer)
            XML::readTaggedElement(r, tag, *m_MinuitMinimizer);
        else if (tag == Tag::GSLMultiMinimizer)
            XML::readTaggedElement(r, tag, *m_GSLMultiMinimizer);
        else if (tag == Tag::GeneticMinimizer)
            XML::readTaggedElement(r, tag, *m_GeneticMinimizer);
        else if (tag == Tag::SimAnMinimizer)
            XML::readTaggedElement(r, tag, *m_SimAnMinimizer);
        else if (tag == Tag::GSLLMAMinimizer)
            XML::readTaggedElement(r, tag, *m_GSLLMAMinimizer);
        else
            r->skipCurrentElement();
    }
}

// ----------------------------------------------------------------------------

MinuitMinimizerItem::MinuitMinimizerItem()
    : MinimizerItem()
    , m_strategy(1)
    , m_error_def(1.0)
    , m_tolerance(0.01)
    , m_precision(-1.0)
    , m_max_func_calls(0)
{
    QString minimizer_name = minimizerTypeToName(MinimizerType::Minuit2);
    m_algorithm = ::algorithmCombo(minimizer_name);
}

MinuitMinimizerItem::~MinuitMinimizerItem() = default;

QString MinuitMinimizerItem::currentAlgorithm() const
{
    return m_algorithm.currentValue();
}

void MinuitMinimizerItem::setCurrentAlgorithm(const QString& name)
{
    m_algorithm.setCurrentValue(name);
}
void MinuitMinimizerItem::setStrategy(int value)
{
    m_strategy = value;
}
void MinuitMinimizerItem::setErrorDefinition(double value)
{
    m_error_def = value;
}
void MinuitMinimizerItem::setTolerance(double value)
{
    m_tolerance = value;
}
void MinuitMinimizerItem::setPrecision(double value)
{
    m_precision = value;
}
void MinuitMinimizerItem::setMaxFuncCalls(int value)
{
    m_max_func_calls = value;
}

std::unique_ptr<IMinimizer> MinuitMinimizerItem::createMinimizer() const
{
    auto* domainMinimizer = new Minuit2Minimizer(currentAlgorithm().toStdString());
    domainMinimizer->setStrategy(strategy());
    domainMinimizer->setErrorDefinition(errorDefinition());
    domainMinimizer->setTolerance(tolerance());
    domainMinimizer->setPrecision(precision());
    domainMinimizer->setMaxFunctionCalls(maxFuncCalls());

    return std::unique_ptr<IMinimizer>(domainMinimizer);
}

void MinuitMinimizerItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeTaggedElement(w, Tag::Algorithm, m_algorithm);
    XML::writeTaggedValue(w, Tag::Strategy, m_strategy);
    XML::writeTaggedValue(w, Tag::ErrorDef, m_error_def);
    XML::writeTaggedValue(w, Tag::Tolerance, m_tolerance);
    XML::writeTaggedValue(w, Tag::Precision, m_precision);
    XML::writeTaggedValue(w, Tag::MaxFuncCalls, m_max_func_calls);
}

void MinuitMinimizerItem::readFrom(QXmlStreamReader* r)
{
    while (r->readNextStartElement()) {
        QString tag = r->name().toString();
        if (tag == Tag::Algorithm)
            XML::readTaggedElement(r, tag, m_algorithm);
        else if (tag == Tag::Strategy)
            m_strategy = XML::readTaggedInt(r, tag);
        else if (tag == Tag::ErrorDef)
            m_error_def = XML::readTaggedDouble(r, tag);
        else if (tag == Tag::Tolerance)
            m_tolerance = XML::readTaggedDouble(r, tag);
        else if (tag == Tag::Precision)
            m_precision = XML::readTaggedDouble(r, tag);
        else if (tag == Tag::MaxFuncCalls)
            m_max_func_calls = XML::readTaggedInt(r, tag);
        else
            r->skipCurrentElement();
    }
}

// ----------------------------------------------------------------------------

GSLMultiMinimizerItem::GSLMultiMinimizerItem()
    : MinimizerItem()
    , m_max_iterations(0)
{
    QString minimizer_name = minimizerTypeToName(MinimizerType::GSLMultiMin);
    m_algorithm = ::algorithmCombo(minimizer_name);
}

GSLMultiMinimizerItem::~GSLMultiMinimizerItem() = default;

QString GSLMultiMinimizerItem::currentAlgorithm() const
{
    return m_algorithm.currentValue();
}

void GSLMultiMinimizerItem::setCurrentAlgorithm(const QString& name)
{
    m_algorithm.setCurrentValue(name);
}
void GSLMultiMinimizerItem::setMaxIterations(int value)
{
    m_max_iterations = value;
}

std::unique_ptr<IMinimizer> GSLMultiMinimizerItem::createMinimizer() const
{
    auto* domainMinimizer = new GSLMultiMinimizer(currentAlgorithm().toStdString());
    domainMinimizer->setMaxIterations(maxIterations());
    return std::unique_ptr<IMinimizer>(domainMinimizer);
}

void GSLMultiMinimizerItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeTaggedElement(w, Tag::Algorithm, m_algorithm);
    XML::writeTaggedValue(w, Tag::MaxIterations, m_max_iterations);
}

void GSLMultiMinimizerItem::readFrom(QXmlStreamReader* r)
{
    while (r->readNextStartElement()) {
        QString tag = r->name().toString();
        if (tag == Tag::Algorithm)
            XML::readTaggedElement(r, tag, m_algorithm);
        else if (tag == Tag::MaxIterations)
            m_max_iterations = XML::readTaggedInt(r, tag);
        else
            r->skipCurrentElement();
    }
}

// ----------------------------------------------------------------------------

GeneticMinimizerItem::GeneticMinimizerItem()
    : MinimizerItem()
    , m_tolerance(0.01)
    , m_max_iterations(3)
    , m_population_size(300)
    , m_random_seed(0)
{
}

GeneticMinimizerItem::~GeneticMinimizerItem() = default;
void GeneticMinimizerItem::setTolerance(double value)
{
    m_tolerance = value;
}
void GeneticMinimizerItem::setMaxIterations(int value)
{
    m_max_iterations = value;
}
void GeneticMinimizerItem::setPopulationSize(int value)
{
    m_population_size = value;
}
void GeneticMinimizerItem::setRandomSeed(int value)
{
    m_random_seed = value;
}

std::unique_ptr<IMinimizer> GeneticMinimizerItem::createMinimizer() const
{
    auto* domainMinimizer = new GeneticMinimizer;
    domainMinimizer->setTolerance(tolerance());
    domainMinimizer->setMaxIterations(maxIterations());
    domainMinimizer->setPopulationSize(populationSize());
    domainMinimizer->setRandomSeed(randomSeed());
    return std::unique_ptr<IMinimizer>(domainMinimizer);
}

void GeneticMinimizerItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeTaggedValue(w, Tag::Tolerance, m_tolerance);
    XML::writeTaggedValue(w, Tag::MaxIterations, m_max_iterations);
    XML::writeTaggedValue(w, Tag::PopulationSize, m_population_size);
    XML::writeTaggedValue(w, Tag::RandomSeed, m_random_seed);
}

void GeneticMinimizerItem::readFrom(QXmlStreamReader* r)
{
    while (r->readNextStartElement()) {
        QString tag = r->name().toString();
        if (tag == Tag::Tolerance)
            m_tolerance = XML::readTaggedDouble(r, tag);
        else if (tag == Tag::MaxIterations)
            m_max_iterations = XML::readTaggedInt(r, tag);
        else if (tag == Tag::PopulationSize)
            m_population_size = XML::readTaggedInt(r, tag);
        else if (tag == Tag::RandomSeed)
            m_random_seed = XML::readTaggedInt(r, tag);
        else
            r->skipCurrentElement();
    }
}

// ----------------------------------------------------------------------------

SimAnMinimizerItem::SimAnMinimizerItem()
    : MinimizerItem()
    , m_max_iterations(100)
    , m_iterations_at_temp(10)
    , m_step_size(1.0)
    , m_boltzmann_K(1.0)
    , m_boltzmann_T_init(50.0)
    , m_boltzmann__mu(1.05)
    , m_boltzmann_T_min(0.1)
{
}

SimAnMinimizerItem::~SimAnMinimizerItem() = default;
void SimAnMinimizerItem::setMaxIterations(int value)
{
    m_max_iterations = value;
}
void SimAnMinimizerItem::setIterationsAtEachTemp(int value)
{
    m_iterations_at_temp = value;
}
void SimAnMinimizerItem::setStepSize(double value)
{
    m_step_size = value;
}
void SimAnMinimizerItem::setBoltzmanK(double value)
{
    m_boltzmann_K = value;
}
void SimAnMinimizerItem::setBoltzmanInitT(double value)
{
    m_boltzmann_T_init = value;
}
void SimAnMinimizerItem::setBoltzmanMu(double value)
{
    m_boltzmann__mu = value;
}
void SimAnMinimizerItem::setBoltzmanMinT(double value)
{
    m_boltzmann_T_min = value;
}

std::unique_ptr<IMinimizer> SimAnMinimizerItem::createMinimizer() const
{
    auto* domainMinimizer = new SimAnMinimizer;
    domainMinimizer->setMaxIterations(maxIterations());
    domainMinimizer->setIterationsAtEachTemp(iterationsAtEachTemp());
    domainMinimizer->setStepSize(stepSize());
    domainMinimizer->setBoltzmannK(boltzmanK());
    domainMinimizer->setBoltzmannInitialTemp(boltzmanInitT());
    domainMinimizer->setBoltzmannMu(boltzmanMu());
    domainMinimizer->setBoltzmannMinTemp(boltzmanMinT());
    return std::unique_ptr<IMinimizer>(domainMinimizer);
}

void SimAnMinimizerItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeTaggedValue(w, Tag::MaxIterations, m_max_iterations);
    XML::writeTaggedValue(w, Tag::IterationsAtTemp, m_iterations_at_temp);
    XML::writeTaggedValue(w, Tag::StepSize, m_step_size);
    XML::writeTaggedValue(w, Tag::BoltzmannK, m_boltzmann_K);
    XML::writeTaggedValue(w, Tag::Tinit, m_boltzmann_T_init);
    XML::writeTaggedValue(w, Tag::BoltzmannMu, m_boltzmann__mu);
    XML::writeTaggedValue(w, Tag::Tmin, m_boltzmann_T_min);
}

void SimAnMinimizerItem::readFrom(QXmlStreamReader* r)
{
    while (r->readNextStartElement()) {
        QString tag = r->name().toString();
        if (tag == Tag::MaxIterations)
            m_max_iterations = XML::readTaggedInt(r, tag);
        else if (tag == Tag::IterationsAtTemp)
            m_iterations_at_temp = XML::readTaggedInt(r, tag);
        else if (tag == Tag::StepSize)
            m_step_size = XML::readTaggedInt(r, tag);
        else if (tag == Tag::BoltzmannK)
            m_boltzmann_K = XML::readTaggedDouble(r, tag);
        else if (tag == Tag::Tinit)
            m_boltzmann_T_init = XML::readTaggedDouble(r, tag);
        else if (tag == Tag::BoltzmannMu)
            m_boltzmann__mu = XML::readTaggedDouble(r, tag);
        else if (tag == Tag::Tmin)
            m_boltzmann_T_min = XML::readTaggedDouble(r, tag);
        else
            r->skipCurrentElement();
    }
}

// ----------------------------------------------------------------------------

GSLLMAMinimizerItem::GSLLMAMinimizerItem()
    : MinimizerItem()
    , m_tolerance(0.01)
    , m_max_iterations(0)
{
}

GSLLMAMinimizerItem::~GSLLMAMinimizerItem() = default;
void GSLLMAMinimizerItem::setTolerance(double value)
{
    m_tolerance = value;
}
void GSLLMAMinimizerItem::setMaxIterations(int value)
{
    m_max_iterations = value;
}

std::unique_ptr<IMinimizer> GSLLMAMinimizerItem::createMinimizer() const
{
    auto* domainMinimizer = new GSLLevenbergMarquardtMinimizer;
    domainMinimizer->setTolerance(tolerance());
    domainMinimizer->setMaxIterations(maxIterations());
    return std::unique_ptr<IMinimizer>(domainMinimizer);
}

void GSLLMAMinimizerItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeTaggedValue(w, Tag::Tolerance, m_tolerance);
    XML::writeTaggedValue(w, Tag::MaxIterations, m_max_iterations);
}

void GSLLMAMinimizerItem::readFrom(QXmlStreamReader* r)
{
    while (r->readNextStartElement()) {
        QString tag = r->name().toString();
        if (tag == Tag::Tolerance)
            m_tolerance = XML::readTaggedDouble(r, tag);
        else if (tag == Tag::MaxIterations)
            m_max_iterations = XML::readTaggedInt(r, tag);
        else
            r->skipCurrentElement();
    }
}
