#include <fstream>
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>

#include <productiondatabaseclient.h>
#include <boost/program_options.hpp>

using namespace std;
using namespace ProductionDatabase;
namespace po = boost::program_options;

static string fileName = "serials.dat";
static int boxNo = 0;
static bool debug = false;
static string username = "";
static string password = "";

static vector<string> apdSerials;
static std::vector<int> boxes;
static std::vector<int> positions;

void processArgumentsAndQueryMissing(int m_argc, char* m_argv[]);
void loadSerialsFromFileName(string m_fileName, bool m_debug = false);

int main(int argc, char* argv[]) {
    ProductionDatabaseClient *proddb = new ProductionDatabaseClient();
    processArgumentsAndQueryMissing(argc, argv);

    if (debug) cout << "Intitialized apdBoxSetter with version " << proddb->getVersion() << " of the database access libraries." << endl << endl
                    << "Now trying to set Batch number " << boxNo << " for ADPs from serial file " << fileName << endl;

    loadSerialsFromFileName(fileName);

    if (username == "" || password == "") proddb->queryCredentials();
    else proddb->setCredentials(username, password);
    DatabaseClientResponse response = proddb->checkConnectivityAndCredentials();
    if (response != Successful ) {
        cerr << "Connection to database failed because of error: " << response << endl;
        return response;
    }
    if (debug) cout << "Connection successful!" << endl;

    proddb->storeApdBoxNumber(apdSerials,boxes,positions);

    cout << "\nBox " << boxNo << " was successfully assigned to APDs from serial file " << fileName << ". At least I hope so. In any case, something happened for " << apdSerials.size() << " APDs."
         << "\nThank you for using apdBoxSetter! :)" << endl << endl;

    return (-apdSerials.empty());
}

void processArgumentsAndQueryMissing(int m_argc, char* m_argv[]) {
    po::options_description desc("Available options");
    desc.add_options()
            ("help", "produce help message")
            ("type", po::value<string>(), "(required) type of APDs [new, irr]")
            ("box", po::value<int>(), "(required) box number to be assigned")
            ("fileName", po::value<string>(), "file name or serial numbers [serials.dat]")
            ("debug", "emit additional messages for debugging purposes")
            ("user", po::value<string>(), "User name to connect to DB. Only used when combined with pass!")
            ("pass", po::value<string>(), "Password to connect to DB. Only used when combined with user!")
            ;
    po::variables_map vm;
    po::store(po::parse_command_line(m_argc, m_argv, desc), vm);
    po::notify(vm);

    if (vm.count("help")) {
        cout << desc << "\n";
        exit(0);
    }
    if (vm.count("box")) {
        boxNo = int(vm["box"].as<int>());
    } else {
        cout << "Which box should these APDs be assigned to?" << endl;
        try {
            cin >> boxNo;
            cout << endl;
        } catch (...) {
            boxNo = 0;
        }
        if (boxNo <= 0) {
            cerr << "Invalid batch number!" << endl;
            exit(-1);
        }
    }
    if (vm.count("fileName")) {
        fileName = vm["fileName"].as<string>();
        cout << "Reading serials from " << fileName << endl;
    }
    if (vm.count("user")) {
        username = vm["user"].as<string>();
    }
    if (vm.count("pass")) {
        password = vm["pass"].as<string>();
    }
    if (vm.count("debug")) {
        debug = true;
    }
}

void loadSerialsFromFileName(string m_fileName, bool m_debug) {
    ifstream in(m_fileName.c_str());
    if (!in.good()) {
        cerr << "Could not load serials. Does the input file exist?" << endl;
        exit(0);
    }

    apdSerials.clear();
    positions.clear();
    boxes.clear();

    int pos = 1;

    string line = "";
    while (in.good()) {
        getline(in,line);
        if (line.length() == 0 || !isdigit(line[0])) {
          pos++;
          continue;
        }
        apdSerials.push_back(line);
        boxes.push_back(boxNo);
        positions.push_back(pos++);
        if (m_debug) cout << "Found serial: " << line << endl;
    }
    if (apdSerials.size() != positions.size() || apdSerials.size() != boxes.size()) {
      cerr << "ERROR: Array sizes don't match!" << endl;
      apdSerials.clear();
      boxes.clear();
      positions.clear();
      return;
    }
    cerr << "Found " << apdSerials.size() << " APDs" << endl;
    return;
}