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

namespace arhiplex
{
class Constraint;
class LinearExpression;
class QuadExpression;
class GeneralExpression;
/**
 \~russian
 * Работа с переменной - чтение/изменение имени, верхней и нижней границы, типа,
 * а также удаление
 \brief Переменная

 \~english
 * Working with a variable - reading/changing the name, upper and lower bounds, type,
 * and also deleting
 \brief Variable

 \~chinese-traditional
 * Working with a variable - reading/changing the name, upper and lower bounds, type,
 * and also deleting
 \brief Variable
 */
class Variable
{
  public:
  /**
  \private
  */
    Variable(arhiplex::IVariable *var) : _var(var, releasable_object_deleter)
    {
    }

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

\~english
Gets the name of a variable in the model
@return the name of the variable

\~chinese-traditional
Gets the name of a variable in the model
@return the name of the variable
*/
    const char *GetName() const
    {
        auto result = _var->GetName();
        CHECKERR(_var);
        return result;
    }

/*!
\~russian
Задаёт имя переменной в модели
\param[in] szName

\~english
Sets the name of the variable in the model
\param[in] szName

\~chinese-traditional
Sets the name of the variable in the model
\param[in] szName
*/
    void SetName(const char *szName)
    {
        _var->SetName(szName);
        CHECKERR(_var);
    }

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

\~english
Gets the type of the variable
@return the type of the variable

\~chinese-traditional
Gets the type of the variable
@return the type of the variable
*/	
    arhiplex::variable_type GetType() const
    {
        auto result = _var->GetType();
        CHECKERR(_var);
        return result;
    }

/*!
\~russian
Задаёт тип переменной в модели
\param[in] type тип переменной

\~english
Sets the type of variable in the model
\param[in] type variable type

\~chinese-traditional
Sets the type of variable in the model
\param[in] type variable type
*/	
    void SetType(arhiplex::variable_type type)
    {
        _var->SetType(type);
        CHECKERR(_var);
    }

/*!
\~russian
Задаёт верхнюю границу для переменной
\param[in] upper_bound новая верхняя граница переменной

\~english
Sets the upper bound for the variable
\param[in] upper_bound new upper bound for the variable

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

\~english
Gets the upper bound of the variable
@return the upper bound of the variable

\~chinese-traditional
Gets the upper bound of the variable
@return the upper bound of the variable
*/	
    double GetUpperBound() const
    {
        double ret = _var->GetUpperBound();
        CHECKERR(_var);
        return ret;
    }
	
/*!
\~russian
Задаёт нижнюю границу для переменной
\param[in] lower_bound новая нижняя граница переменной

\~english
Sets the lower bound for the variable
\param[in] lower_bound new lower bound for the variable

\~chinese-traditional
Sets the lower bound for the variable
\param[in] lower_bound new lower bound for the variable
*/	
    void SetLowerBound(double lower_bound)
    {
        _var->SetLowerBound(lower_bound);
        CHECKERR(_var);
    }
	
/*!
\~russian
Получает нижнюю границу переменной
@return нижняя граница переменной

\~english
Gets the lower bound of a variable
@return the lower bound of a variable

\~chinese-traditional
Gets the lower bound of a variable
@return the lower bound of a variable
*/	
    double GetLowerBound() const
    {
        double ret = _var->GetLowerBound();
        CHECKERR(_var);
        return ret;
    }

/*!
\~russian
Помечает переменную как удаленную. Переменная не будет участвовать в расчетах

\~english
Marks the variable as removed. The variable will not participate in calculations.

\~chinese-traditional
Marks the variable as removed. The variable will not participate in calculations.
*/

    void Remove()
    {
        _var->Remove();
        CHECKERR(_var);
    }
/**
\privatesection
*/
    arhiplex::IVariable *Get() const noexcept
    {
        return _var.get();
    }

    friend LinearExpression operator+(const Variable &var, double a);
    friend LinearExpression operator+(double a, const Variable &var);
    friend LinearExpression operator+(const Variable &left, const Variable &right);
    friend LinearExpression operator-(const Variable &var, double a);
    friend LinearExpression operator-(double a, const Variable &var);
    friend GeneralExpression operator/(double a, const Variable &var);
    friend LinearExpression operator-(const Variable &var);
    friend LinearExpression operator-(const Variable &left, const Variable &right);
    friend LinearExpression operator*(const Variable &var, double a);
    friend LinearExpression operator/(const Variable &var, double a);
    friend LinearExpression operator*(double a, const Variable &var);

    friend Constraint operator==(const Variable &left, double a);
    friend Constraint operator==(double a, const Variable &left);
    friend Constraint operator==(const Variable &left, const Variable &right);
    friend Constraint operator>=(const Variable &left, double a);
    friend Constraint operator>=(double a, const Variable &left);
    friend Constraint operator>=(const Variable &left, const Variable &right);
    friend Constraint operator<=(const Variable &left, double a);
    friend Constraint operator<=(double a, const Variable &left);
    friend Constraint operator<=(const Variable &left, const Variable &right);

    friend QuadExpression operator*(const Variable &var1, const Variable &var2);
    friend QuadExpression operator*(const Variable &var, const LinearExpression &expr);
/**
\privatesection
*/
  private:
    std::shared_ptr<arhiplex::IVariable> _var;
};

} // namespace arhiplex