//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Fit/Param/RealLimits.cpp
//! @brief     Implements class Limits.
//!
//! @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 "Fit/Param/RealLimits.h"
#include <iomanip>
#include <iostream>
#include <limits>
#include <sstream>

RealLimits::RealLimits()
    : m_has_lower_limit(false)
    , m_has_upper_limit(false)
    , m_lower_limit(0.)
    , m_upper_limit(0.)
{
}

RealLimits::RealLimits(bool has_lower_limit, bool has_upper_limit, double lower_limit,
                       double upper_limit)
    : m_has_lower_limit(has_lower_limit)
    , m_has_upper_limit(has_upper_limit)
    , m_lower_limit(lower_limit)
    , m_upper_limit(upper_limit)
{
}

RealLimits RealLimits::upperLimited(double bound_value)
{
    return RealLimits(false, true, 0., bound_value);
}

RealLimits RealLimits::limited(double left_bound_value, double right_bound_value)
{
    return RealLimits(true, true, left_bound_value, right_bound_value);
}

RealLimits RealLimits::limitless()
{
    return RealLimits();
}
bool RealLimits::isInRange(double value) const
{
    if (hasLowerLimit() && value < m_lower_limit)
        return false;
    if (hasUpperLimit() && value >= m_upper_limit)
        return false;
    return true;
}

double RealLimits::clamp(double value) const
{
    if (m_has_lower_limit && value < m_lower_limit)
        return m_lower_limit;
    if (m_has_upper_limit && value > m_upper_limit)
        return m_upper_limit;
    return value;
}

RealLimits RealLimits::lowerLimited(double bound_value)
{
    return RealLimits(true, false, bound_value, 0.);
}

RealLimits RealLimits::positive()
{
    return lowerLimited(std::numeric_limits<double>::min());
}

RealLimits RealLimits::nonnegative()
{
    return lowerLimited(0.);
}

std::string RealLimits::toString() const
{
    std::ostringstream result;

    if (isLimitless())
        result << "unlimited";

    else if (isPositive())
        result << "positive";

    else if (isNonnegative())
        result << "nonnegative";

    else if (isLowerLimited())
        result << "lowerLimited(" << std::fixed << std::setprecision(2) << min() << ")";

    else if (isUpperLimited())
        result << "upperLimited(" << std::fixed << std::setprecision(2) << max() << ")";

    else if (isLimited())
        result << "limited(" << std::fixed << std::setprecision(2) << min() << "," << std::fixed
               << std::setprecision(2) << max() << ")";

    return result.str();
}

bool RealLimits::operator==(const RealLimits& other) const
{
    return (m_has_lower_limit == other.m_has_lower_limit)
           && (m_has_upper_limit == other.m_has_upper_limit)
           && (m_lower_limit == other.m_lower_limit) && (m_upper_limit == other.m_upper_limit);
}

bool RealLimits::operator!=(const RealLimits& other) const
{
    return !(*this == other);
}

bool RealLimits::isLimitless() const
{
    return !hasLowerLimit() && !hasUpperLimit();
}

bool RealLimits::isPositive() const
{
    return hasLowerLimit() && !hasUpperLimit() && min() == std::numeric_limits<double>::min();
}

bool RealLimits::isNonnegative() const
{
    return hasLowerLimit() && !hasUpperLimit() && min() == 0.0;
}

bool RealLimits::isLowerLimited() const
{
    return hasLowerLimit() && !hasUpperLimit();
}

bool RealLimits::isUpperLimited() const
{
    return !hasLowerLimit() && hasUpperLimit();
}

bool RealLimits::isLimited() const
{
    return hasLowerLimit() && hasUpperLimit();
}
