////////////////////////////////////////////////////////////////////////////////
// 
// CommandlineOptions.cc
//
//    produced: 04 Oct 1999 jr
// last change: 04 Oct 1999 jr
//
////////////////////////////////////////////////////////////////////////////////

#include <iostream>

#include "CommandlineOptions.hh"
#include <algorithm>
CerrWrapper<false> cerrWrap;
CerrWrapper<true> cerrFirstWorker;

void err_print(const std::stringstream& stb) {
   std::string str = stb.str();
   const char *cs = str.c_str();
   std::cerr << cs;
}


//void __attribute__((weak)) put_error(const std::stringstream& stb) { }
//void __attribute__((weak)) report_state(const std::stringstream& stb) { }

/////////////////////////////////
// mptopcom options
bool      CommandlineOptions::_print_version           = false;
bool      CommandlineOptions::_small_group             = false;
bool      CommandlineOptions::_large_group             = false;
bool      CommandlineOptions::_make_tree               = false;
bool      CommandlineOptions::_all_edges               = false;
bool      CommandlineOptions::_make_marked_tree        = false;
size_type CommandlineOptions::_flip_cache              = 2000;
size_type CommandlineOptions::_orbit_cache             = 2000;
size_type CommandlineOptions::_regularity_cache        = 2000;
bool      CommandlineOptions::_central           = false;
bool      CommandlineOptions::_cyclic_flips            = false;
#ifdef WITH_SOPLEX
const char* CommandlineOptions::_polymake_lp_client    = "soplex";
#else
const char* CommandlineOptions::_polymake_lp_client    = "cdd";
#endif

/////////////////////////////////

bool      CommandlineOptions::_verbose                 = false;
bool      CommandlineOptions::_debug                   = false;
bool      CommandlineOptions::_memopt                  = false;
bool      CommandlineOptions::_check                   = false;
bool      CommandlineOptions::_simple                  = false;
bool      CommandlineOptions::_neighborcount           = false;
bool      CommandlineOptions::_input_chiro             = false;
bool      CommandlineOptions::_fine_only               = false;
bool      CommandlineOptions::_reduce_points           = false;
bool      CommandlineOptions::_dont_add_points         = false;
bool      CommandlineOptions::_dont_change_card        = false;
bool      CommandlineOptions::_output_triangs          = false;
bool      CommandlineOptions::_output_flips            = false;
bool      CommandlineOptions::_compute_all             = false;
bool      CommandlineOptions::_preprocess              = false;
bool      CommandlineOptions::_check_regular           = false;
bool      CommandlineOptions::_check_nonregular        = false;
bool      CommandlineOptions::_check_sometimes         = false;
bool      CommandlineOptions::_ignore_symmetries       = false;
bool      CommandlineOptions::_symmetries_are_affine   = false;
bool      CommandlineOptions::_output_heights          = false;
bool      CommandlineOptions::_use_soplex              = false;
bool      CommandlineOptions::_dump_status             = false;
bool      CommandlineOptions::_read_status             = false;
size_type CommandlineOptions::_report_frequency        = 1000UL;
size_type CommandlineOptions::_sometimes_frequency     = 10000UL;
size_type CommandlineOptions::_chirocache              = 1000000UL;
size_type CommandlineOptions::_localcache              = 1000000UL;
size_type CommandlineOptions::_no_of_simplices         = 0L;
size_type CommandlineOptions::_dump_frequency          = 1L;
size_type CommandlineOptions::_dump_rotations          = 2;
size_type CommandlineOptions::_estimates               = 0;
const char* CommandlineOptions::_dump_file             = "TOPCOM.dump";
const char* CommandlineOptions::_read_file             = "TOPCOM.dump";

//polymake:Poly CommandlineOptions::_polymakeobj;
void display_help(const char* argv0){
      std::cout << "--help or -h: \t help requested:" << std::endl;

      std::cout << "usage:" << std::endl;
      std::cout << argv0 << "[options]" << std::endl;
      std::cout << "where the input is read from stdin" << std::endl;
      std::cout << "and the output is written to stdout." << std::endl;
      std::cout << std::endl;

      std::cout << "options concerning shell output:" << std::endl;
      std::cout << "-h or --help         : print this help message" << std::endl;
      std::cout << "-v                   : write verbose information to stderr." << std::endl;
      std::cout << "--reportfrequency [n]: report every [n]th new triangulation." << std::endl;
      std::cout << "-d                   : write debug information to stderr." << std::endl;
      std::cout << "--heights            : write height vectors for triangulations to stdout (implies --regular)." 
		<< std::endl;
      std::cout << "--flips              : write flips as pairs of triangulation IDs to stdout." 
		<< std::endl;
      std::cout << std::endl;
     
      std::cout << "options for checking input:" << std::endl;
      std::cout << "--checktriang        : check any given triangulations for correctness." << std::endl;
      std::cout << std::endl;

      std::cout << "options for reporting properties of discovered triangulations:" << std::endl;
      std::cout << "--flipdeficiency     : report flip deficiency in triangulations to stderr." << std::endl;
      std::cout << "--checkfrequency [n] : check for regularity every [n]th triangulation and exit if regular." 
		<< std::endl;
      std::cout << "--frequency [n]      : (deprecated) check for regularity every [n]th triangulation and exit if regular." 
		<< std::endl;
      std::cout << std::endl;

      std::cout << "options concerning which triangulations are output (no influence on exploration):" << std::endl;
      std::cout << "--cardinality [n]    : count/output only triangulations of cardinality [n]." << std::endl;
      std::cout << "--nonregular         : count/output only non-regular triangulations." << std::endl;
      std::cout << std::endl;

      std::cout << "options concerning which triangulations are explored:" << std::endl;
      std::cout << "--full               : only list full/fine triangulations. Note: Does not work with all commands, consider using points2finetriangs. Accepted by mptopcom1 and mptopcom." << std::endl;
      std::cout << "--regular            : consider only regular triangulations" << std::endl;
      std::cout << "--noinsertion        : never use a point that is unused in the seed triangulation." << std::endl;
      std::cout << "--reducepoints       : try to remove the number of use points, reported to stderr." << std::endl;
      std::cout << "--keepcard           : never change the cardinality of triangulations by flipping." << std::endl;
      std::cout << "--estimates [n]      : do [n] random probes to estimate number of triangulations" << std::endl;
      std::cout << std::endl;

      std::cout << "options concerning symmetries:" << std::endl;
      std::cout << "--affine             : assume that given symmetries are affine." << std::endl;
      std::cout << "--nosymmetries       : ignore symmetries." << std::endl;
      std::cout << std::endl;

      std::cout << "options for dumping and reading the status of an interrupted computation:" << std::endl;
      std::cout << "--dump               : dump status of previous computation (last completed BFS round) into file" << std::endl;
      std::cout << "--dumpfile [filename]: set dump file name to [filename] (default: TOPCOM.dump)" << std::endl;
      std::cout << "--dumpfrequency [n]  : dump each [n]th completed BFS round (default: 1)" << std::endl;
      std::cout << "--dumprotations [n]  : dump into [n] rotating files" << std::endl;
      std::cout << "--read               : read status of previous computation (last completed BFS round) from file" << std::endl;
      std::cout << "--readfile [filename]: set dump file name to read to [filename] (default: TOPCOM.dump)" << std::endl;
      std::cout << std::endl;
      

      std::cout << "options concerning internal behaviour:" << std::endl;
      std::cout << "--memopt             : try to reduce memory consumption." << std::endl;
      std::cout << "--chirocache [n]     : set the chirotope cache to [n] elements." << std::endl;
      std::cout << "--localcache [n]     : set the cache for local data to [n] elements ." << std::endl;
      std::cout << "--soplex             : use soplex for regularity checks." << std::endl;
      std::cout << std::endl;
      std::cout << "mptopcom options: " << std::endl;
      std::cout << "--large-group        : will use switch table" << std::endl;
      std::cout << "--small-group        : will always compute full orbits" << std::endl;
      std::cout << "--make-tree          : output tree (only with mptopcom1)" << std::endl;
      std::cout << "--all-edges          : output edge graph of secondary polytope (only with mptopcom1)" << std::endl;
      std::cout << "--central      : only producing triangulations with gkz[0] maximal" << std::endl;
      std::cout << "--make-marked-tree   : output marked tree (only with mptopcom1)" << std::endl;
      std::cout << "--flip-cache         : size of flip cache" << std::endl;
      std::cout << "--orbit-cache        : size of orbit cache" << std::endl;
      std::cout << "--regularity-cache   : size of regularity cache" << std::endl;
      std::cout << "--cyclic-flips       : use special algorithm for cyclic polytopes" << std::endl;
      std::cout << "--polymake-lp-client : choose polymake lp client, default " << CommandlineOptions::polymake_lp_client() << std::endl;
}

bool isNumber(const char* str){
   bool result = true;
   for(int i = 0; i < strlen(str); i++)
    {
      result &= isdigit(str[i]) != 0;
   }
   return result;
}

void stretch_name(std::string& name){
   unsigned long total = std::string("--soplex            ").length();
   if(name.length() < total){
      name.append(total - name.length(), ' ');
   }
}

bool check_cache_size(const int& argc, const char** argv, int& pos, std::string cache_name, size_type& cache_var, bool print=true){
    if (std::string(argv[pos]) == cache_name) {
      if (argc > pos + 1) {
         if(isNumber(argv[pos+1])){
            cache_var = (int)atol(argv[pos+1]);
            pos++;
         } else {
            cerrFirstWorker << argv[pos+1] << " is not a valid value for " << cache_name << std::endl;
            cerrFirstWorker << "Exiting." << std::endl;
            exit(1);
         }
      } else {
         cerrFirstWorker << "No value given for " << cache_name << std::endl;
         cerrFirstWorker << "Exiting." << std::endl;
         exit(1);
      }
      if(print){
         stretch_name(cache_name);
         cerrFirstWorker << cache_name << " : size set to " << cache_var << std::endl;
      }
      return true;
    } else {
      return false;
    }
}

bool check_string_arg(const int& argc, const char** argv, int& pos, std::string arg_name, const char* str, std::string msg=""){
   if (std::string(argv[pos]) == arg_name) {
      if (argc > pos + 1) {
         str = argv[pos+1];
         pos++;
         cerrFirstWorker << arg_name << " : " << msg << " "<< str << std::endl;
      } else {
         cerrFirstWorker << arg_name << " specified, but no string given." << std::endl;
         cerrFirstWorker << "Exiting." << std::endl;
         exit(1);
      }
      return true;
   } else {
      return false;
   }
}


bool check_bool(const char* in, std::string name, const std::string message, bool& target, bool print=true){
   if(std::string(in) == name){
      target = true;
      if(print){
         stretch_name(name);
         cerrFirstWorker << name << " : " << message << std::endl;
      }
      return true;
   } else {
      return false;
   }
}

void CommandlineOptions::init(const int argc, const char** argv) {
   for (int i = 1; i < argc; ++i) {

      // print usage message:
      if ((strcmp(argv[i], "--help") == 0) || (strcmp(argv[i], "-h") == 0)) {
         display_help(argv[0]);
         exit(0);
      }
      
      if(check_bool(argv[i], "--version", "", _print_version, false)){ return; }
   }

   cerrFirstWorker << "Evaluating Commandline Options ..." << std::endl;
   for (int i = 1; i < argc; ++i) {
      // output options:
      if(check_bool(argv[i], "-v", "verbose output activated", _verbose)){ continue; }
      if(check_bool(argv[i], "-d", "debug output activated", _debug)){ continue; }
      if(check_bool(argv[i], "--flips", "output of flips activated", _debug)){ continue; }
      
      if(check_cache_size(argc, argv, i, "--reportfrequency", _report_frequency)){
         _verbose = true;
         cerrFirstWorker << "--reportrequency : report every " << _dump_frequency << "th new triangulation" << std::endl;
         continue;
      }
      
      if (check_bool(argv[i], "--heights", "output of defining heights activated", _output_heights)){
         _check_sometimes = false;
         _check_regular = true;
         continue;
      }

      if(check_bool(argv[i], "--large-group", "will use switch table", _large_group)){ continue; }
      if(check_bool(argv[i], "--small-group", "will always compute full orbits", _small_group)){ continue; }
      if(check_bool(argv[i], "--make-tree", "output tree (only with mptopcom1)", _make_tree)){ continue; }
      if(check_bool(argv[i], "--all-edges", "output graph of secondary polytope (only with mptopcom1)", _all_edges)){ 
         _make_tree = true;
         continue; 
      }
      if(check_bool(argv[i], "--central", "only producing triangulations with gkz[0] maximal", _central)){ continue; }
      if(check_bool(argv[i], "--cyclic-flips", "Using combinatorial flip description for cyclic polytopes", _cyclic_flips)){ continue; }

      if(check_bool(argv[i], "--make-marked-tree", "output marked tree (only with mptopcom1)", _make_marked_tree)){
         _make_tree = true;
         continue;
      }

      if(check_cache_size(argc, argv, i, "--flip-cache", _flip_cache)){ continue; }
      if(check_cache_size(argc, argv, i, "--orbit-cache", _orbit_cache)){ continue; }
      if(check_cache_size(argc, argv, i, "--regularity-cache", _regularity_cache)){ continue; }

      if(check_string_arg(argc, argv, i, "--polymake-lp-client", _polymake_lp_client)){ continue; }


      // options for checking input:
      if(check_bool(argv[i], "--checktriang", "check seed triangulation activated", _check)){ continue; }

      // options for reporting properties of discovered triangulations:
      if(check_bool(argv[i], "--flipdeficiency", "search for flip deficiency activate", _neighborcount)){ continue; }
      
      if(check_cache_size(argc, argv, i, "--frequency", _sometimes_frequency, false)){
         cerrFirstWorker << "WARNING:" << std::endl;
         cerrFirstWorker << "--frequency deprecated, please use --checkfrequency instead." << std::endl;
         if (_check_regular) {
            cerrFirstWorker << "--frequency cannot be used with --regular, ignoring --frequency." << std::endl;
         } else {
            _check_sometimes = true;
            cerrFirstWorker << "--frequency     : check regularity every " << _sometimes_frequency << "th triangulation acticated" << std::endl;
         }
         continue;
      }

      if(check_cache_size(argc, argv, i, "--checkfrequency", _sometimes_frequency, false)){
         if (_check_regular) {
            cerrFirstWorker << "--checkfrequency cannot be used with --regular, ignoring --checkfrequency." << std::endl;
            _check_sometimes = false;
         } else {
            cerrFirstWorker << "--frequency     : check regularity every " << _sometimes_frequency << "th triangulation acticated" << std::endl;
         }
         continue;
      }

      // options concerning which triangulations are output (no influence on explorartion):
      if(check_cache_size(argc, argv, i, "--cardinality", _no_of_simplices)){
         cerrFirstWorker << "--cardinality   : restrict to triangulations with " << _no_of_simplices << " simplices" << std::endl;
      }

      if(check_bool(argv[i], "--nonregular", "search for non-regular triangulations activated", _check_nonregular)){ continue; }

      // options concerning which triangulations are explored:
      if (strcmp(argv[i], "--regular") == 0) {
         if (_check_sometimes) {
            cerrFirstWorker << "--regular cannot be used with --frequency, ignoring --regular." << std::endl;
         }
         else {
            _check_sometimes = false;
            _check_regular = true;

            cerrFirstWorker << "--regular       : check for regular triangulations activated" << std::endl;
         }
         continue;
      }
      if(check_bool(argv[i], "--reducepoints", "reduce points heuristics activated", _reduce_points)){ continue; }
      if(check_bool(argv[i], "--noinsertion", "never add points activated", _dont_add_points)){ continue; }
      if(check_bool(argv[i], "--keepcard", "never change cardinality of triangulations by flipping activated", _dont_change_card)){ continue; }
      
      if(check_cache_size(argc, argv, i, "--estimates", _estimates)){
         cerrFirstWorker << "--estimates  : performing " << _estimates << " estimates" << std::endl;
         continue;
      }
      
      if(check_bool(argv[i], "--full", "only list full/fine triangulations. Note: Does not work with all commands, consider using points2finetriangs. Accepted by mptopcom1 and mptopcom.", _fine_only)){ continue; }

      // options concerning symmetries:
      if(check_bool(argv[i], "--affine", "assumption that symmetries are affine activated", _symmetries_are_affine)){ continue; }
      if(check_bool(argv[i], "--nosymmetries", "ignoring symmetries activated", _ignore_symmetries)){ continue; }

      // options concerning internals:
      if(check_bool(argv[i], "--memopt", "higher memory efficiency activated", _memopt)){ continue; }
      if(check_cache_size(argc, argv, i, "--chirocache", _chirocache)){
         cerrFirstWorker << "--chirocache    : cache for lazy chirotope set to " << _chirocache << " elements" << std::endl;
         continue;
      }
      if(check_cache_size(argc, argv, i, "--localcache", _localcache)){
         cerrFirstWorker << "--localcache    : cache for local data set to " << _localcache << " elements" << std::endl;
         continue;
      }
      if(check_cache_size(argc, argv, i, "--dumpfrequency", _dump_frequency)){
         _dump_status = true;
         cerrFirstWorker << "--dumpfrequency : dump every " << _dump_frequency << "th processed triangulation" << std::endl;
         continue;
      }
      if(check_cache_size(argc, argv, i, "--dumprotations", _dump_rotations)){
         _dump_status    = true;
         cerrFirstWorker << "--dumprotations : rotate dump into " << _dump_rotations << " many dump files" << std::endl;
         continue;
      }
      
      if(check_string_arg(argc, argv, i, "--dumpfile", _dump_file, "dump to file")){ 
         _dump_status = true;
         continue; 
      }
      if(check_bool(argv[i], "--dump", "dump status of computation into file activated", _dump_status)){ continue; }

      if(check_string_arg(argc, argv, i, "--readfile", _read_file, "read from file")){ 
         _read_status = true;
         continue; 
      }

      if(check_bool(argv[i], "--read", "read status from file", _read_status)){
         cerrFirstWorker << "--read            : read status from file" << std::endl;
         continue;
      }
      if (strcmp(argv[i], "--soplex") == 0) {
#ifdef HAVE_LIBSOPLEX      
         _use_soplex = true;

         cerrFirstWorker << "--soplex        : soplex activated" << std::endl;
#else
         cerrFirstWorker << "This binary was compiled without soplex support, ignoring --soplex." << std::endl;
#endif
         continue;
      }

      // some options that are not documented:
      if(check_bool(argv[i], "-s", "undocumented", _simple)){ continue; }
      if(check_bool(argv[i], "-A", "undocumented", _compute_all)){ continue; }
      if(check_bool(argv[i], "-C", "undocumented", _input_chiro)){ continue; }
      if(check_bool(argv[i], "-O", "undocumented", _output_triangs)){ continue; }
      if(check_bool(argv[i], "-E", "undocumented", _output_flips)){ continue; }
      if(check_bool(argv[i], "-P", "undocumented", _preprocess)){ continue; }
      
      // `continue;` was not called for this option, so we did not recognize.
      cerrFirstWorker << "Unrecognized option: " << argv[i] << std::endl;
      cerrFirstWorker << "Exiting." << std::endl;
      exit(1);
   }
   cerrFirstWorker << "... done." << std::endl;
}
    
    

// eof CommandlineOptions.cc
