#pragma once
#include "../arhiplex_itf.h"
#include "../arhiplex_utils.h"
#include "linear_expression.h"
#include "quad_expression.h"
#include "utils.h"
#include <memory>

namespace arhiplex
{

/**
 \~russian
 * Работа с ограничениями у модели, позволяет работать с выражением и границами
 * а также удалять его
 \brief ограничение

 \~english
 * Working with model constraints, allows you to work with expression and boundaries
 * and also delete it
 \brief constraint

 \~chinese-traditional
 * Working with model constraints, allows you to work with expression and boundaries
 * and also delete it
 \brief constraint
 */
class Constraint
{
  public:
/**
\private
*/
    Constraint(arhiplex::IConstraint *constr) : _constr(constr, releasable_object_deleter)
    {
    }
/**
\~russian
создает ограничение вида ( epxr (<=,==,>=) constant )
\param[in] expr выражение, являющееся левой частью ограничения
\param[in] sense переменная, задающая знак ( <=, ==, >=)
\param[in] rhs правая часть ограничения

\~english
creates a constraint of the form ( epxr (<=,==,>=) constant )
\param[in] expr expression that is the left side of the constraint
\param[in] sense variable that specifies the sign ( <=, ==, >=)
\param[in] rhs right side of the constraint

\~chinese-traditional
creates a constraint of the form ( epxr (<=,==,>=) constant )
\param[in] expr expression that is the left side of the constraint
\param[in] sense variable that specifies the sign ( <=, ==, >=)
\param[in] rhs right side of the constraint
*/
    Constraint(const LinearExpression &expr, constraint_sense sense, double rhs) : 
        _constr(CreateConstraint(expr.Get(), sense, rhs), releasable_object_deleter)
    {
        CHECKERR(_constr);
        if(!_constr)
            throw arhiplex_exception(-1, "Невозможно создать ограничение"); 
    }

/**
\~russian
создает ограничение вида ( epxr (<=,==,>=) constant )
\param[in] expr выражение, являющееся левой частью ограничения
\param[in] sense переменная, задающая знак ( <=, ==, >=)
\param[in] rhs правая часть ограничения

\~english
creates a constraint of the form ( epxr (<=,==,>=) constant )
\param[in] expr expression that is the left side of the constraint
\param[in] sense variable that specifies the sign ( <=, ==, >=)
\param[in] rhs right side of the constraint

\~chinese-traditional
creates a constraint of the form ( epxr (<=,==,>=) constant )
\param[in] expr expression that is the left side of the constraint
\param[in] sense variable that specifies the sign ( <=, ==, >=)
\param[in] rhs right side of the constraint
*/
    Constraint(const QuadExpression &expr, constraint_sense sense, double rhs) : 
        _constr(CreateConstraint(expr.Get(), sense, rhs), releasable_object_deleter)
    {
        CHECKERR(_constr);
        if(!_constr)
            throw arhiplex_exception(-1, "Невозможно создать ограничение"); 
    }


/*!
\~russian
Линейное выражение, связанное с ограничением
@return Выражение, связанное с ограничением

\~english
Linear expression associated with constraint
@return Expression associated with constraint

\~chinese-traditional
Linear expression associated with constraint
@return Expression associated with constraint
*/

    LinearExpression GetLinearExpression() const
    {
        auto ret = _constr->GetLinearExpression();
        CHECKERR(_constr);
        return ret;
    }

/*!
\~russian
Квадратичное выражение, связанное с ограничением
@return Выражение, связанное с ограничением

\~english
Quadratic expression associated with constraint
@return Expression associated with constraint

\~chinese-traditional
Quadratic expression associated with constraint
@return Expression associated with constraint
*/

    QuadExpression GetQuadExpression() const
    {
        auto ret = _constr->GetQuadExpression();
        CHECKERR(_constr);
        return ret;
    }

/*!
\~russian
Задает верхнюю границу ограничения
\param[in] upper_bound Новая верхняя граница ограничения

\~english
Sets the upper bound of the constraint
\param[in] upper_bound The new upper bound of the constraint

\~chinese-traditional
Sets the upper bound of the constraint
\param[in] upper_bound The new upper bound of the constraint
*/
    void SetUpperBound(double upper_bound)
    {
        _constr->SetUpperBound(upper_bound);
        CHECKERR(_constr);
    }
/*!
\~russian
Получает верхнюю границу ограничения
@return Верхнюю границу ограничения

\~english
Gets the upper bound of the constraint
@return The upper bound of the constraint

\~chinese-traditional
Gets the upper bound of the constraint
@return The upper bound of the constraint
*/

    double GetUpperBound() const
    {
        auto ret = _constr->GetUpperBound();
        CHECKERR(_constr);
        return ret;
    }

/*!
\~russian
Задаёт нижнюю границу ограничения
\param[in] lower_bound Новая нижняя граница ограничения

\~english
Sets the lower bound of the constraint
\param[in] lower_bound New lower bound of the constraint

\~chinese-traditional
Sets the lower bound of the constraint
\param[in] lower_bound New lower bound of the constraint
*/
    void SetLowerBound(double lower_bound)
    {
        _constr->SetLowerBound(lower_bound);
        CHECKERR(_constr);
    }

/*!
\~russian
Получает нижнюю границу ограничения
@return Нижнюю границу ограничения

\~english
Gets the lower bound of the constraint
@return The lower bound of the constraint

\~chinese-traditional
Gets the lower bound of the constraint
@return The lower bound of the constraint
*/
    double GetLowerBound() const
    {
        auto ret = _constr->GetLowerBound();
        CHECKERR(_constr);
        return ret;
    }

/*!
\~russian
Получает интервал ограничения: upper_bound - lower_bound
@return upper_bound - lower_bound

\~english
Gets the constraint range: upper_bound - lower_bound
@return upper_bound - lower_bound

\~chinese-traditional
Gets the constraint range: upper_bound - lower_bound
@return upper_bound - lower_bound
*/

    double GetRange() const
    {
        auto ret = _constr->GetRange();
        CHECKERR(_constr);
        return ret;
    }
/*!
\~russian
Задаёт интервал ограничения: lower_bound <= expression <= lower_bound + range
\param[in] range интервал для ограничения

\~english
Specifies the constraint range: lower_bound <= expression <= lower_bound + range
\param[in] range range for the constraint

\~chinese-traditional
Specifies the constraint range: lower_bound <= expression <= lower_bound + range
\param[in] range range for the constraint
*/
    void SetRange(double range)
    {
        _constr->SetRange(range);
        CHECKERR(_constr);
    }

/*!
\~russian
Помечает ограничение как удаленное. Такое ограничение будет исключено из расчетов

\~english
Marks the constraint as removed. This constraint will be excluded from calculations.

\~chinese-traditional
Marks the constraint as removed. This constraint will be excluded from calculations.
*/
    void Remove()
    {
        _constr->Remove();
        CHECKERR(_constr);
    }

/*!
\~russian
Получает имя ограничения
@return имя ограничения

\~english
Gets the constraint name
@return the constraint name

\~chinese-traditional
Gets the constraint name
@return the constraint name
*/
    const char *GetName() const
    {
        auto result = _constr->GetName();
        CHECKERR(_constr);
        return result;
    }

/*!
\~russian
Задаёт имя ограничения
\param[in] szName имя ограничения

\~english
Specifies the name of the constraint
\param[in] szName constraint name

\~chinese-traditional
Specifies the name of the constraint
\param[in] szName constraint name
*/
    void SetName(const char *szName)
    {
        _constr->SetName(szName);
        CHECKERR(_constr);
    }


/*\private*/
    arhiplex::IConstraint *Get() const noexcept
    {
        return _constr.get();
    }

  private:
    std::shared_ptr<arhiplex::IConstraint> _constr;
};

} // namespace arhiplex