/**
 * DESCRIPTION OF THE FILE
 *
 * @author Michal KravĨenko
 * @date 4.9.18 -
 */

#include <boost/serialization/export.hpp>

#include "exprtk.hpp"
#include "ExprtkWrapper.h"
#include "ExprtkWrapperSerialization.h"
#include "exceptions.h"
#include <string>

BOOST_CLASS_EXPORT_IMPLEMENT(ExprtkWrapper);

ExprtkWrapper::ExprtkWrapper(std::string expression_string) {

    this->p_impl = new ExprtkWrapperImpl();

    this->p_impl->expression_str = expression_string;

    this->p_impl->symbol_table = new symbol_table_t();

    this->p_impl->symbol_table->add_variable("x",
                                             this->p_impl->x);
    this->p_impl->symbol_table->add_variable("y",
                                             this->p_impl->y);
    this->p_impl->symbol_table->add_variable("z",
                                             this->p_impl->z);
    this->p_impl->symbol_table->add_variable("t",
                                             this->p_impl->t);
    this->p_impl->symbol_table->add_variable("f",
                                             this->p_impl->z);

    this->p_impl->expression = new expression_t();
    this->p_impl->expression->register_symbol_table(*this->p_impl->symbol_table);

    this->p_impl->parser = new parser_t();
    this->p_impl->parser->compile(this->p_impl->expression_str,
                                  *this->p_impl->expression);
}

ExprtkWrapper::~ExprtkWrapper() {

    if (this->p_impl->expression) {
        delete this->p_impl->expression;
        this->p_impl->expression = nullptr;
    }

    if (this->p_impl->symbol_table) {
        delete this->p_impl->symbol_table;
        this->p_impl->symbol_table = nullptr;
    }

    if (this->p_impl->parser) {
        delete this->p_impl->parser;
        this->p_impl->parser = nullptr;
    }

    delete this->p_impl;
    this->p_impl = nullptr;

}

double ExprtkWrapper::eval(double x1,
                           double x2,
                           double x3,
                           double x4) {

    this->p_impl->x = x1;
    this->p_impl->y = x2;
    this->p_impl->z = x3;
    this->p_impl->t = x4;

    return this->p_impl->expression->value();

}

double ExprtkWrapper::eval(std::vector<double>& p) {


    if (p.size() > 0) {
        this->p_impl->x = p[0];
    }
    if (p.size() > 1) {
        this->p_impl->y = p[1];
    }
    if (p.size() > 2) {
        this->p_impl->z = p[2];
    }
    if (p.size() > 3) {
        this->p_impl->t = p[3];
    }

    double result = this->p_impl->expression->value();

    return result;
}