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

#include <productiondatabaseclient.h>
#include <boost/program_options.hpp>
#include "toolbox.cpp"
#include "BulkDbAccess.h"

#include "QtGui/QApplication"

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

static string fileName = "serials.dat";
static string username = "";
static string password = "";
static string newLocation = "";
static string change = "";
static bool debug = false;
static bool unknownonly = false;

void processArgumentsAndQueryMissing(int m_argc, char* m_argv[]) {
  po::options_description desc("Available options");
  desc.add_options()
      ("help", "produce help message")
      ("fileName", po::value<string>(), "file name for serial numbers [serials.dat]")
      ("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!")
      ("location", po::value<string>(), "New Location of APDs")
      ("debug", "emit additional messages for debugging purposes")
      ("unknownOnly", "only work on APDs with location 'unknown' or 'Bochum'")
      ("change", po::value<string>(), "Overwrite this even if 'unknownonly' is set.")
      ;

  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("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("location")) {
    newLocation = vm["location"].as<string>();
  } else {
    cout << "Where are these APDs?" << endl;
    try {
      cin >> newLocation;
      cout << endl;
    } catch (...) {
      newLocation = "";
    }
    if (newLocation == "") {
      cerr << "Invalid location!" << endl;
      exit(-1);
    }
  }
  if (vm.count("debug")) {
    debug = true;
  }
  if (vm.count("unknownOnly")) {
    unknownonly = true;
  }
  if (vm.count("change")) {
    change = vm["change"].as<string>();
  }
}

int main(int argc, char* argv[]) {
  QApplication *_app = new QApplication(argc, argv);

  processArgumentsAndQueryMissing(argc, argv);
  AbsDbAccess *_proddb = new BulkDbAccess();

  vector<string> apdSerials;
  vector<string> allApdSerials;

  allApdSerials = loadSerialsFromFileName(fileName);
  apdSerials.clear();

  for ( size_t i = 0 ; i < allApdSerials.size() ; i++ ) {
    string existinglocation = _proddb->location( uint( stoul( allApdSerials[i] ) ) );
    if ( existinglocation != newLocation && ( !unknownonly || existinglocation == "unknown" || existinglocation == "Bochum" || existinglocation == change ) )
          apdSerials.push_back(allApdSerials[i]);
  }

  if ( apdSerials.size() == 0 ) {
    cout << "No data to write! Exiting." << endl;
    return 0;
  }
  cout << "Data will be written for " << apdSerials.size() << " APDs." << endl;

  ProductionDatabaseClient *_proddbclient = new ProductionDatabaseClient();

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

  _proddbclient->storeNewAPDLocation(apdSerials,newLocation);

  cout << "\nSuccessfully assigned Location '" << newLocation << "' to APDs from serial file " << fileName << "!\n\nThank you for using the apdLocationSetter :)" << endl << endl;

  _app->exit(0);
  return (-apdSerials.empty());
}