////////////////////////////////////////////////////////////////////////////////
// 
// Options.hh 
//
//    produced: 14/08/25 jr
//
////////////////////////////////////////////////////////////////////////////////
#ifndef OPTIONS_HH
#define OPTIONS_HH

#include <iostream>

#include "CommandlineOptions.hh"

namespace topcom {
  
  class Options {
  public:
    
    // flags:
    static constexpr uint64_t BIT_ONE                     = 0x0000000000000001;
    static constexpr uint64_t INPUT_CHIRO                 = BIT_ONE << 0;
    static constexpr uint64_t PREPROCESS_CHIRO            = BIT_ONE << 1;
    static constexpr uint64_t COMPUTE_ALL                 = BIT_ONE << 2;
    static constexpr uint64_t FULL_ONLY                   = BIT_ONE << 3;
    static constexpr uint64_t UNIMODULAR_ONLY             = BIT_ONE << 4;
    static constexpr uint64_t ONE_FLIP_ONLY               = BIT_ONE << 5;
    static constexpr uint64_t LEX_INCREASING_FLIPS_ONLY   = BIT_ONE << 6;
    static constexpr uint64_t LEX_DECREASING_FLIPS_ONLY   = BIT_ONE << 7;
    static constexpr uint64_t LEXFLIP_DIVE_UP_ONLY        = BIT_ONE << 8;
    static constexpr uint64_t LEXFLIP_DIVE_DOWN_ONLY      = BIT_ONE << 9;
    static constexpr uint64_t LEXFLIP_DIVE_RANDDIR_ONLY   = BIT_ONE << 10;
    static constexpr uint64_t LEXFLIP_DIVE_OPT_ONLY       = BIT_ONE << 11;
    static constexpr uint64_t LEXGKZ_DIVE_UP_ONLY         = BIT_ONE << 12;
    static constexpr uint64_t LEXGKZ_DIVE_DOWN_ONLY       = BIT_ONE << 13;
    static constexpr uint64_t LEXGKZ_DIVE_RANDDIR_ONLY    = BIT_ONE << 14;
    static constexpr uint64_t LEXGKZ_DIVE_OPT_ONLY        = BIT_ONE << 15;
    static constexpr uint64_t STOP_AT_REGULAR             = BIT_ONE << 16;
    static constexpr uint64_t STOP_AT_REGULARCOMP         = BIT_ONE << 17;
    static constexpr uint64_t STOP_AT_TARGET              = BIT_ONE << 18;
    static constexpr uint64_t NO_SYMMETRIES               = BIT_ONE << 19;
    static constexpr uint64_t NO_REGULARITY_CHECKS        = BIT_ONE << 20;
    static constexpr uint64_t NO_SEED                     = BIT_ONE << 21;
    static constexpr uint64_t FIND_FLIPS                  = BIT_ONE << 22;
    static constexpr uint64_t FIND_MIN                    = BIT_ONE << 23;
    static constexpr uint64_t OUTPUT_TRIANGS              = BIT_ONE << 24;
    static constexpr uint64_t OUTPUT_SUBSETS              = BIT_ONE << 25;
    static constexpr uint64_t SAVE_SUBSETS                = BIT_ONE << 26;
    static constexpr uint64_t NO_SIMPLICIAL_COMPLEX       = BIT_ONE << 27;
    static constexpr uint64_t DONT_ADD_POINTS             = BIT_ONE << 28;
    static constexpr uint64_t REDUCE_POINTS               = BIT_ONE << 29;
    static constexpr uint64_t ALL_SYMMETRIES              = BIT_ONE << 30;
    static constexpr uint64_t SHOW_INPUT_ONLY             = BIT_ONE << 31;
    static constexpr uint64_t IS_DOWNSTREAM_CALL          = BIT_ONE << 32; // avoids dumping and signalling more than once
  private:
    uint64_t _flags; // bistring holding the option bits
  public:

    // constructors:
    inline Options()                         : _flags(0)              { process_commandline_options(); } // no options
    inline Options(const uint64_t flags)     : _flags(flags)          { process_commandline_options(); } // options sepcified by flags
    inline Options(const Options& options)   : _flags(options._flags) { }
    inline Options(Options&& options)        : _flags(options._flags) { }

    // destructor:
    inline ~Options() {}

    // assignments:
    inline Options& operator=(const Options& options) {
      if (this == &options) {
	return *this;
      }
      _flags = options._flags;
      return *this;
    }
    inline Options& operator=(Options&& options) {
      if (this == &options) {
	return *this;
      }
      _flags = options._flags;
      return *this;
    }
    
    // logical accessors:
    inline bool input_chiro                () const { return _flags & INPUT_CHIRO; } 
    inline bool preprocess_chiro           () const { return _flags & PREPROCESS_CHIRO; }
    inline bool compute_all                () const { return _flags & COMPUTE_ALL; }
    inline bool full_only                  () const { return _flags & FULL_ONLY; }
    inline bool unimodular_only            () const { return _flags & UNIMODULAR_ONLY; }
    inline bool one_flip_only              () const { return _flags & ONE_FLIP_ONLY; }
    inline bool lex_increasing_flips_only  () const { return _flags & LEX_INCREASING_FLIPS_ONLY; }
    inline bool lex_decreasing_flips_only  () const { return _flags & LEX_DECREASING_FLIPS_ONLY; }
    inline bool lexflip_dive_up_only       () const { return _flags & LEXFLIP_DIVE_UP_ONLY; }
    inline bool lexflip_dive_down_only     () const { return _flags & LEXFLIP_DIVE_DOWN_ONLY; }
    inline bool lexflip_dive_randdir_only  () const { return _flags & LEXFLIP_DIVE_RANDDIR_ONLY; }
    inline bool lexflip_dive_opt_only      () const { return _flags & LEXFLIP_DIVE_OPT_ONLY; }
    inline bool lexgkz_dive_up_only        () const { return _flags & LEXGKZ_DIVE_UP_ONLY; }
    inline bool lexgkz_dive_down_only      () const { return _flags & LEXGKZ_DIVE_DOWN_ONLY; }
    inline bool lexgkz_dive_randdir_only   () const { return _flags & LEXGKZ_DIVE_RANDDIR_ONLY; }
    inline bool lexgkz_dive_opt_only       () const { return _flags & LEXGKZ_DIVE_OPT_ONLY; }
    inline bool stop_at_regular            () const { return _flags & STOP_AT_REGULAR; }
    inline bool stop_at_regularcomp        () const { return _flags & STOP_AT_REGULARCOMP; }
    inline bool stop_at_target             () const { return _flags & STOP_AT_TARGET; }
    inline bool no_symmetries              () const { return _flags & NO_SYMMETRIES; }
    inline bool no_regularity_checks       () const { return _flags & NO_REGULARITY_CHECKS; }
    inline bool no_seed                    () const { return _flags & NO_SEED; }
    inline bool find_flips                 () const { return _flags & FIND_FLIPS; }
    inline bool find_min                   () const { return _flags & FIND_MIN; }
    inline bool output_triangs             () const { return _flags & OUTPUT_TRIANGS; }
    inline bool output_subsets             () const { return _flags & OUTPUT_SUBSETS; }
    inline bool save_subsets               () const { return _flags & SAVE_SUBSETS; }
    inline bool no_simplicial_complex      () const { return _flags & NO_SIMPLICIAL_COMPLEX; }
    inline bool dont_add_points            () const { return _flags & DONT_ADD_POINTS; }
    inline bool reduce_points              () const { return _flags & REDUCE_POINTS; }
    inline bool all_symmetries             () const { return _flags & ALL_SYMMETRIES; }
    inline bool show_input_only            () const { return _flags & SHOW_INPUT_ONLY; }
    inline bool is_downstream_call         () const { return _flags & IS_DOWNSTREAM_CALL; }

    // derived accessors:
    inline bool stop_at_invalid            () const { return stop_at_regular() || stop_at_target(); }

    // modifiers:
    inline void replace_options            (const uint64_t flags) { _flags = flags; } 
    inline void add_options                (const uint64_t flags) { _flags |= flags; } 
    inline void remove_options             (const uint64_t flags) { _flags &= ~flags; }

    // insert command line options:
    inline void process_commandline_options() {
      if (CommandlineOptions::preprocess_chiro()) {
	_flags |= PREPROCESS_CHIRO;
      }
      else if (CommandlineOptions::memopt()) {
	_flags &= ~PREPROCESS_CHIRO;
      }
      if (no_regularity_checks()) {
	_flags &= ~STOP_AT_REGULAR;
      }
      else {
	if (CommandlineOptions::check_nonregular_comp()
	    || CommandlineOptions::check_possibly_nonregular_comp()
	    || CommandlineOptions::check_nonsubregular()
	    || CommandlineOptions::check_nonsupregular()
	    || CommandlineOptions::check_findregular()
	    || CommandlineOptions::check_randomfindregular()
	    ) {
	  _flags |= STOP_AT_REGULAR; // any flip-graph exploration stops at regular triangulations
	}
	if (CommandlineOptions::check_nonregular_comp()
	    || CommandlineOptions::check_possibly_nonregular_comp()
	    ) {
	  _flags |= STOP_AT_REGULARCOMP; // more aggressive caching
	}
      }
      if (CommandlineOptions::ignore_symmetries()) {
	_flags |= NO_SYMMETRIES;
      }
      if (CommandlineOptions::check_full()) {
	_flags |= FULL_ONLY;
      }
      if (CommandlineOptions::check_unimodular()) {
	_flags |= UNIMODULAR_ONLY;
      }
      if (CommandlineOptions::dont_add_points()) {
	_flags |= DONT_ADD_POINTS;
      }
      if (CommandlineOptions::reduce_points()) {
	_flags |= REDUCE_POINTS;
      }
      if (CommandlineOptions::observe_seed()) {
	_flags &= ~NO_SEED;
      }
      else {
	_flags |= NO_SEED;
      }
    }
    
    // comparison operators:
    inline bool operator==(const Options& options) const {
      return (_flags == options._flags);
    }

  public:
    
    // stream output/input:
    inline std::ostream& write(std::ostream& ost) const {
      return ost << _flags;
    }
    inline std::istream& read(std::istream& ist) {
      return ist >> _flags;
    }
    friend inline std::ostream& operator<<(std::ostream& ost, const Options& options) {
      return options.write(ost);
    }
    friend inline std::istream& operator>>(std::istream& ist, Options& options) {
      return options.read(ist);
    }
  };
  

}; // namespace topcom

#endif

// eof Options.hh
