//! \~\file pievaluator.h
//! \~\ingroup Math
//! \~\brief
//! \~english Mathematical expression evaluator
//! \~russian Вычислитель математических выражений
//! \~\details
//! \~english
//! Declares evaluator types, expression storage, and the evaluator that
//! prepares an expression once and then reuses compiled instructions.
//! \~russian
//! Объявляет типы вычислителя, контейнер выражения и вычислитель,
//! который один раз подготавливает выражение и затем повторно использует
//! скомпилированные инструкции.
/*
    PIP - Platform Independent Primitives
    Evaluator designed for stream calculations
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PIEVALUATOR_H
#define PIEVALUATOR_H

#include "pimathcomplex.h"
#include "pistringlist.h"


namespace PIEvaluatorTypes {

typedef std::function<complexd(void *, int, complexd *)> FuncHanlder;

enum eType {
	etNumber,
	etOperator,
	etVariable,
	etFunction
};

enum Operation {
	oNone,
	oAdd,
	oSubtract,
	oMultiply,
	oDivide,
	oResidue,
	oPower,
	oEqual,
	oNotEqual,
	oGreater,
	oSmaller,
	oGreaterEqual,
	oSmallerEqual,
	oAnd,
	oOr,
	oFunction
};

enum BaseFunctions {
	bfUnknown,
	bfSin,
	bfCos,
	bfTg,
	bfCtg,
	bfArcsin,
	bfArccos,
	bfArctg,
	bfArcctg,
	bfExp,
	bfRandom,
	bfRandomn,
	bfSh,
	bfCh,
	bfTh,
	bfCth,
	bfSqrt,
	bfSqr,
	bfPow,
	bfAbs,
	bfLn,
	bfLg,
	bfLog,
	bfSign,
	bfIm,
	bfRe,
	bfArg,
	bfLen,
	bfConj,
	bfRad,
	bfDeg,
	bfJ0,
	bfJ1,
	bfJN,
	bfY0,
	bfY1,
	bfYN,
	bfMin,
	bfMax,
	bfClamp,
	bfStep,
	bfMix,
	bfDefined,
	bfRound,
	bfCustom = 0xFFFF
};

struct PIP_EXPORT Instruction {
	Instruction() {
		out       = -1;
		function  = -1;
		operation = oNone;
	}

	Instruction(Operation oper, PIVector<short> opers, short out_ind, short func = -1) {
		operation = oper;
		operators = opers;
		out       = out_ind;
		function  = func;
	}

	Operation operation;
	short out;
	short function;
	PIVector<short> operators;
};


struct PIP_EXPORT Element {
	Element() {
		num     = 0;
		var_num = -1;
		type    = etNumber;
	}

	Element(eType new_type, short new_num, short new_var_num = -1) { set(new_type, new_num, new_var_num); }

	void set(eType new_type, short new_num, short new_var_num = -1) {
		type    = new_type;
		num     = new_num;
		var_num = new_var_num;
	}

	eType type;
	short num;
	short var_num;
};


struct PIP_EXPORT Function {
	Function() {
		arguments = 0;
		type      = bfUnknown;
		handler   = nullptr;
	}

	Function(const PIString & name, short args, BaseFunctions ftype) {
		identifier = name;
		arguments  = args;
		type       = ftype;
		handler    = nullptr;
	}

	Function(const PIString & name, short args, FuncHanlder h) {
		identifier = name;
		arguments  = args;
		type       = bfCustom;
		handler    = std::move(h);
	}

	PIString identifier;
	BaseFunctions type;
	FuncHanlder handler;
	short arguments;
};


struct PIP_EXPORT Variable {
	Variable() { value = 0.; }

	Variable(const PIString & var_name, complexd val) {
		name  = var_name;
		value = val;
	}

	PIString name;
	complexd value;
};

} // namespace PIEvaluatorTypes

/*
    ≠	:
    ≥	}
    ≤	{
    ⋀	&
    ⋁	|
*/
class PIP_EXPORT PIEvaluatorContent {
	friend class PIEvaluator;
	BINARY_STREAM_FRIEND(PIEvaluatorContent);

public:
	PIEvaluatorContent();
	~PIEvaluatorContent() { ; }

	void addFunction(const PIString & name, int args = 1);
	int addVariable(const PIString & name, const complexd & val = 0.);
	void addCustomFunction(const PIString & name, int args_count, PIEvaluatorTypes::FuncHanlder func);
	int functionsCount() const { return functions.size(); }
	int variablesCount() const { return variables.size(); }
	int customVariablesCount() const;
	int findFunction(const PIString & name) const;
	int findVariable(const PIString & var_name) const;
	PIEvaluatorTypes::Function function(int index);
	PIEvaluatorTypes::Variable variable(int index);
	PIEvaluatorTypes::Function function(const PIString & name) { return function(findFunction(name)); }
	PIEvaluatorTypes::Variable variable(const PIString & name) { return variable(findVariable(name)); }
	PIEvaluatorTypes::Variable customVariable(int index);
	bool setVariableValue(int index, complexd new_value);
	bool setVariableValue(const PIString & var_name, const complexd & new_value) {
		return setVariableValue(findVariable(var_name), new_value);
	}
	bool setVariableName(int index, const PIString & new_name);
	bool setVariableName(const PIString & var_name, const PIString & new_name) { return setVariableName(findVariable(var_name), new_name); }
	void clearCustomVariables();
	PIEvaluatorTypes::BaseFunctions getBaseFunction(const PIString & name);
	void dump();

private:
	PIVector<PIEvaluatorTypes::Function> functions;
	PIVector<PIEvaluatorTypes::Variable> variables;
	int cv_count;
};


//! \~\ingroup Math
//! \~\brief
//! \~english Expression evaluator with reusable compiled instructions.
//! \~russian Вычислитель выражений с повторно используемыми скомпилированными инструкциями.
//!
//! \~\details
//! \~english
//! The evaluator prepares an expression with \a check(), keeps the processed
//! form and instruction list, and then reevaluates it after variable updates.
//! Built-in constants include \c i, \c pi, and \c e.
//! \~russian
//! Вычислитель подготавливает выражение через \a check(), сохраняет
//! обработанную форму и список инструкций, а затем повторно вычисляет его
//! после обновления переменных. Встроенные константы: \c i, \c pi и \c e.
class PIP_EXPORT PIEvaluator {
public:
	//! \~english Constructs an empty evaluator.
	//! \~russian Создает пустой вычислитель.
	PIEvaluator() {
		correct = false;
		data_   = 0;
	}

	//! \~english Destroys the evaluator.
	//! \~russian Уничтожает вычислитель.
	~PIEvaluator() { ; }


	//! \~english Returns custom user data passed to callback functions.
	//! \~russian Возвращает пользовательские данные, передаваемые в callback-функции.
	void * data() { return data_; }

	//! \~english Sets custom user data for callback functions.
	//! \~russian Устанавливает пользовательские данные для callback-функций.
	void setData(void * _data) { data_ = _data; }


	//! \~english Checks and compiles an expression.
	//! \~russian Проверяет и компилирует выражение.
	bool check(const PIString & string);

	//! \~english Returns true if the last \a check() succeeded.
	//! \~russian Возвращает true, если последний вызов \a check() завершился успешно.
	bool isCorrect() const { return correct; }

	//! \~english Sets a named variable and creates it if needed.
	//! \~russian Устанавливает именованную переменную и создает ее при необходимости.
	int setVariable(const PIString & name, complexd value = complexd(0.));

	//! \~english Sets a variable by index.
	//! \~russian Устанавливает переменную по индексу.
	void setVariable(int index, complexd value = 0.);

	//! \~english Evaluates the last successfully compiled expression.
	//! \~russian Вычисляет последнее успешно скомпилированное выражение.
	complexd evaluate();

	//! \~english Removes user-added variables and keeps built-in constants.
	//! \~russian Удаляет добавленные пользователем переменные и сохраняет встроенные константы.
	void clearCustomVariables() { content.clearCustomVariables(); }

	//! \~english Returns variable index by name, or -1.
	//! \~russian Возвращает индекс переменной по имени или -1.
	int variableIndex(const PIString & name) const { return content.findVariable(name); }

	//! \~english Returns unknown variables found during the last \a check().
	//! \~russian Возвращает неизвестные переменные, найденные при последнем \a check().
	const PIStringList & unknownVariables() const { return unknownVars; }

	//! \~english Returns variables used in the last checked expression.
	//! \~russian Возвращает переменные, использованные в последнем проверенном выражении.
	const PIStringList & usedVariables() const { return usedVars; }

	//! \~english Returns the normalized form of the last checked expression.
	//! \~russian Возвращает нормализованную форму последнего проверенного выражения.
	const PIString & expression() const { return currentString; }

	//! \~english Returns the last status or error text from \a check().
	//! \~russian Возвращает последний статус или текст ошибки из \a check().
	const PIString & error() const { return lastError; }

	//! \~english Returns the last evaluation result.
	//! \~russian Возвращает последний результат вычисления.
	const complexd & lastResult() const { return out; }

	//! \~english Serializes evaluator state.
	//! \~russian Сериализует состояние вычислителя.
	PIByteArray save() const;

	//! \~english Restores evaluator state from serialized data.
	//! \~russian Восстанавливает состояние вычислителя из сериализованных данных.
	void load(PIByteArray ba);

private:
	const PIString & prepare(const PIString & string);
	PIString preprocess(const PIString & string);
	int parse(const PIString & string, int offset = 0);
	void convert();
	void checkBrackets();
	void removeSpaces();
	void findUnknownVariables();
	void removeJunk();
	void replaceOperators();
	void makeOutput(PIString & string);
	bool fillElements();
	bool setSignes();
	bool isSign(const PIChar & ch);
	PIString inverse(const PIString & string) {
		int len = string.length();
		PIString s;
		for (int i = 0; i < len; i++)
			s += string[len - i - 1];
		return s;
	}
	bool check();
	bool execInstructions();
	PIString inBrackets(const PIString & string);
	PIString operationChar(const PIEvaluatorTypes::Operation & operation);
	void operationsByPriority(int p, PIVector<PIEvaluatorTypes::Operation> & ret);
	complexd value(const int & index) {
		if (index < 0)
			return tmpvars[-index - 1].value;
		else
			return content.variables[index].value;
	}
	inline complexd residue(const complexd & f, const complexd & s);
	inline void execFunction(const PIEvaluatorTypes::Instruction & ci);

	PIEvaluatorContent content;
	PIDeque<PIEvaluatorTypes::Element> elements;
	PIVector<PIEvaluatorTypes::Variable> currentVariables, variables, tmpvars;
	PIVector<PIEvaluatorTypes::Instruction> instructions;
	PIStringList unknownVars, usedVars;
	PIString currentString, lastError;
	complexd out;
	bool correct;
	void * data_;
};

inline bool operator==(PIEvaluatorTypes::Element e1, PIEvaluatorTypes::Element e2) {
	return (e1.type == e2.type && e1.num == e2.num);
}
inline bool operator!=(PIEvaluatorTypes::Element e1, PIEvaluatorTypes::Element e2) {
	return (e1.type != e2.type || e1.num != e2.num);
}

BINARY_STREAM_WRITE(PIEvaluatorTypes::Instruction) {
	s << PIMemoryBlock(&v, sizeof(v) - sizeof(v.operators)) << v.operators;
	return s;
}
BINARY_STREAM_READ(PIEvaluatorTypes::Instruction) {
	s >> PIMemoryBlock(&v, sizeof(v) - sizeof(v.operators)) >> v.operators;
	return s;
}

BINARY_STREAM_WRITE(PIEvaluatorTypes::Element) {
	s << createMemoryBlock(&v);
	return s;
}
BINARY_STREAM_READ(PIEvaluatorTypes::Element) {
	s >> createMemoryBlock(&v);
	return s;
}

BINARY_STREAM_WRITE(PIEvaluatorTypes::Variable) {
	s << v.name << v.value;
	return s;
}
BINARY_STREAM_READ(PIEvaluatorTypes::Variable) {
	s >> v.name >> v.value;
	return s;
}

BINARY_STREAM_WRITE(PIEvaluatorContent) {
	s << v.variables << v.cv_count;
	return s;
}
BINARY_STREAM_READ(PIEvaluatorContent) {
	s >> v.variables >> v.cv_count;
	return s;
}

#endif // PIEVALUATOR_H
