#include "masstransit.h"

#include "db_helper.h"
#include "object_helpers.h"

#include <maps/libs/log8/include/log8.h>

#include <fstream>

namespace maps::wiki::masstransit {

Masstransit::Masstransit(const Params& params)
    : params(params)
    , failed_(false)
    , dataErrorLog_(params.dataErrorLogFilePath)
{
    mtrObjectHandlers_.push_back(std::make_shared<MtrStopHandler>(*this));
    mtrObjectHandlers_.push_back(std::make_shared<MtrRouteHandler>(*this));
    mtrObjectHandlers_.push_back(std::make_shared<MtrThreadHandler>(*this));
    mtrObjectHandlers_.push_back(std::make_shared<MtrThreadStopsHandler>(*this));
    mtrObjectHandlers_.push_back(std::make_shared<MtrThreadGeometryHandler>(*this));
    mtrObjectHandlers_.push_back(std::make_shared<MtrConnectorHandler>(*this));
    mtrObjectHandlers_.push_back(std::make_shared<MtrConnectorGeometryHandler>(*this));
    mtrObjectHandlers_.push_back(std::make_shared<MtrTransitionHandler>(*this));
    mtrObjectHandlers_.push_back(std::make_shared<MtrLocalizationHandler>(*this));
    mtrObjectHandlers_.push_back(std::make_shared<MtrTravelTimeHandler>(*this));
    mtrObjectHandlers_.push_back(std::make_shared<MtrCalendarHandler>(*this));
    mtrObjectHandlers_.push_back(std::make_shared<MtrRouteAliasHandler>(*this));
    mtrObjectHandlers_.push_back(std::make_shared<MtrStopClosureHandler>(*this));
    mtrObjectHandlers_.push_back(std::make_shared<MtrThreadClosureHandler>(*this));
    mtrObjectHandlers_.push_back(std::make_shared<MtrBoardingHandler>(*this));

    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfOperatorHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfStopHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfRouteHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfThreadHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfThreadStopsHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfBusGeometryHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfTramGeometryHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfMetroGeometryHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfConnectorHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfBusConnectorGeometryHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfTramConnectorGeometryHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfMetroConnectorGeometryHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfTransitionHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfLocalizationHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfCalendarHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfRouteAliasHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfRoadConditionsHandler>(*this));
    ymapsdfObjectHandlers_.push_back(std::make_shared<YmapsdfBoardingHandler>(*this));
}

bool
Masstransit::failed() const
{
    return failed_;
}

void
Masstransit::fail(const std::string& name)
{
    if (!name.empty()) {
        ERROR() << "Failed " << name;
    } else {
        ERROR() << "Failed <unknown>";
    }
    failed_ = true;
}

DataErrorLog&
Masstransit::log()
{
    return dataErrorLog_;
}

void
Masstransit::safeRunner(const ThreadPool::Functor& f, const std::string& name)
{
    if (!name.empty()) {
        INFO() << "Starting " << name;
    }
    try {
        f();
        if (!name.empty()) {
            INFO() << "Finished " << name;
        }
    } catch (const pqxx::failure& ex) {
        ERROR() << "pqxx error: " << ex.what();
        fail("[pqxx] " + name);
    } catch (const maps::Exception& ex) {
        ERROR() << "maps error: " << ex;
        fail("[maps] " + name + " <unknown>");
    } catch (const std::exception& ex) {
        ERROR() << "std error: " << ex.what();
        fail("[std] " + name);
    }
}

void
Masstransit::addSafeTask(
    ThreadPool& taskPool,
    const ThreadPool::Functor& f,
    const std::string& name)
{
    taskPool.push(std::bind(&Masstransit::safeRunner, this, f, name));
}

void
Masstransit::readFromYmapsdf(pgpool3::Pool& ymapsdfPool, const std::string& schemaName)
{
    auto poolState = ymapsdfPool.state();
    auto connectionCount = poolState.constants.slaveMaxSize + poolState.constants.masterMaxSize;

    DBHelper dBHelper(ymapsdfPool, schemaName);

    idMap.initLastDBID(dBHelper.maxId());

    ThreadPool taskPool(std::min(params.threadCount, connectionCount));

    for (auto& objectHandler : ymapsdfObjectHandlers_) {
        auto task = std::bind(&YmapsdfObjectHandler::read, objectHandler, std::ref(dBHelper));
        addSafeTask(taskPool, task, "readFromYmapsdf");
    }
    taskPool.shutdown();

    for (auto& objectHandler : ymapsdfObjectHandlers_) {
        objectHandler->update();
    }
}

void
Masstransit::writeToMtr(const std::string& dirPath)
{
    ThreadPool taskPool(params.threadCount);

    for (auto& objectHandler : mtrObjectHandlers_) {
        auto task = std::bind(&MtrObjectHandler::write, objectHandler, dirPath);
        addSafeTask(taskPool, task, "writeToMtr");
    }
    taskPool.shutdown();
}

} // namespace maps::wiki::masstransit
