/*! \file arhiplex_itf.h */

#pragma once
#ifdef _WIN32
#define ARHIPLEX_CALL __stdcall
#else
#define ARHIPLEX_CALL
#endif
#include <cstdint>
#include <limits>

/*!
arhiplex
*/
namespace arhiplex
{
constexpr auto inf = std::numeric_limits<double>::infinity();

class IReleasable
{
  protected:
    virtual ~IReleasable() = default;

  public:
    virtual void Release() = 0;
};

class INamedObject
{
  public:
    virtual ~INamedObject() = default;
    virtual const char* GetName() const = 0;
    virtual void SetName(const char* name) = 0;
};

class IErrorInfo
{
  public:
    virtual ~IErrorInfo() = default;
    virtual int GetErrorCode() const = 0;
    virtual const char* GetErrorText() const = 0;
};

/*! \enum variable_type
 \~russian
 * Тип переменной

 \~english
 * Variable type

 \~chinese-traditional
 * Variable type
 */
enum class variable_type
{
    continuous, /*!< \~russian непрерывная \~english continuous \~chinese-traditional continuous */
    binary, /*!< \~russian бинарная \~english binary \~chinese-traditional binary */
    integer, /*!< \~russian целочисленная \~english integer \~chinese-traditional integer */
    semicontinuous, /*!< \~russian полу-непрерывная \~english semicontinuous \~chinese-traditional semicontinuous */
    semiinteger /*!< \~russian полу-целая \~english semiinteger \~chinese-traditional semiinteger */
};

/** 
\~russian
Направление оптимизации целевой функции

\~english
Direction of optimization of the objective function

\~chinese-traditional
Direction of optimization of the objective function
 */
enum class objective_sense
{
    minimize, /*!< \~russian Минимизация \~english Minimization \~chinese-traditional Minimization */
    maximize  /*!< \~russian Максимизация \~english Maximization \~chinese-traditional Maximization */
};
/**
\~russian
Знак ограничения между левой частью (выражением) и правой частью (константой)

\~english
The constraint sign between the left side (expression) and the right side (constant)

\~chinese-traditional
The constraint sign between the left side (expression) and the right side (constant)
*/
enum class constraint_sense
{
    equal, /*!< '==' */
    less_equal, /*!< '<=' */
    greater_equal /*!< '>=' */
};

class IVariable : public INamedObject, public IErrorInfo, public IReleasable
{
  protected:
    virtual ~IVariable() = default;

  public:
    virtual void SetUpperBound(double upper_bound) = 0;
    virtual double GetUpperBound() const = 0;
    virtual void SetLowerBound(double lower_bound) = 0;
    virtual double GetLowerBound() const = 0;
    virtual void Remove() = 0;
    virtual variable_type GetType() const = 0;
    virtual void SetType(variable_type type) = 0;
};

class IQuadExpression;

class ILinearExpression : public IReleasable, public IErrorInfo, public INamedObject
{
  protected:
    virtual ~ILinearExpression() = default;

  public:
    virtual int GetTermsCount() const = 0;
    virtual IVariable *GetTermVariable(int term_idx) const = 0;
    virtual int GetTermVariableIndex(int term_idx) const = 0;
    virtual double GetTermCoeff(int term_idx) const = 0;
    virtual void SetTermCoeff(int term_idx, double value) = 0;

    virtual double GetConstant() const = 0;
    virtual void SetConstant(double constant) = 0;
    virtual void AddConstant(double constant) = 0;

    virtual void AddTerm(const IVariable *pVar, double coeff) = 0;
    virtual void AddExpression(const ILinearExpression *pExpr, double mult) = 0;

    virtual void RemoveTerm(int term_idx) = 0;
    virtual void RemoveVariable(const IVariable *pVar) = 0;

    virtual IQuadExpression* GetQuadPart() = 0;
    virtual ILinearExpression *CreateFreeCopy() const = 0;
};

class IQuadExpression : public IReleasable, public IErrorInfo, public INamedObject
{
  protected:
    virtual ~IQuadExpression() = default;

  public:
    virtual int GetTermsCount() const = 0;
    virtual IVariable *GetTermVariable1(int term_idx) const = 0;
    virtual int GetTermVariable1Index(int term_idx) const = 0;
    virtual IVariable *GetTermVariable2(int term_idx) const = 0;
    virtual int GetTermVariable2Index(int term_idx) const = 0;

    virtual double GetTermCoeff(int term_idx) const = 0;
    virtual void SetTermCoeff(int term_idx, double value) = 0;

    virtual void AddTerm(const IVariable *pVar1, const IVariable *pVar2, double coeff) = 0;
    virtual void AddExpression(const IQuadExpression *pExpr, double mult) = 0;

    virtual void RemoveTerm(int term_idx) = 0;

    virtual ILinearExpression* GetLinearPart() = 0;
    virtual IQuadExpression *CreateFreeCopy() const = 0;
};

enum class gen_expr_type {
  constant, variable, minus, plus, divide, multiply,
  func_square_root, func_sqr, func_pow, func_exp, func_log, func_log2, func_log10,
  func_sin, func_cos, func_tan,
  func_logistic, func_tanh, func_signpow
};

class IGeneralExpression : public IReleasable, public IErrorInfo {
protected:
  virtual ~IGeneralExpression() = default;
public:
  virtual gen_expr_type GetType() const = 0;
  virtual double GetConstant() const = 0; // for gen_expr_type::constant
  virtual IVariable* GetVariable() = 0; // for gen_expr_type::var
  virtual int GetExprCount() const = 0;
  virtual int GetArity() const = 0;
  virtual IGeneralExpression* GetExpr(int idx) const = 0;
  virtual void SetExpr(int idx, const IGeneralExpression* expr) = 0;
  virtual void AddExpr(const IGeneralExpression*) = 0;
  virtual void RemoveExpr(int idx) = 0;
  virtual IGeneralExpression *CreateFreeCopy() const = 0;
};

class IConstraint : public IErrorInfo, public IReleasable, public INamedObject
{
  protected:
    virtual ~IConstraint() = default;

  public:
    virtual void Remove() = 0;
    virtual ILinearExpression *GetLinearExpression() = 0;
    virtual IQuadExpression *GetQuadExpression() = 0;
    virtual void SetUpperBound(double upper_bound) = 0;
    virtual double GetUpperBound() const = 0;
    virtual void SetLowerBound(double lower_bound) = 0;
    virtual double GetLowerBound() const = 0;

    virtual double GetRange() const = 0;
    virtual void SetRange(double range) = 0;
};

class IGeneralConstraint : public IErrorInfo, public IReleasable, public INamedObject {
protected:
  virtual ~IGeneralConstraint() = default;

public:
  virtual void Remove() = 0;
  virtual IGeneralExpression* GetExpression() = 0;
  virtual IVariable* GetVariable() = 0;
};

class ILogCallback
{
  public:
    virtual ~ILogCallback() = default;
    virtual void Log(const char* msg) = 0;
};

/**
\~russian
\enum solve_result Результат процесса расчетов

\~english
\enum solve_result Result of the calculation process

\~chinese-traditional
\enum solve_result Result of the calculation process
*/
enum class solve_result
{
    success, /*!< \~russian Процесс решения успешен - найдено некоторое решение или обнаружена недостижимость модели \~english The solution process is successful - some solution is found or the model is found to be infeasible \~chinese-traditional The solution process is successful - some solution is found or the model is found to be infeasible */
    fail, /*!< \~russian Процесс решения неудачен - не найдено никаких решений и не обнаружена недостижимость модели \~english The solution process failed - no solutions were found and no model infeasibility was detected \~chinese-traditional The solution process failed - no solutions were found and no model infeasibility was detected */
    remote_invalid_api_key, /*!< \~russian Невалидный ключ для удаленного расчета \~english Invalid key for remote calculation \~chinese-traditional Invalid key for remote calculation */
    remote_api_key_not_set, /*!< \~russian Переменная окружения X_API_KEY не установлена \~english Environment variable X_API_KEY is not set \~chinese-traditional Environment variable X_API_KEY is not set */
    remote_time_amount_is_over, /*!< \~russian Не осталось доступного времени для осуществления удаленного расчета \~english There is no time left to perform remote calculation \~chinese-traditional There is no time left to perform remote calculation */
    remote_time_per_calc_violated, /*!< \~russian Превышен лимит времени для единичного удаленного расчета. Параметр time_limit нарушает ограничения лицензии \~english Time limit for single remote calculation exceeded. The time_limit parameter violates license restrictions \~chinese-traditional Time limit for single remote calculation exceeded. The time_limit parameter violates license restrictions */
    remote_fail /*!< \~russian Удаленный расчет неудачен \~english Remote calculation failed \~chinese-traditional Remote calculation failed */
};

/**
\~russian
\enum solution_status Статус решения по результатам расчета

\~english
\enum solution_status The status of the solution based on the calculation results

\~chinese-traditional
\enum solution_status The status of the solution based on the calculation results
*/
enum class solution_status
{
    invalid_solution, /*!< \~russian неопределенное/невалидное значение \~english undefined/invalid value \~chinese-traditional undefined/invalid value */
    optimal, /*!< \~russian решение оптимально (погрешность в рамках заданного значения) \~english the solution is optimal (error within the specified value) \~chinese-traditional the solution is optimal (error within the specified value) */
    feasible, /*!< \~russian решение найдено, но не оптимально (погрешность > заданного значения) \~english solution found, but not optimal (error > specified value) \~chinese-traditional solution found, but not optimal (error > specified value) */
    infeasible, /*!< \~russian модель не имеет решения (решение недостижимо) \~english the model has no solution (the solution is infeasible) \~chinese-traditional the model has no solution (the solution is infeasible) */
    unbounded, /*!< \~russian модель неограничена (целевая функция может бесконечно неограниченно увеличиваться/уменьшаться) \~english the model is unbounded (the objective function can increase/decrease infinitely without limit) \~chinese-traditional the model is unbounded (the objective function can increase/decrease infinitely without limit) */
    infeasible_or_unbounded/*!< \~russian модель недостижима или неограничена \~english model is infeasible or unbounded \~chinese-traditional model is infeasible or unbounded */
};

class ISolveResult : public IReleasable, public IErrorInfo
{
  protected:
    virtual ~ISolveResult() = default;

  public:
    virtual solve_result GetSolveResult() const = 0;
    virtual solution_status GetSolutionStatus() const = 0;
    virtual double GetRelativeGap() const = 0;
    virtual unsigned int GetIterationsCount() const = 0;
    virtual std::int64_t GetProcessedNodesCount() const = 0;
    virtual double GetObjectiveFunctionValue() const = 0;
    virtual double GetBestBoundValue() const = 0;
    virtual double GetSolveTime() const = 0; // секунды

    virtual double GetVariableValue(const IVariable *var) const = 0;
    virtual double GetVariableValue(const char* var_name) const = 0;
    virtual double GetDualValue(const IConstraint* var) const = 0;
    virtual double GetDualValue(const char* constr_name) const =0;
    virtual double GetReducedCost(const IVariable* var) const = 0;
    virtual double GetReducedCost(const char* var_name) const = 0;
    virtual double GetExpressionValue(const ILinearExpression *expression) const = 0;
    virtual double GetExpressionValue(const IQuadExpression *expression) const = 0;

    virtual void WriteSolution(const char* file_name) const = 0;
};

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

\~english
\enum compute_iis_result Result of the process of computing an irreducible infeasible set

\~chinese-traditional
\enum compute_iis_result Result of the process of computing an irreducible infeasible set
*/
enum class compute_iis_result {
    success, /*!< \~russian Процесс вычисления несократимого недостижимого множества успешен - найдено некоторое несократимое недостижимое множество или обнаружена достижимость модели \~english The process of computing an irreducible infeasible set is successful - some irreducible infeasible set is found or the feasibility of the model is found \~chinese-traditional The process of computing an irreducible infeasible set is successful - some irreducible infeasible set is found or the feasibility of the model is found */
    fail, /*!< \~russian Процесс вычисления несократимого недостижимого множества неудачен - не найдено никаких несократимых недостижимых множеств и не обнаружена достижимость модели \~english The process of computing an irreducible infeasible set failed - no irreducible infeasible sets were found and the model was not found to be feasible \~chinese-traditional The process of computing an irreducible infeasible set failed - no irreducible infeasible sets were found and the model was not found to be feasible */
};

/**
\~russian
\enum model_status Статус модели по результатам вычисления несократимого недостижимого множества

\~english
\enum model_status Status of the model based on the results of computing the irreducible infeasible set

\~chinese-traditional
\enum model_status Status of the model based on the results of computing the irreducible infeasible set
*/
enum class model_status {
    unknown, /*!< \~russian неизвестное значение \~english unknown value \~chinese-traditional unknown value */
    feasible, /*!< \~russian модель достижима (модель не имеет несократимого недостижимого множества) \~english the model is feasible (the model does not have an irreducible infeasible set) \~chinese-traditional the model is feasible (the model does not have an irreducible infeasible set) */
    infeasible, /*!< \~russian модель недостижима (найдено некоторое несократимое недостижимое множество) \~english the model is infeasible (some irreducible infeasible set is found) \~chinese-traditional the model is infeasible (some irreducible infeasible set is found) */
};

class IComputeIisResult : public IReleasable, public IErrorInfo {
  protected:
    virtual ~IComputeIisResult() = default;

  public:
    virtual compute_iis_result GetComputeIisResult() const = 0;
    virtual model_status GetModelStatus() const = 0;
    virtual bool GetMinimal() const = 0;
    virtual int GetNumConstraints() const = 0;
    virtual int GetNumBounds() const = 0;
    virtual double GetComputeIisTime() const = 0; // секунды

    virtual void WriteIis(const char* file_name) const = 0;
};

class IModel : public INamedObject, public IErrorInfo, public IReleasable
{
  protected:
    virtual ~IModel() = default;

  public:
    virtual void SetOemLicenseKey(const char* oem_license_key) = 0;

    virtual void Read(const char* file_name) = 0;
    virtual void ReadMps(const char* file_name) = 0;
    virtual void ReadLp(const char* file_name) = 0;

    virtual void Write(const char* file_name, const char* name_mapping_file) = 0;
    virtual void WriteMps(const char* file_name, const char* name_mapping_file) = 0;
    virtual void WriteLp(const char* file_name, const char* name_mapping_file) = 0;

    virtual int GetIntParam(const char* param_name) const = 0;
    virtual double GetDblParam(const char* param_name) const = 0;
    virtual const char* GetStringParam(const char* param_name) const = 0;
    virtual bool GetBoolParam(const char* param_name) const = 0;
    virtual void SetIntParam(const char* param_name, int nVal) = 0;
    virtual void SetDblParam(const char* param_name, double dVal) = 0;
    virtual void SetStringParam(const char* param_name, const char* cVal) = 0;
    virtual void SetBoolParam(const char* param_name, bool dVal) = 0;

    virtual IVariable *AddVariable(double lb, double ub, double obj, variable_type var_type, const char* name) = 0;

    virtual IConstraint *AddConstraint(const ILinearExpression* expr, constraint_sense sense, double rhs,
                                       const char* name) = 0;
    virtual IConstraint *AddConstraint(const IQuadExpression* expr, double lb, double ub, const char* name) = 0;

    virtual IConstraint *AddConstraint(const IQuadExpression* expr, constraint_sense sense, double rhs,
                                       const char* name) = 0;
    virtual IConstraint *AddConstraint(const ILinearExpression* expr, double lb, double ub, const char* name) = 0;

    virtual IConstraint *AddConstraint(const IConstraint* constr, const char* name) = 0;

    virtual int GetVariablesCount() const = 0;
    virtual IVariable* GetVariable(int var_idx) = 0;
    virtual IVariable* GetVariableByName(const char* name) = 0;

    virtual int GetConstraintsCount() const = 0;
    virtual IConstraint* GetConstraint(int constr_idx) = 0;
    virtual IConstraint* GetConstraintByName(const char* name) = 0;

    virtual void SetObjectiveSense(objective_sense sense) = 0;
    virtual objective_sense GetObjectiveSense() const = 0;
    virtual void SetObjectiveOffset(double offset) = 0;
    virtual void SetObjective(const ILinearExpression* expr, objective_sense sense) = 0;
    virtual void SetObjective(const IQuadExpression* quad_expr, objective_sense sense) = 0;
    virtual void ClearObjective() = 0;
    virtual ILinearExpression* GetObjective() = 0;
    virtual IQuadExpression* GetQuadObjective() = 0;

    virtual ISolveResult* Solve() const = 0;
    virtual ISolveResult* SolveRemote(const char* name_mapping_file) = 0;
    virtual void SolveRemoteAsync(const char* name_mapping_file) = 0;
    virtual const char* GetRemoteSolveLog(const char* szCalcUID) const = 0;

    virtual IComputeIisResult* ComputeIis() const = 0;

    virtual void Remove(IVariable* var) = 0;
    virtual void Remove(IConstraint* constr) = 0;

    virtual void Clear() = 0;

    virtual void SetLogFile(const char* log_file) = 0;
    virtual void SetLogCallback(ILogCallback* log_callback) = 0;

    virtual void AddMipStartValue(const char* var_name, double var_value) = 0;
    virtual void AddMipStartValue(const IVariable* var, double var_value) = 0;
    virtual void AddMipStartValues(const char* sol_file) = 0;
    virtual void ClearMipStartValues() = 0;

    virtual const char* GetCalcUID() const = 0;

    virtual bool IsMip() const = 0;
    virtual bool IsNonlinear() const = 0;

    virtual int GetGeneralConstraintsCount() const = 0;
    virtual IGeneralConstraint* GetGeneralConstaint(int idx) = 0;
    virtual IGeneralConstraint* GetGeneralConstaintByName(const char* name) = 0;
    virtual IGeneralConstraint* AddGeneralConstraint(const IGeneralExpression* constr, const IVariable* var, const char* name) = 0;
    virtual void AddGeneralConstraint(const IGeneralConstraint*) = 0;
    virtual void Remove(IGeneralConstraint* constr) = 0;
};

} // namespace arhiplex
