/*
   simple tool for a parallel search of a simple string within a file
*/


#include <cstdlib>
#include <iostream>
#include <thread>
#include <string>
#include <sstream>
#include <vector>
#include "engine.hpp"
#include "mapped_file.hpp"

int main(int argc, char** argv) {
   const char* cmdname = *argv++; --argc;
   if (argc != 3) {
      std::cerr << "Usage: " << cmdname << " nthreads regex file" << std::endl;
      exit(1);
   }
   const char* nthreads = *argv++; --argc;
   const char* regex = *argv++; --argc;
   if (*regex == 0) {
      std::cerr << cmdname << ": empty regular expression given" << std::endl;
      exit(1);
   }
   std::regex re(regex);
   const char* filename = *argv++; --argc;
   
   std::istringstream is(nthreads);
   unsigned int nofthreads;
   if (!(is >> nofthreads) || nofthreads == 0) {
      std::cerr << cmdname << ": bad value for nthreads" << std::endl;
      exit(1);
   }

   MappedFile mf(filename);
   if (!mf.valid()) {
      std::cerr << cmdname << ": " << filename << ": " <<
	 strerror(mf.get_errno()) << std::endl;
      exit(1);
   }
   const char* addr = mf.get_addr();
   size_t len = mf.get_length();

   if (nofthreads >= len) {
      nofthreads = 1;
   }

   std::vector<const char*> tstart(nofthreads);
   std::vector<std::size_t> tlen(nofthreads);
   const char* start = addr;
   for (unsigned int i = 0; i < nofthreads; ++i) {
      tstart[i] = start;
      if (i == nofthreads - 1) {
	 tlen[i] = len - (start - addr); /* take the rest */
      } else {
	 const char* cp = start + (addr + len - start) / (nofthreads - i);
	 /* find next line terminator */
	 while (cp < addr + len && *cp != '\n') ++cp;
	 if (cp < addr) ++cp; /* skip line terminator */
	 tlen[i] = cp - start;
	 start = cp;
	 if (cp == addr + len) {
	    /* we need less threads */
	    nofthreads = i + 1; break;
	 }
      }
   }
   /* fork... */
   std::vector<Engine> engines(nofthreads);
   std::vector<std::thread> threads(nofthreads);
   for (unsigned int tid = 0; tid < nofthreads; ++tid) {
      const char* addr = tstart[tid]; size_t len = tlen[tid];
      threads[tid] = std::thread([=,&engines]() {
	 Engine& engine(engines[tid]); engine.configure(addr, len);
	 engine.search(re);
      });
   }
   /* ...and join */
   for (auto& t: threads) {
      t.join();
   }

   /* print results */
   size_t lines = 0;
   for (auto& engine: engines) {
      auto results = engine.fetch_results();
      for (auto& result: results) {
	 std::size_t line = lines + result.rline_no;
	 std::cout << line << ": ";
	 std::cout.write(result.line, result.len);
	 std::cout << std::endl;
      }
      lines += engine.get_number_of_lines();
   }
}
