#pragma once

#include "util/Pointers.h"
#include <string>
#include <map>
#include <boost/shared_ptr.hpp>
#include "util/simpleobject/AgString.h"

class ReferenceResolver;
class EnvironmentReferenceResolver;
class Function;

class Evaluator
{
private:
    static AgString ENVIRONMENT_PREVIOUS_() { return "previous"; }
    static AgString ENVIRONMENT_COUNT_() { return "count"; }
    boost::shared_ptr<EnvironmentReferenceResolver> environmentResolver;
    std::map<AgString, boost::shared_ptr<ReferenceResolver> > resolvers;
    std::map<AgString, boost::shared_ptr<Function> > customFunctions;
    boost::shared_ptr<void> previousResult;
    int64_t count;

protected:
    void init_(ReferenceResolverPtr defaultResolver);
    void ctor(ContextManager* cm, Context* defaultContext, DataTablePtr defaultTable, CallerControllerPtr caller);

public:
    AgObjectPtr evaluate(ExpressionPtr expression);
    AgObjectPtr evaluate(ExpressionPtr expression, EvaluationEnvironmentPtr environment);
    AgString evaluateToString(boost::shared_ptr<Expression> expression);
    AgString evaluateToStringOrNull(boost::shared_ptr<Expression> expression);
    bool evaluateToBoolean(boost::shared_ptr<Expression> expression);
    bool evaluateToBooleanOrNull(boost::shared_ptr<Expression> expression);
    int evaluateToNumber(boost::shared_ptr<Expression> expression);
    int evaluateToNumber(boost::shared_ptr<Expression> expression, bool validate, bool allowNull);
    boost::shared_ptr<ReferenceResolver> getResolver(const AgString & schema);
    boost::shared_ptr<ReferenceResolver> getDefaultResolver();
    void setResolver(const AgString & schema, boost::shared_ptr<ReferenceResolver> resolver);
    std::map<AgString, boost::shared_ptr<ReferenceResolver> > getResolvers();
    boost::shared_ptr<void> getPreviousResult();
    void setPreviousResult(boost::shared_ptr<void> previousResult);
    void registerCustomFunction(const AgString& name, boost::shared_ptr<Function> impl);
    std::map<AgString, boost::shared_ptr<Function> > getAllFunctions();
    boost::shared_ptr<Function> getCustomFunction(const AgString& name);
    boost::shared_ptr<EnvironmentReferenceResolver> getEnvironmentResolver();

    Evaluator(ContextManager* cm, boost::shared_ptr<CallerController> caller);
    Evaluator(boost::shared_ptr<DataTable> defaultTable);
    Evaluator(ContextManager* cm, Context* defaultContext,
              boost::shared_ptr<DataTable> defaultTable, boost::shared_ptr<CallerController> caller);
    Evaluator(boost::shared_ptr<ReferenceResolver> resolver);
};
