#include "action/ActionManager.h"
#include "IllegalArgumentException.h"
#include "IllegalStateException.h"

ActionManager::ActionManager(ActionDirectoryPtr actionDirectory)
{
	actionIdGenerator = ActionIdGeneratorPtr(new ActionIdGenerator());

	if(actionDirectory == 0)
	{        
    }
    this->actionDirectory = actionDirectory;
}

ActionIdentifierPtr ActionManager::initActions(std::list<BatchEntryPtr>  entries, ActionContextPtr batchActionContext)
{
	if(batchActionContext == 0)
	{
        return ActionIdentifierPtr();
	}

	RequestCachePtr requestCache = RequestCachePtr(new RequestCache());
	BatchContextPtr batchContext = BatchContextPtr(new BatchContext());


	std::list<BatchEntryPtr>::const_iterator  BatchEntryIt;

    for (BatchEntryIt = entries.begin(); BatchEntryIt!= entries.end(); ++BatchEntryIt)
    {
        BatchEntryPtr entry = BatchEntryPtr(*BatchEntryIt);
        if(entry == 0)
        {
            throw IllegalArgumentException("Entries list contains nulls");
        }

        ActionContextPtr actionContext = entry->getActionContext();
        actionContext->setBatchContext(batchContext);
        actionContext->setRequestCache(requestCache);
        batchContext->addBatchEntry(entry);

    }
    batchActionContext->setBatchContext(batchContext);
    batchActionContext->setRequestCache(requestCache);

    BatchActionPtr batchAction = BatchActionPtr(new BatchAction(ActionManagerPtr(this)));
    batchAction->init(batchActionContext, InitialRequestPtr());
    batchActionContext->setActionState((unsigned char)ActionContext::INITIALIZED);
    return registerAction(batchActionContext, batchAction, ActionExecutionModePtr(new ActionExecutionMode(ActionExecutionMode::BATCH())));

    return ActionIdentifierPtr();
}

ActionIdentifierPtr ActionManager::initAction(
    ActionContextPtr actionContext,
    InitialRequestPtr initialParameters,
    ActionExecutionModePtr mode)
{
    ActionDefinitionPtr actionDefinition = actionContext->getActionDefinition();

    ActionPtr action = instantiateAction(actionDefinition);
    actionContext->setActionState(ActionContext::CREATED);
    action->init(actionContext, initialParameters);
    actionContext->setActionState(ActionContext::INITIALIZED);

    return registerAction(actionContext, action, mode);
}


ActionPtr ActionManager::instantiateAction(ActionDefinitionPtr actionDefinition)
{
    if(actionDefinition == 0)
    {
        return ActionPtr();
    }
    return actionDefinition->instantiate();
}

ActionCommandPtr ActionManager::service(ActionIdentifierPtr actionId, ActionResponsePtr actionRequest)
{
    if(actionId == 0) {
        return ActionCommandPtr();
    }

    ActionPtr action = actions[actionId];
    if(action == 0)
    {
        throw IllegalStateException(AgString("Action with id '") + actionId->toString() + "' doesn't exists");
    }
    ActionContextPtr actionContext = actionContexts[action];

    if(actionRequest == 0 && actionContext->getActionState() != ActionContext::INITIALIZED)
    {
        throw IllegalArgumentException("Null actionRequest is allowed only within first call to service()");
    }

    actionContext->setActionState(ActionContext::WORKING);
    ActionCommandPtr actionCommand = ActionCommandPtr();
    ActionResponsePtr activeRequest = actionRequest;
    do
    {
        RequestCachePtr requestCache = actionContext->getRequestCache();

        if((activeRequest != 0) && (activeRequest->getRequestId() != 0) && (activeRequest->shouldRemember()==true))
        {
            if(requestCache == 0)
            {
              RequestCachePtr  requestCache = RequestCachePtr(new RequestCache());
              actionContext->setRequestCache(requestCache);
            }
           requestCache->addRequest(activeRequest->getRequestId(), activeRequest);
        }

        actionCommand = action->service(activeRequest);

        if ( (requestCache != 0) && (actionCommand != 0) && (actionCommand->getRequestId() != 0) &&
                (std::find(actionContext->getRequestedIds().begin(),
                           actionContext->getRequestedIds().end(),
                           actionCommand->getRequestId()) == actionContext->getRequestedIds().end()) )
        {
            activeRequest = requestCache->getRequest(actionCommand->getRequestId());
        }
        else
        {
            activeRequest = ActionResponsePtr();
        }

        if((actionCommand != 0) && (actionCommand->getRequestId() != 0))
        {
            actionContext->getRequestedIds().push_back(actionCommand->getRequestId());
        }
    }

    while (activeRequest != 0);

    return actionCommand;
}

ActionResultPtr ActionManager::destroyAction(ActionIdentifierPtr actionId)
{
    if(actionId == 0)
    {
    }

    ActionPtr action = actions[actionId];

    if(action == 0)
    {
        return ActionResultPtr();
    }

    try
    {
        actionContexts[action]->setActionState(ActionContext::DESTROYED);
        return action->destroy();
    }catch(...) {
        actions.erase(actionId);
        actionContexts.erase(action);
    }

    return  ActionResultPtr();
}

void ActionManager::destroyAll()
{
}

ActionContextPtr ActionManager::getActionContext(ActionIdentifierPtr actionId)
{
    ActionPtr action =  actions[actionId];
    return actionContexts[action];
}

ActionDirectoryPtr ActionManager::getActionDirectory()
{
    return actionDirectory;
}

ActionIdentifierPtr ActionManager::registerAction(ActionContextPtr actionContext, ActionPtr action, ActionExecutionModePtr mode)
{
    UNUSED(mode);
    ActionIdentifierPtr actionId = actionIdGenerator->generate(action);
    actions[actionId]= action;
    actionContexts[action]= actionContext;

    return actionId;
}


