////////////////////////////////////////////////////////////////////////////////
// 
// Admissibles.hh 
//
//    produced: 19/04/98 jr
//
////////////////////////////////////////////////////////////////////////////////
#ifndef ADMISSIBLES_HH
#define ADMISSIBLES_HH

#include <assert.h>
#include <unordered_map>

#include "Global.hh"
#include "ContainerIO.hh"
#include "SimplicialComplex.hh"
#include "VertexFacetTable.hh"

#include "CommandlineOptions.hh"

#include "Chirotope.hh"
#include "Circuits.hh"

#include "Symmetry.hh"

namespace topcom {

  // a map from simplices to pairs (admissible simplices, lex-larger admissible simplices with common vertices);
  // where the second part of the pair is only filled for flag-check:
  typedef std::unordered_map<Simplex, std::pair<SimplicialComplex, SimplicialComplex>, Hash<Simplex> > admissibles_data;
  typedef std::unordered_map<Simplex, SimplicialComplex, Hash<Simplex> > simplex_orbit_table_type;
  
  // helper class that reads the specific data without derived information:
  class AdmissiblesReader {
  private:
    admissibles_data  _admissibles ;
    SimplicialComplex _valid_simplices;
  public:
    inline AdmissiblesReader() : _admissibles() {}
    inline ~AdmissiblesReader() {}
    inline const admissibles_data&  admissibles       () const { return _admissibles; }
    inline const SimplicialComplex& valid_simplices   () const { return _valid_simplices; }
    inline std::istream& read(std::istream& ist) {
      char dash;
      char arrow;
      char c;
      
      _admissibles.clear();
      ist >> std::ws >> c;
      if (c == ContainerChars::list_start_char) {
	while (ist >> std::ws >> c) {
	  if (c == ContainerChars::list_end_char) {
	    break;
	  }
	  if (c == ContainerChars::delim_char) {
	    continue;
	  }
	  ist.putback(c);
	  
	  // an entry starts here:
	  Simplex key_reader;
	  std::pair<SimplicialComplex, SimplicialComplex> data_reader;
	  
	  if (!(ist >> std::ws >> key_reader)) {
#ifdef READ_DEBUG
	    MessageStreams::forced() << message::lock
				     << "std::istream& Admissibles::read(std::istream& ist): "
				     << "key simplex not found." << std::endl
				     << message::unlock;
#endif
	    ist.clear(std::ios::failbit);
	    return ist;
	  }
	  if (!(ist >> std::ws >> dash >> arrow)
	      || (dash != ContainerChars::dash)
	      || (arrow != ContainerChars::arrow)) {
#ifdef READ_DEBUG
	    MessageStreams::forced() << message::lock
				     << "std::istream& Admissibles::read(std::istream& ist): "
				     << '`' << ContainerChars::map_chars << "' not found." << std::endl
				     << message::unlock;
#endif
	    ist.clear(std::ios::failbit);
	    return ist;
	  }
	  if (!(ist >> std::ws >> data_reader)) {
#ifdef READ_DEBUG
	    MessageStreams::forced() << message::lock
				     << "std::istream& Admissibles::read(std::istream& ist): "
				     << "data simplicial complex not found." << std::endl
				     << message::unlock;
#endif
	    ist.clear(std::ios::failbit);
	    return ist;
	  }
	  if (!(ist >> std::ws >> c)
	      || (c != ContainerChars::delim_char)) {
#ifdef READ_DEBUG
	    MessageStreams::forced() << message::lock
				     << "std::istream& Admissibles::read(std::istream& ist): "
				     << '`' << ContainerChars::delim_char << "' not found." << std::endl
				     << message::unlock;
#endif
	    ist.clear(std::ios::failbit);
	    return ist;
	  }
	  if (!(ist >> std::ws >> _valid_simplices)) {
#ifdef READ_DEBUG
	    MessageStreams::forced() << message::lock
				     << "std::istream& Admissibles::read(std::istream& ist): "
				     << "simplicial complex for _valid_simplices not found." << std::endl
				     << message::unlock;
#endif
	    ist.clear(std::ios::failbit);
	    return ist;
	  }
	  _admissibles.insert(std::pair<Simplex, std::pair<SimplicialComplex, SimplicialComplex> >(key_reader, data_reader));
	}
      }
      else {
#ifdef READ_DEBUG
	MessageStreams::forced() << message::lock
				 << "std::istream& Admissibles::read(std::istream& ist): "
				 << "missing `" << ContainerChars::list_start_char << "'." << std::endl
				 << message::unlock;
#endif
	ist.clear(std::ios::failbit);
	return ist;
      }
      ist.clear(std::ios::goodbit);
      return ist;
    }
    
    inline friend std::istream& operator>>(std::istream& ist, AdmissiblesReader& admr) {
      return admr.read(ist);
    }
  };
  
  class Admissibles : public admissibles_data {
  private:
    class Worker {
    private:
      int                     _ID;
      SimplicialComplex       _input;
      admissibles_data        _output;
    private:
      Worker() = delete;
    public:
      inline Worker(const Worker& init_worker) : _ID(init_worker._ID), _input(init_worker._input), _output(init_worker._output) {}
      inline Worker(const Worker&& init_worker) : _ID(init_worker._ID), _input(std::move(init_worker._input)), _output(std::move(init_worker._output)) {}
      inline Worker(const int init_ID, const SimplicialComplex& init_input) : _ID(init_ID), _input(init_input), _output() {}
      inline Worker& operator=(const Worker& worker) {
	if (this == &worker) {
	  return *this;
	}
	_ID = worker._ID;
	_input = worker._input;
	_output = worker._output;
	return *this;
      }
      inline Worker& operator=(const Worker&& worker) {
	if (this == &worker) {
	  return *this;
	}
	_ID = worker._ID;
	_input = std::move(worker._input);
	_output = std::move(worker._output);
	return *this;
      }
      void operator()(const SimplicialComplex&, const VertexFacetTable&, const Circuits&);
    public:
      inline       int                ID    () const { return _ID; }
      inline const admissibles_data&  output() const { return _output; }
      inline void                     clear_input()  { _input.clear(); }
      inline void                     clear_output() { _output.clear(); }
    };
  private:
    SimplicialComplex _symvalid_simplices; // all simplices that can be used in a symmetric feasible triangulation
    SimplicialComplex _valid_simplices; // all simplices that can be used in any feasible triangulation
  public:

    // constructors:
    inline Admissibles();
    inline Admissibles(const Admissibles&);
    inline Admissibles(Admissibles&&);
    inline Admissibles(AdmissiblesReader&&);
    Admissibles(const Circuits&,
		const PointConfiguration*, // 0 if no points were given
		const Chirotope&,
		const SymmetryGroup&, // all symmetries
		const SymmetryGroup&, // all required symmetries
		const bool = false);  // only fine

    // destructor:
    inline ~Admissibles();

    // assignment:
    inline Admissibles& operator=(const Admissibles&);
    inline Admissibles& operator=(Admissibles&&);

    // accessors:
    inline const SimplicialComplex& valid_simplices() const;

    // functions:
    void require_symmetries(const Chirotope&, const SymmetryGroup&);

    // stream input/output:
    std::istream& read(std::istream&);
    inline friend std::istream& operator>>(std::istream&, Admissibles&);
    std::ostream& write(std::ostream&) const;
    inline friend std::ostream& operator<<(std::ostream&, const Admissibles&);

  private:

    // auxiliary functions for constructors - currently unused:
    bool _have_proper_intersection(const Simplex&, const Simplex&, const Circuits&) const;
  };

  // constructors:
  inline Admissibles::Admissibles() :
    admissibles_data(),
    _valid_simplices() {}

  inline Admissibles::Admissibles(const Admissibles& admissibles) :
    admissibles_data(admissibles),
    _valid_simplices(admissibles._valid_simplices) {}

  inline Admissibles::Admissibles(Admissibles&& admissibles) :
    admissibles_data(std::move(admissibles)),
    _valid_simplices(std::move(admissibles._valid_simplices)) {}

  inline Admissibles::Admissibles(AdmissiblesReader&& admr) :
    admissibles_data(std::move(admr.admissibles())),
    _valid_simplices(std::move(admr.valid_simplices())) {}

  // destructor:
  inline Admissibles::~Admissibles() {
#ifdef CONSTRUCTOR_DEBUG
    MessageStreams::debug() << message::lock
			    << "Admissibles::~Admissibles(): destructor called." << std::endl
			    << message::unlock;
#endif
  }

  // assignment:
  inline Admissibles& Admissibles::operator=(const Admissibles& admissibles) {
    if (this == &admissibles) {
      return *this;
    }
    admissibles_data::operator=(admissibles);
    _valid_simplices     = admissibles._valid_simplices;
    return *this;
  }
  inline Admissibles& Admissibles::operator=(Admissibles&& admissibles) {
    if (this == &admissibles) {
      return *this;
    }
    admissibles_data::operator=(std::move(admissibles));
    _valid_simplices     = std::move(admissibles._valid_simplices);
    return *this;
  }

  // accessors:
  inline const SimplicialComplex& Admissibles::valid_simplices() const {
    return _valid_simplices;
  }

  // stream input/output:
  inline std::istream& operator>>(std::istream& ist, Admissibles& admissibles) {
    return admissibles.read(ist);
  }
  inline std::ostream& operator<<(std::ostream& ost, const Admissibles& admissibles) {
    return admissibles.write(ost);
  }
  
}; // namespace topcom

#endif

// eof Admissibles.hh
