/*
 * observer.cpp
 *
 *  Created on: 13.10.2009
 *      Author: tail
 */
#include "observer.h"
#include <maps/wikimap/mapspro/services/editor/src/configs/config.h>
#include <maps/wikimap/mapspro/services/editor/src/objects_cache.h>

#include <maps/libs/log8/include/log8.h>
#include <yandex/maps/wiki/threadutils/threadpool.h>

namespace maps {
namespace wiki {

class AfterCommitContext {
public:
    struct Params {
        const TBranchId branchId;
    };

    AfterCommitContext(
            const ObserverPtr& observer,
            Observer::ContextDataPtr contextData,
            const Params& params)
        : observer_(observer)
        , contextData_(std::move(contextData))
        , params_(params)
    {}

    void operator()() { run(); }

private:
    void run() const
    {
        try {
            observer_->afterCommit(params_.branchId, *contextData_);
        }
        catch (const std::exception& ex) {
            WARN() << BOOST_CURRENT_FUNCTION << " Caught exception: " << ex.what();
        }
    }

    ObserverPtr observer_;
    Observer::ContextDataPtr contextData_;
    Params params_;
};

void
ObserverCollection::add(ObserverPtr pObserver)
{
    observers_.push_back(pObserver);
}

std::list<Observer::ContextDataPtr>
ObserverCollection::beforeCommit(
    ObjectsCache& cache,
    UserContext& userContext,
    const CommitContext& commitContext) const
{

    auto modifiedObjects = cache.find(
        [](const GeoObject* object) {
            return object->isModified();
        });

    std::list<Observer::ContextDataPtr> dataPtrList;
    for (const auto& observer : observers_) {
        dataPtrList.push_back(
            observer->beforeCommit(cache, modifiedObjects, userContext, commitContext));
    }
    return dataPtrList;
}
void
ObserverCollection::afterCommit(std::list<Observer::ContextDataPtr>& dataPtrList, TBranchId branchId) const
{
    AfterCommitContext::Params params{branchId};
    for (const auto& observer : observers_) {
        Observer::ContextDataPtr dataPtr = dataPtrList.front();
        dataPtrList.pop_front();
        if (!dataPtr) {
            continue;
        }
        auto poolType = observer->afterCommitPoolType();
        if (poolType) {
            AfterCommitContext ctx(observer, dataPtr, params);
            auto& threadPool = cfg()->afterCommitThreadPool(*poolType);
            if (threadPool.push(ctx)) {
                continue;
            }
        }
        observer->afterCommit(branchId, *dataPtr);
    }
}

Token
ObserverCollection::doCommit(
    ObjectsCache& cache,
    UserContext& userContext,
    const CommitContext& commitContext) const
{
    const auto branchId = cache.branchContext().branch.id();
    auto dataPtrList = beforeCommit(cache, userContext, commitContext);
    auto token = cache.commit();
    afterCommit(dataPtrList, branchId);
    return token;
}

} // namespace wiki
} // namespace maps
