#include <iostream>
#include <iomanip>
#include <chrono>
#include <cmath>
#include <cpuid.h>
#include <fstream>
#include <csignal>
#include <unistd.h>

#include "treewalk.hpp"

using namespace std;
using namespace std::chrono;


fstream file_out;
bool checkpoint;
bool stack_correct;
#define SIGNAL_CHECKPOINT SIGUSR2


void signal_checkpoint_handler(int signum){
  checkpoint=true;
}

void treat(const Semigroup& m){
  int q=ceil(float(m.conductor)/float(m.min));
  unsigned int rho=q*m.min-m.conductor;
  if(m.wilf<=rho){
    if((m.wilf<0) or (m.wilf<=rho and m.e>2 and m.left_primitive>1 and q>=4)){
      output(m,file_out);
    }
  }
}


void walk(Stack& stack){
  Semigroup *current,*son;
  Semigroup temp;
  while(not stack.is_empty() and not checkpoint){
    current = stack.pop();
    if(not cut(*current)){
      if(current->genus < MAX_GENUS - 1){
	auto it=generator_iter<CHILDREN>(*current);
	ind_t pos=0;
	while (it.move_next()){
	  son=stack.pushed();
	  remove_generator(*son, *current, it.get_gen(),pos++);
	  treat(*son);
	}
      }
      else{
	auto it = generator_iter<CHILDREN>(*current);
	ind_t pos=0;
	while (it.move_next()){
	  remove_generator(temp, *current, it.get_gen(),pos++);
	  treat(temp);
	}
      }
    }
  }
}


int main(int argc, char **argv){
  if(argc!=2){
    cerr<<"Usage : "<<argv[0]<<" tasks/task_file"<<endl;
    exit(-1);
  }
  char* task_filename=argv[1];
  unsigned int ax, bx, cx, dx;
  if (!__get_cpuid(0x00000001, &ax, &bx, &cx, &dx)){
    cerr << "Unable to determine the processor type !" << endl;
    return EXIT_FAILURE;
  }
  if (!(cx & bit_SSSE3)){
    cerr << "This programm require sse3 instructions set !" << endl;
    return EXIT_FAILURE;
  }
  if (!(cx & bit_POPCNT)){
    cerr << "This programm require popcount instruction !" << endl;
    return EXIT_FAILURE;
  }
  fstream file_in;
  file_in.open(task_filename,ios::in|ios::binary);
  string filename=string("tasks/")+task_filename;
  filename+="-out";
  file_out.open(filename,ios::out|ios::trunc);
  Stack stack;
  char d[3*MAX_GENUS+1];
  char c,g,m;
  size_t n;
  file_in.read((char*)&n,8);
  cout<<"Stack size = "<<n<<endl;
  for(size_t i=0;i<n;++i){
    file_in.read(&c,1);
    file_in.read(&g,1);
    file_in.read(&m,1);
    file_in.read(d,3*MAX_GENUS+1);
    Semigroup* S=stack.pushed();
    init(*S,c,g,m,d);
    treat(*S);
  }
  file_in.close();
  checkpoint=false;
  signal(SIGNAL_CHECKPOINT, signal_checkpoint_handler);
  walk(stack);

  file_out.close();
  if(checkpoint){
    cout<<"Stoping exploration due to checkpoint signal reception."<<endl;
    filename=string("tasks/checkpoint/")+task_filename+string("-checkpoint");
    fstream file_stack;
    file_stack.open(filename.c_str(),ios::out|ios::binary);
    size_t size=stack.stack_size;
    file_stack.write((char*)(&size),8);
    cout<<"Checkpoint size : "<<size<<endl;
    for(size_t i=0;i<size;++i){
      record(*stack.stack[i],file_stack);
    }
    file_stack.close();
    return 1;
  }
  else{
    return 0;
  }
}

