#include "binding/DefaultBindingProcessor.h"
#include "binding/BindingReferenceListener.h"
#include "expression/Evaluator.h"
#include "expression/ExpressionUtils.h"
#include "util/Log.h"
#include "IllegalStateException.h"
#include <iterator>

DefaultBindingProcessor::DefaultBindingProcessor(BindingProviderPtr provider, EvaluatorPtr evaluator)
{
    this->disableStartupConcurrency = false;
    this->shareConcurrency = false;
    this->stopped = false;
    this->enabled = false;
    this->provider = provider;
    this->evaluator = evaluator;
}

bool DefaultBindingProcessor::start()
{
    start(executionService != 0 && !disableStartupConcurrency);
    return !stopped;
}

void DefaultBindingProcessor::start(bool concurrentProcessing)
{
    if (stopped)
    {
        throw IllegalStateException("Cannot reuse binding processor");
    }


    if (!concurrentProcessing) {
        try {
            startImpl(concurrentProcessing);
        }catch (AggreGateException ex) {
            throw IllegalStateException(ex.getMessage(), ex.getDetails());
        }
    }else {
        throw AggreGateException("concurrentProcessing not implemented in DefaultBindingProcessor");
    }
}

void DefaultBindingProcessor::startImpl(bool concurrentProcessing)
{
    std::map<BindingPtr, EvaluationOptionsPtr> bindings = provider->createBindings();

    if (executionService != NULL && concurrentProcessing)
    {
        throw AggreGateException("concurrentProcessing not implemented in DefaultBindingProcessor");
    }
    else
    {
        for (std::map<BindingPtr, EvaluationOptionsPtr>::iterator it=bindings.begin(); it!=bindings.end(); ++it)
        {
            initBinding(it->first, it->second);
        }
    }

    if (stopped)
    {
        return;
    }

    provider->start();
}

void DefaultBindingProcessor::stop()
{
    stopped = true;

    if (shareConcurrency)
    {
         throw AggreGateException("concurrentProcessing not implemented in DefaultBindingProcessor");
    }
    else
    {
        if (executionService != NULL)
        {
        }
    }

    for (std::list<ReferenceListenerPtr>::iterator it=listeners.begin(); it!=listeners.end(); ++it){
        provider->removeReferenceListener(*it);
    }

    provider->stop();
}

void DefaultBindingProcessor::initBinding(BindingPtr binding, EvaluationOptionsPtr options)
{
    try {
        if (stopped) {
            return;
        }

        LOG_BINDINGS_DEBUG("Adding binding: " + binding->toString().toUtf8()+", evaluation options: " + options->toString().toUtf8());

        if (enabled && (options->getPattern() & EvaluationOptions::STARTUP) != 0) {
            try{
                if (checkCondition(options, EvaluationEnvironmentPtr())) {
                    AgObjectPtr result = evaluator->evaluate(binding->getExpression(), EvaluationEnvironmentPtr());
                    provider->processExecution(EvaluationOptions::STARTUP, binding, options, ReferencePtr(), result);
                    writeReference(EvaluationOptions::STARTUP, binding->getReference(), ReferencePtr(), result, ChangeCachePtr());
                }else {
                     LOG_BINDINGS_DEBUG("Condition '" + options->getCondition()->toString().toUtf8() + "' is false for binding: " + binding->toString().toUtf8());
                }
            }catch (AggreGateException ex) {
                provider->processError(binding, EvaluationOptions::STARTUP, ReferencePtr(), ex);
            }
        }

        if ((options->getPattern() & EvaluationOptions::EVENT) != 0) {
            if (options->getActivator() != NULL) {
                try {
                    addReferenceListener(binding, options, options->getActivator());
                }catch (AggreGateException ex) {
                    provider->processError(binding, EvaluationOptions::EVENT, options->getActivator(), ex);
                }
            }
        }

    }catch (AggreGateException ex) {
         LOG_BINDINGS_DEBUG("Error initializing binding: " + binding->toString().toUtf8());
    }
}

void DefaultBindingProcessor::addReferenceListener(BindingPtr binding, EvaluationOptionsPtr options, ReferencePtr reference) /* throws(BindingException) */
{
    if (stopped) {
        return;
    }

    ReferenceListenerPtr listener = ReferenceListenerPtr(new BindingReferenceListener(binding, options, this));
    provider->addReferenceListener(reference, listener);
    listeners.push_back(listener);
}

void DefaultBindingProcessor::writeReference(int method, ReferencePtr destination,
                                             ReferencePtr cause, AgObjectPtr value, ChangeCachePtr cache) /* throws(BindingException) */
{
    if (stopped || !enabled) {
        return;
    }
    provider->writeReference(method, destination, cause, value, cache);
}

bool DefaultBindingProcessor::checkCondition(EvaluationOptionsPtr options, EvaluationEnvironmentPtr evaluationEnvironment) /* throws(BindingException) */
{
    try {
        bool const conditionIsAbsent = options == 0 || options->getCondition() == 0
                || options->getCondition()->getText().empty();
        if (conditionIsAbsent) {
            return true;
        }

        AgObjectPtr condition = evaluator->evaluate(options->getCondition(), evaluationEnvironment);
        return (condition != 0) ? (Util::convertToBoolean(condition, true, false)) : true;
    } catch (AggreGateException ex) {
        throw BindingException(ex.getMessage(), ex.getDetails());
    }
    return false;
}

bool DefaultBindingProcessor::isStopped()
{
    return stopped;
}

void DefaultBindingProcessor::submit(Callable<int>* task)
{
    UNUSED(task);
    //not used
    assert(0);
}

void DefaultBindingProcessor::setExecutionService(ThreadPoolCachedExPtr service)
{
    UNUSED(service);
    // not used
    assert(0);    
}

ThreadPoolCachedExPtr DefaultBindingProcessor::getExecutorService()
{
    return executionService;
}

void DefaultBindingProcessor::setEnabled(bool enabled)
{
    this->enabled = enabled;
}

BindingProviderPtr DefaultBindingProcessor::getProvider()
{
    return provider;
}

EvaluatorPtr DefaultBindingProcessor::getEvaluator()
{
    return evaluator;
}

bool DefaultBindingProcessor::isDisableStartupConcurrency()
{
    return disableStartupConcurrency;
}

void DefaultBindingProcessor::setDisableStartupConcurrency(bool disableStartupConcurrency)
{
    this->disableStartupConcurrency = disableStartupConcurrency;
}


