#include "binding/BindingReferenceListener.h"

#include "binding/ReferenceListener.h"
#include "binding/EvaluationOptions.h"
#include "expression/EvaluationEnvironment.h"
#include "expression/Evaluator.h"

BindingReferenceListener::BindingReferenceListener(BindingPtr binding, EvaluationOptionsPtr options,
                                                   DefaultBindingProcessor* ownerBinding)
{
    this->binding = binding;
    this->options = options;
    this->owner = ownerBinding;
}

void BindingReferenceListener::referenceChanged(
        ReferencePtr cause,
        const std::map<AgString, AgObjectPtr>& environment,
        ChangeCachePtr cache)
{
    referenceChanged(cause, environment, cache, false);
}

void BindingReferenceListener::referenceChanged(
        ReferencePtr cause,
        const std::map<AgString, AgObjectPtr>& environment,
        ChangeCachePtr cache,
        bool asynchronousProcessing)
{
    if (owner->stopped || !owner->enabled) {
        return;
    }

    bool async = owner->executionService != NULL && asynchronousProcessing;

    if (async)
    {
        throw AggreGateException("async mode not implemented in BindingReferenceListener");
    }
    else
    {
        processReferenceChange(cause, environment, cache);
    }
}

void BindingReferenceListener::processReferenceChange(ReferencePtr cause,
        const std::map<AgString, AgObjectPtr> &environment,
        ChangeCachePtr cache)
{
    if (owner->stopped || !owner->enabled) {
        return;
    }

    EvaluationEnvironmentPtr evaluationEnvironment = EvaluationEnvironmentPtr(new EvaluationEnvironment(cause, environment));
    if (owner->checkCondition(options, evaluationEnvironment))
    {
        try
        {
            AgObjectPtr result = owner->evaluator->evaluate(binding->getExpression(), evaluationEnvironment);
            owner->provider->processExecution(EvaluationOptions::EVENT, binding, options, cause, result);
            owner->writeReference(EvaluationOptions::EVENT, binding->getReference(), cause, result, cache);
        }
        catch (AggreGateException ex)
        {
            throw BindingException(ex.getMessage(), ex.getDetails());
        }
    }
}

BindingPtr BindingReferenceListener::getBinding()
{
    return binding;
}

void BindingReferenceListener::setContent(AgObjectPtr content)
{
    this->content = content;
}

AgObjectPtr BindingReferenceListener::getContent()
{
    return content;
}

BindingProcessor* BindingReferenceListener::getBindingProcessor()
{
    return owner;
}

EvaluationOptionsPtr BindingReferenceListener::getEvaluationOptions()
{
    return options;
}

AgString BindingReferenceListener::toString()
{
    return "[binding: " + binding->toString() + ", options: " + options->toString() + "]";
}
