#include <vector>
#include <iomanip>
#include <fstream>
#include <iterator>

#pragma GCC diagnostic ignored "-Wempty-body"
#include <util/generic/hash.h>
#pragma GCC diagnostic warning "-Wempty-body"

#include "application.h"

#include <Poco/Data/MySQL/Connector.h>
#include <Poco/Data/Session.h>

#include <wmconsole/legacy/util/logutil.h>
#include <wmconsole/legacy/util/host_db_hash.h>
#include <wmconsole/legacy/util/mysql_url.h>
#include <wmconsole/legacy/util/session.h>

using std::string;
using std::copy;
using std::vector;
using std::ostream_iterator;
using std::cout;
using std::cin;
using std::cerr;
using std::endl;
using std::ifstream;
using std::istream;

using Poco::trim;
using Poco::trimInPlace;
using Poco::toLower;

namespace NWebmaster {
const string DBWorkApplication::OP_INPUT_FILE = "input-file"; // i
}

int NWebmaster::DBWorkApplication::doMain(const vector<string> & /*args*/) {
    Poco::Data::MySQL::Connector::registerConnector();

    // DB options
    std::string userDbUrl = getUserDbUrl();
    WMCLOG_INFO(logger(), "User DB url = " << userDbUrl);
    NWebmaster::MySQLUrl userDb(userDbUrl);
    std::string userDbConnectionString = userDb.getConnectionString();

    std::vector<std::string> hostDbUrls = getHostDbUrls();
    std::vector<std::string> hostDbConnectionStrings;

    for (size_t i = 0; i < hostDbUrls.size(); i++) {
        WMCLOG_INFO(logger(), "Host DB url [" << i << "] = " << hostDbUrls[i]);
        NWebmaster::MySQLUrl dbUrl(hostDbUrls[i]);
        hostDbConnectionStrings.push_back(dbUrl.getConnectionString());
    }

    WMCSession wmcSession(Poco::Data::MySQL::Connector::KEY, userDbConnectionString, hostDbConnectionStrings);
    WMCLOG_INFO(logger(), "WMCSession created");

    // create map: DB index --> Process
    THashMap<int, Poco::SharedPtr<DBProcessor> > db2processor;
    Iterator it = wmcSession.getHostDbIterator();

    for (int dbIndex = it.get(); it.next(); dbIndex = it.get()) {
        db2processor[dbIndex] = createDBProcessor(wmcSession.getHostSession(dbIndex));
        WMCLOG_INFO(logger(), "Process for db #" << dbIndex << " created");
    }

    ifstream fin;
    istream *in = &cin;

    if (config().hasOption(OP_INPUT_FILE)) {
        fin.open(config().getString(OP_INPUT_FILE).c_str(), ifstream::in);
        in = &fin;
    }

    if (in->fail()) {
        WMCLOG_ERROR(logger(), "Cannot read input file!");
        cerr << "Cannot open input file!" << endl;
        return 2;
    }

    WMCLOG_INFO(logger(), "Start reading input file");
    int count = 0;

    string line;
    while (getline(*in, line)) {
        if (line.size() == 0 || line[0] == '#') {
            continue;
        }

        string key;
        string value;

        std::string::size_type tabPos = line.find_first_of('\t');
        if (tabPos != std::string::npos) {
            key = line.substr(0, tabPos);
            value = line.substr(tabPos + 1);
        } else {
            key = line;
        }

        int dbIndex = getHostDBHash(key.c_str())% wmcSession.getHostDbCount();
        WMCLOG_INFO(logger(), dbIndex);
        db2processor[dbIndex]->process(key, value);

        count++;
    }

    WMCLOG_INFO(logger(), "Done [" << count << " lines processed]");
    return Application::EXIT_OK;
}
