#include <boost/date_time/posix_time/posix_time.hpp>
#include <fstream>
#include <iostream>
#include <netinet/in.h>
#include <sys/socket.h>
#include <thread>
#include <unistd.h>
#include <vector>

using std::cerr;
using std::cout;
using std::endl;
using std::ofstream;
using std::stoi;
using std::thread;
using std::vector;

#define DEBUG

class myServer {
  unsigned short port;
  int serverSocket;

  sockaddr_in serverAddress;
  vector<thread> clients;

  ofstream logFile;

public:
  myServer(unsigned short port) {
    this->port = port;

    this->logFile.open("log.txt", std::fstream::out | std::fstream::app);
    if (!this->logFile.is_open()) {
      cerr << "Error! Cannot open logfile." << endl;
    }

    this->serverSocket = socket(AF_INET, SOCK_STREAM, 0);

    if (this->serverSocket < 0) {
      cerr << "Error! Cannot open a socket." << endl;
      exit(-2);
    }

    this->serverAddress.sin_family = AF_INET;
    this->serverAddress.sin_port = htons(port);
    this->serverAddress.sin_addr.s_addr = INADDR_ANY;

    start();
  }

  void start() {
    if (bind(this->serverSocket, (sockaddr *)&serverAddress,
             sizeof(serverAddress)) < 0) {
      cerr << "Cannot bind a socket!" << endl;
      exit(-3);
    }

    if (listen(this->serverSocket, 10) < 0) {
      cerr << "Cannot listen a socket!" << endl;
      exit(-4);
    }

#ifdef DEBUG
    cout << "[DEBUG] server started on port " << this->port << "." << endl;
    this->logFile << "[DEBUG] server started on port " << this->port << "." << endl;
#endif

    acceptClients();
  }

  void handleClient(unsigned short clientSocket) {
#ifdef DEBUG
    cout << "Starting handle of a client with socket descriptor "
         << clientSocket << endl
         << ".";
#endif
    while (true) {
      char buffer[128] = {0};
      recv(clientSocket, buffer, 128, 0);
      cout << "Received message from a client [" << clientSocket
           << "]: " << buffer << "." << endl;

      this->logFile << "["
                    << boost::posix_time::microsec_clock::universal_time()
                    << "]" << buffer << endl;
    }
  }

  void acceptClients() {
    while (true) {
      unsigned short clientSocket =
          accept(this->serverSocket, nullptr, nullptr);
#ifdef DEBUG
      this->logFile << "["
                    << boost::posix_time::microsec_clock::universal_time()
                    << "] {DEBUG} A client " << clientSocket
                    << " has connected";
#endif 
      thread clientHandler(&myServer::handleClient, this, clientSocket);
      this->clients.push_back(std::move(clientHandler));
    }
    for (int i = 0; i < this->clients.size(); i ++) {
        this->clients[i].join();
    }
  }

  ~myServer() { close(this->serverSocket); }
};

int main(int argc, char *argv[]) {

  if (argc < 2) {
    cerr << "Usage:" << argv[0] << " [port]." << endl;
    exit(-1);
  }

  const unsigned short port = stoi(argv[1]);

#ifdef DEBUG
  cout << "[DEBUG] Port: " << port << "." << endl;
#endif
  myServer s(port);

    while(1);
}