#include "AdvLevmarControl.hh"

const std::string AdvLevmarControl::className=std::string("AdvLevmarControl");

/**
 *  constructor
 */
AdvLevmarControl::AdvLevmarControl() {
}

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

/*
AdvParamSet *serDefaultParam() {
    std::string memberName=std::string("setDefaultParam()");
    DebugMessage(className, memberName, "enter\n");

    AdvParamSet *param = new AdvParamSet();
    param->add(AdvLevmarConsts::CONSTRAIN,          AdvLevmarConsts::DEFAULT_CONSTRAIN);
    param->add(AdvLevmarConsts::USE_NUMERICAL_DIFF, AdvLevmarConsts::DEFAULT_USE_NUMERICAL_DIFF);
    param->add(AdvLevmarConsts::DIFF_METHOD,        AdvLevmarConsts::DEFAULT_DIFF_METHOD);
    param->add(AdvLevmarConsts::USE_DATA_WEIGHTS,   AdvLevmarConsts::DEFAULT_USE_DATA_WEIGHTS);
    param->add(AdvLevmarConsts::MAX_ITERATIONS,     AdvLevmarConsts::DEFAULT_MAX_ITERATIONS);
    param->add(AdvLevmarConsts::OUTPUT_INTERVAL,    AdvLevmarConsts::DEFAULT_OUTPUT_INTERVAL);
    param->add(AdvLevmarConsts::HISTORY_CAPACITY,   AdvLevmarConsts::DEFAULT_HISTORY_CAPACITY);

    DebugMessage(className, memberName, "enter\n");
    return param;
}
*/


/**
 *  check parameters
 *
 * \param[in] param a paameter container
 */
Bool AdvLevmarControl::checkParam(const AdvParamSet &param) {

    Bool retval=true;
    retval = retval && this->checkConstrainType(param);
    retval = retval && this->checkUseNumericalDiff(param);
    retval = retval && this->checkDiffMethod(param);
    retval = retval && this->checkUseDataWeights(param);

    retval = retval && this->checkMaxIterations(param);
    retval = retval && this->checkOutputInterval(param);

    retval = retval && this->checkHistoryCapacity(param);
    return true;
}

/**
 *  check Levmar constrain type in a parameter container
 * 
 *  \param[in] param a paameter container
 *  \return true, if the parameter container containes Levmar constrain type
 */
Bool AdvLevmarControl::checkConstrainType(const AdvParamSet &param) {
    std::string memberName=std::string("checkConstrainType(AdvParamSet &)");
    DebugMessage(className, memberName, "enter\n");

    std::string key = AdvLevmarConsts::CONSTRAIN;

    if ( ! param.contain(key) ) {
        errorMessage(className, memberName, "does not contain the parameter with the key \"%s\"\n", key.c_str());
        return false;
    } else {
        try {
            AdvLevmarConsts::Constrain constrain=this->getConstrain(param);
        } catch (std::exception &e) {
            errorMessage(className, memberName, "fail to get constrain type.\n");
            return false;
        }
    }
    DebugMessage(className, memberName, "exit\n");
    return true;
}

/**
 *  check the flag for usin numerical differential to evaluate the Jacobian of fitting function
 * 
 *  \param[in] param a paameter container
 *  \return true, if the parameter container containes the flag
 */
Bool AdvLevmarControl::checkUseNumericalDiff(const AdvParamSet &param) {
    std::string memberName=std::string("checkUseNumericalDiff(AdvParamSet &)");
    DebugMessage(className, memberName, "enter\n");
    std::string key = AdvLevmarConsts::USE_NUMERICAL_DIFF;

    if ( ! param.contain(key) ) {
        errorMessage(className, memberName, "does not contain the parameter with the key \"%s\"\n", key.c_str());
        return false;
    }
    DebugMessage(className, memberName, "exit\n");
    return true;
}

/**
 *  check the numerical differential method to evaluate the Jacobian of fitting function
 * 
 *  \param[in] param a paameter container
 *  \return true, if the parameter container containes the flag
 */
Bool AdvLevmarControl::checkDiffMethod(const AdvParamSet &param) {
    std::string memberName=std::string("checkDiffMethod(AdvParamSet &)");
    DebugMessage(className, memberName, "enter\n");
    std::string key = AdvLevmarConsts::DIFF_METHOD;

    if ( ! param.contain(key) ) {
        errorMessage(className, memberName, "does not contain the parameter with the key \"%s\"\n", key.c_str());
        return false;
    } else {
        try {
            AdvLevmarConsts::DiffMethod diffMethod=this->getDiffMethod(param);
        } catch (std::exception &e) {
            errorMessage(className, memberName, "fail to get differntial method\n");
            return false;
        }
    }
    DebugMessage(className, memberName, "exit\n");
    return true;
}

/**
 *  check the flag to use fitting with weight
 *
 *  \param[in] param a paameter container
 *  \return true, if the parameter container contains the flag
 */
Bool AdvLevmarControl::checkUseDataWeights(const AdvParamSet &param) {
    std::string memberName=std::string("checkUseDataWeights(AdvParamSet &)");
    DebugMessage(className, memberName, "enter\n");
    std::string key = AdvLevmarConsts::USE_DATA_WEIGHTS;

    if ( ! param.contain(AdvLevmarConsts::USE_DATA_WEIGHTS) ) {
        errorMessage(className, memberName, "does not contain the parameter with the key \"%s\"\n", key.c_str());
        return false;
    }
    DebugMessage(className, memberName, "exit\n");
    return true;
}

/**
 *  check maximum iteration parameter
 *
 *  \param[in] param a paameter container
 *  \return true, if the parameter container contains the maximun iterations
 */
Bool AdvLevmarControl::checkMaxIterations(const AdvParamSet &param) {
    std::string memberName=std::string("checkMaxIterations(AdvParamSet &)");
    DebugMessage(className, memberName, "enter\n");
    std::string key = AdvLevmarConsts::MAX_ITERATIONS;

    if ( ! param.contain(key) ) {
        errorMessage(className, memberName, "does not contain the parameter with the key \"%s\"\n", key.c_str());
        return false;
    } else {
        Int4 maxIterations=this->getMaxIterations(param);
        if ( maxIterations <= 0) {
            errorMessage(className, memberName, "%s %d is zero of negative number: .\n", key.c_str(), maxIterations);
            return false;
        }
    }
    DebugMessage(className, memberName, "exit\n");
    return true;
}

/**
 *  check output interval parameter
 *
 *  \param[in] param a paameter container
 *  \return true, if the parameter container contains output interval 
 */
Bool AdvLevmarControl::checkOutputInterval(const AdvParamSet &param) {
    std::string memberName=std::string("checkOutputInterval(AdvParamSet &)");
    DebugMessage(className, memberName, "enter\n");
    std::string key=AdvLevmarConsts::OUTPUT_INTERVAL;
    std::string relKey=AdvLevmarConsts::MAX_ITERATIONS;

    if ( ! param.contain(key) ) {
        errorMessage(className, memberName, "does not contain the parameter with the key \"%s\"\n", key.c_str());
        return false;
    } else {
        Int4 outputInterval=this->getOutputInterval(param);
        if ( outputInterval <= 0) {
            errorMessage(className, memberName, "%s %d is zero of negative number: .\n", key.c_str(), outputInterval);
            return false;
        } else {
            Int4 maxIterations= this->getMaxIterations(param);
            if ( maxIterations <= outputInterval ) {
                warningMessage(className, memberName, "no output in convergence loop, because %s < %s\n", relKey.c_str(), key.c_str());
                warningMessage(className, memberName, "%s : %d\n", relKey.c_str(), maxIterations);
                warningMessage(className, memberName, "%s : %d\n", key.c_str(),    outputInterval);
            }
        }
    }
    DebugMessage(className, memberName, "exit\n");
    return true;
}


Bool AdvLevmarControl::checkHistoryCapacity(const AdvParamSet &param) {
    std::string memberName=std::string("checkHistoryCapacity(AdvParamSet &)");
    DebugMessage(className, memberName, "enter\n");
    std::string key=AdvLevmarConsts::HISTORY_CAPACITY;

    if (! param.contain(key)) {
        errorMessage(className, memberName, "does not contain the parameter with the key \"%s\"\n", key.c_str());
        return false;
    } else {
        UInt4 capacity=this->getHistoryCapacity(param);
        if (capacity < 1) {
            errorMessage(className, memberName, "value is invalid as capacity. \"%u\"\n", capacity);
            return false;
        } else if (capacity > 1000) {
            warningMessage(className, memberName, "too large capacity for convergence history \"%u\"\n", capacity);
            return false;
        }
    }
    DebugMessage(className, memberName, "exit\n");
    return true;
}


/**
 *  translate parameters to inner form
 *
 *  \param[in] param a paameter container
 */
void AdvLevmarControl::toInnerForm(const AdvParamSet &param) {
    std::string memberName=std::string("toInnerForm(AdvParamSet &)");
    DebugMessage(className, memberName, "enter\n");

    this->constrain        = this->getConstrain(       param);
    this->useNumericalDiff = this->getUseNumericalDiff(param);
    this->diffMethod       = this->getDiffMethod(      param);
    this->useDataWeights   = this->getUseDataWeights(  param);
    this->maxIterations    = this->getMaxIterations(   param);
    this->outputInterval   = this->getOutputInterval(  param);
    this->historyCapacity  = this->getHistoryCapacity(param);
    DebugMessage(className, memberName, "historyCapacity =\"%u\"\n", this->historyCapacity);

    DebugMessage(className, memberName, "exit\n");
}

/**
 *  get Levmar constrain type from a parameter container
 *
 *  \param[in] param a paameter container
 */
AdvLevmarConsts::Constrain AdvLevmarControl::getConstrain(const AdvParamSet &param) {
    return static_cast<AdvLevmarConsts::Constrain>(param.getInt4(AdvLevmarConsts::CONSTRAIN));
}

/**
 *  get the flag to use numerical differential from a parameter container
 *
 *  \param[in] param a paameter container
 */
Bool AdvLevmarControl::getUseNumericalDiff(const AdvParamSet &param) {
    return param.getBool(AdvLevmarConsts::USE_NUMERICAL_DIFF);
}

/**
 *  get the numerical differential method from a parameter container
 *
 *  \param[in] param a paameter container
 */
AdvLevmarConsts::DiffMethod AdvLevmarControl::getDiffMethod(const AdvParamSet &param) {
    return static_cast<AdvLevmarConsts::DiffMethod>(param.getInt4(AdvLevmarConsts::DIFF_METHOD));
}

/**
 *  get the flag for fiting with weight from a parameter container
 *
 *  \param[in] param a paameter container
 */
Bool AdvLevmarControl::getUseDataWeights(const AdvParamSet &param) {
    return param.getBool(AdvLevmarConsts::USE_DATA_WEIGHTS);
}

/**
 *  get the maximum number of fitting iterations from a parameter container
 *
 *  \param[in] param a paameter container
 */
Int4 AdvLevmarControl::getMaxIterations(const AdvParamSet &param) {
    return param.getInt4(AdvLevmarConsts::MAX_ITERATIONS);
}

/**
 *  get the output interval from a parameter container
 *
 *  \param[in] param a paameter container
 */
Int4 AdvLevmarControl::getOutputInterval(const AdvParamSet &param) {
    return param.getInt4(AdvLevmarConsts::OUTPUT_INTERVAL);
}

/**
 *
 */
UInt4 AdvLevmarControl::getHistoryCapacity(const AdvParamSet &param) {
    return param.getUInt4(AdvLevmarConsts::HISTORY_CAPACITY);
}


/**
 *  output control parameters to the stdard out
 *
 *  \param[in] param a paameter container
 */
void AdvLevmarControl::output() {
    std::string memberName=std::string("outputControlValues())");
    DebugMessage(className, memberName, "enter\n");

    std::string boolStr[] = {
        *(new std::string("false")),  // bool::false
        *(new std::string("true")),   // bool::true
    };

#define ControlValue(fmt, key, value)    message(fmt, " ", " ", key.c_str(), value);
    char controlStrFmt[] ="%4s%4s%-23s : %s\n";
    char controlIntFmt[] ="%4s%4s%-23s : %d\n";
    ControlValue(controlStrFmt, AdvLevmarConsts::CONSTRAIN,          AdvLevmarConsts::CONSTRAIN_STR[this->constrain].c_str()       );
    ControlValue(controlStrFmt, AdvLevmarConsts::USE_NUMERICAL_DIFF, boolStr[static_cast<Int4>(this->useNumericalDiff)].c_str());
    if (this->useNumericalDiff) {
        ControlValue(controlStrFmt, AdvLevmarConsts::DIFF_METHOD,        AdvLevmarConsts::DIFF_METHOD_STR[this->diffMethod].c_str()     );
    }
    ControlValue(controlStrFmt, AdvLevmarConsts::USE_DATA_WEIGHTS,   boolStr[static_cast<Int4>(this->useDataWeights)].c_str()  );
    message("\n");
    ControlValue(controlIntFmt, AdvLevmarConsts::MAX_ITERATIONS,     this->maxIterations   );
    ControlValue(controlIntFmt, AdvLevmarConsts::OUTPUT_INTERVAL,    this->outputInterval  );
    ControlValue(controlIntFmt, AdvLevmarConsts::HISTORY_CAPACITY,   this->historyCapacity );
    message("\n");

    DebugMessage(className, memberName, "exit\n");
}

