////////////////////////////////////////////////////////////////////////////////
// 
// PartialTriang.hh 
//
//    produced: 30/04/98 jr
//
////////////////////////////////////////////////////////////////////////////////
#ifndef PARTIALTRIANG_HH
#define PARTIALTRIANG_HH

#include "Message.hh"

#include "SimplicialComplex.hh"
#include "SparseSimplicialComplex.hh"
#include "FastSimplicialComplex.hh"

#include "CommandlineOptions.hh"

#include "Field.hh"
#include "Permutation.hh"

#include "ConfData.hh"
#include "PointConfiguration.hh"
#include "Chirotope.hh"
#include "Circuits.hh"
#include "Facets.hh"
#include "Admissibles.hh"
#include "Incidences.hh"
#include "Volumes.hh"

namespace topcom {

  typedef SimplicialComplex pt_complex_type;

  // the following is an auxiliary class to make possible
  // to read the object-specific data for a PartialTriang;
  // PartialTriang then has a constructor from
  // the global context data and PartialTriangReader:
  class PartialTriangReader {
  public:
    pt_complex_type          partial_triang; // the SimplicialComplex part of a PartialTriang
    SimplicialComplex        admissibles;
    SimplicialComplex        freeintfacets;
#ifdef USE_CONFFACETS
    facets_data              freeconffacets;
#endif
    Field                    covered_volume;
  public:
    // the only functionality is to read data into the member fields:
    inline std::istream& read(std::istream& ist) {

      char c;

      // the expected syntax should be idential to the write syntax of PartialTriang:
      if (!(ist >> std::ws >> c >> std::ws) || (c != ContainerChars::list_start_char)) {
	MessageStreams::forced() << message::lock
				 << "PartialTriangReader::read(std::istream& ist): error while reading - expected "
				 << ContainerChars::list_start_char
				 << " - exiting"
				 << std::endl
				 << message::unlock;
	exit(1);
      }
      if (!(ist >> partial_triang)) {
	MessageStreams::forced() << message::lock
				 << "PartialTriangReader::read(std::istream& ist): error while reading partial_triang - exiting"
				 << std::endl
				 << message::unlock;
	exit(1);
      }
      if (!(ist >> std::ws >> c >> std::ws) || (c != ContainerChars::delim_char)) {
	MessageStreams::forced() << message::lock
				 << "PartialTriangReader::read(std::istream& ist): error while reading - expected "
				 << ContainerChars::delim_char
				 << "after partial_triang - exiting"
				 << std::endl
				 << message::unlock;
	exit(1);
      }
      if (!(ist >> admissibles)) {
	MessageStreams::forced() << message::lock
				 << "PartialTriangReader::read(std::istream& ist): error while reading admissibles - exiting"
				 << std::endl
				 << message::unlock;
	exit(1);
      }
      if (!(ist >> std::ws >> c >> std::ws) || (c != ContainerChars::delim_char)) {
	MessageStreams::forced() << message::lock
				 << "PartialTriangReader::read(std::istream& ist): error while reading - expected "
				 << ContainerChars::delim_char
				 << "after _admissibles - exiting"
				 << std::endl
				 << message::unlock;
	exit(1);
      }
      if (!(ist >> freeintfacets)) {
	MessageStreams::forced() << message::lock
				 << "PartialTriangReader::read(std::istream& ist): error while reading freeintfacets - exiting"
				 << std::endl
				 << message::unlock;
	exit(1);
      }
      if (!(ist >> std::ws >> c >> std::ws) || (c != ContainerChars::delim_char)) {
	MessageStreams::forced() << message::lock
				 << "PartialTriangReader::read(std::istream& ist): error while reading - expected "
				 << ContainerChars::delim_char
				 << "after _freeintfacets - exiting"
				 << std::endl
				 << message::unlock;
	exit(1);
      }
#ifdef USE_CONFFACETS
      if (!(ist >> freeconffacets)) {
      	MessageStreams::forced() << message::lock
      				 << "PartialTriangReader::read(std::istream& ist): error while reading freeconffacets - exiting"
      				 << std::endl
      				 << message::unlock;
      	exit(1);
      }
      if (!(ist >> std::ws >> c >> std::ws) || (c != ContainerChars::delim_char)) {
	MessageStreams::forced() << message::lock
				 << "PartialTriangReader::read(std::istream& ist): error while reading - expected "
				 << ContainerChars::delim_char
				 << "after _freeconffacets - exiting"
				 << std::endl
				 << message::unlock;
	exit(1);
      }
#endif
      if (!(ist >> covered_volume)) {
	MessageStreams::forced() << message::lock
				 << "PartialTriangReader::read(std::istream& ist): error while reading covered_volume - exiting"
				 << std::endl
				 << message::unlock;
	exit(1);
      }
      if (!(ist >> std::ws >> c >> std::ws) || (c != ContainerChars::list_end_char)) {
	MessageStreams::forced() << message::lock
				 << "PartialTriangReader::read(std::istream& ist): error while reading - expected "
				 << ContainerChars::list_end_char
				 << "at end of data record - exiting"
				 << std::endl
				 << message::unlock;
	exit(1);
      }
      return ist;
    }
    inline friend std::istream& operator>>(std::istream& ist, PartialTriangReader& ptr) {
      return ptr.read(ist);
    }
  };
  
  class PartialTriang : public pt_complex_type {
  private:
    SimplicialComplex        _admissibles;
    SimplicialComplex        _freeintfacets;
#ifdef USE_CONFFACETS
    facets_data              _freeconffacets;
#endif
    Field                    _covered_volume;
  private:
    PartialTriang();
  public:

    // constructors:
    inline PartialTriang(const ConfData&            confdata);
    inline PartialTriang(const ConfData&            confdata,
			 PartialTriangReader&&      ptr);
    inline PartialTriang(const PartialTriang& pt);
    inline PartialTriang(PartialTriang&& pt);
    inline PartialTriang(const ConfData& confdata,
			 const PartialTriang& pt,
			 const Simplex& new_simp);
    inline PartialTriang(const ConfData& confdata,
			 const PartialTriang& pt, 
			 const Simplex& new_simp, 
			 const SimplicialComplex& forbidden);

    // destructor:
    inline ~PartialTriang();

    // assignments:
    inline PartialTriang& operator=(const PartialTriang& pt);
    inline PartialTriang& operator=(PartialTriang&& pt);

    // accessors:
    inline const SimplicialComplex& simplicial_complex() const { return *this; }
    inline const SimplicialComplex& admissibles()        const { return _admissibles; }
    inline const SimplicialComplex& freeintfacets()      const { return _freeintfacets; }
#ifdef USE_CONFFACETS
    inline const facets_data&       freeconffacets()     const { return _freeconffacets; }
#endif
    inline const Field&             covered_volume()     const { return _covered_volume; }

    // functions:
    inline PartialTriang& add_simplices(const ConfData& confdata,
					const SimplicialComplex& sc);
    inline void forbid(const Simplex&);
    inline void forbid(const SimplicialComplex&);
    inline bool complete(const ConfData&);

  private:

    // internal algorithms:
    void _add_simplex(const ConfData& confdata, const Simplex& simp);
    void _add_simplex(const ConfData& confdata, const Simplex& simp, const SimplicialComplex& forbidden);
    void _update_admissibles(const ConfData& confdata, const Simplex& simp);
    void _update_admissibles(const ConfData& confdata, const Simplex& simp, const SimplicialComplex& forbidden);
    void _update_freefacets(const ConfData& confdata, const Simplex& simp);
    void _update_volume(const ConfData& confdata, const Simplex& simp);

    // stream intput/output:
    inline std::istream& read(std::istream&);
    inline friend std::istream& operator>>(std::istream&, PartialTriang&);
    inline std::ostream& write(std::ostream&) const;
    inline friend std::ostream& operator<<(std::ostream&, const PartialTriang&);    
  };
  
  // constructors:
  inline PartialTriang::PartialTriang(const ConfData& confdata) :
    pt_complex_type(),
    _admissibles(confdata.admtable().valid_simplices()),
    _freeintfacets(),
#ifdef USE_CONFFACETS
    _freeconffacets(*facetsptr),
#endif
    _covered_volume(FieldConstants::ZERO) {}

  // the following constructor initializes a partial triangulation
  // from pointers to the global data and a PartialTriangReader object
  // containing the object data read from an input stream;
  // after this call, the reader maybe undefined because its data is moved:
  inline PartialTriang::PartialTriang(const ConfData&            confdata,
				      PartialTriangReader&&      ptr) : 
    pt_complex_type(std::move(ptr.partial_triang)),
    _admissibles(std::move(ptr.admissibles)),
    _freeintfacets(std::move(ptr.freeintfacets)),
#ifdef USE_CONFFACETS
    _freeconffacets(std::move(ptr.freeconffacets)),
#endif
    _covered_volume(std::move(ptr.covered_volume)) {
  }
  
  inline PartialTriang::PartialTriang(const PartialTriang& pt) : 
    pt_complex_type(pt),
    _admissibles(pt._admissibles),
    _freeintfacets(pt._freeintfacets),
#ifdef USE_CONFFACETS
    _freeconffacets(pt._freeconffacets),
#endif
    _covered_volume(pt._covered_volume) {
#ifdef CONSTRUCTOR_DEBUG
    MessageStreams::debug() << message::lock
			    << "PartialTriang::PartialTriang(const PartialTriang& pt): copy constructur called" << std::endl
			    << message::unlock;
#endif
  }

  inline PartialTriang::PartialTriang(PartialTriang&& pt) : 
    pt_complex_type(std::move(pt)),
    _admissibles(std::move(pt._admissibles)),
    _freeintfacets(std::move(pt._freeintfacets)),
#ifdef USE_CONFFACETS
    _freeconffacets(std::move(pt._freeconffacets)),
#endif
    _covered_volume(std::move(pt._covered_volume)) {
#ifdef CONSTRUCTOR_DEBUG
    MessageStreams::debug() << message::lock
			    << "PartialTriang::PartialTriang(PartialTriang&& pt): move constructur called" << std::endl
			    << message::unlock;
#endif
  }

  inline PartialTriang::PartialTriang(const ConfData& confdata,
				      const PartialTriang& pt,
				      const Simplex& new_simp) : 
    pt_complex_type(pt),
    _admissibles(pt._admissibles),
    _freeintfacets(pt._freeintfacets),
#ifdef USE_CONFFACETS
    _freeconffacets(pt._freeconffacets),
#endif
    _covered_volume(pt._covered_volume) {
    _add_simplex(confdata, new_simp);
  }

  inline PartialTriang::PartialTriang(const ConfData& confdata,
				      const PartialTriang& pt, 
				      const Simplex& new_simp, 
				      const SimplicialComplex& forbidden) : 
    pt_complex_type(pt), 
    _admissibles(pt._admissibles),
    _freeintfacets(pt._freeintfacets),
#ifdef USE_CONFFACETS
    _freeconffacets(pt._freeconffacets),
#endif
    _covered_volume(pt._covered_volume) {
    _add_simplex(confdata, new_simp, forbidden);
  }

  // destructor:
  inline PartialTriang::~PartialTriang() {}

  // assignment:
  inline PartialTriang& PartialTriang::operator=(const PartialTriang& pt) {
#ifdef CONSTRUCTOR_DEBUG
    MessageStreams::debug() << message::lock
			    << "PartialTriang& PartialTriang::operator=(const PartialTriang& pt): copy assignment operator called" << std::endl
			    << message::unlock;
#endif
    if (this == &pt) {
      return *this;
    }
    pt_complex_type::operator=(pt);
    _admissibles       = pt._admissibles;
    _freeintfacets     = pt._freeintfacets;
#ifdef USE_CONFFACETS
    _freeconffacets    = pt._freeconffacets;
#endif
    _covered_volume    = pt._covered_volume;
    return *this;
  }

  inline PartialTriang& PartialTriang::operator=(PartialTriang&& pt) {
#ifdef CONSTRUCTOR_DEBUG
    MessageStreams::debug() << message::lock
			    << "PartialTriang& PartialTriang::operator=(PartialTriang&& pt): move assignment operator called" << std::endl
			    << message::unlock;
#endif
    if (this == &pt) {
      return *this;
    }
    pt_complex_type::operator=(std::move(pt));
    _admissibles       = std::move(pt._admissibles);
    _freeintfacets     = std::move(pt._freeintfacets);
#ifdef USE_CONFFACETS
    _freeconffacets    = std::move(pt._freeconffacets);
#endif
    _covered_volume    = std::move(pt._covered_volume);
    return *this;
  }

  // functions:

  // the following is useful if we want to update an already initialized
  // PartialTriang with the simplices of a SimplicialComplex
  // (like in the case when we read into an empty PartialTriang):
  inline PartialTriang& PartialTriang::add_simplices(const ConfData& confdata,
						     const SimplicialComplex& sc) {
    for (SimplicialComplex::const_iterator iter = sc.begin();
	 iter != sc.end();
	 ++iter) {
      _add_simplex(confdata, *iter);
    }
    return *this;
  }
  
  inline void PartialTriang::forbid(const Simplex& simp) {
    _admissibles -= simp;
  }

  inline void PartialTriang::forbid(const SimplicialComplex& sc) {
    _admissibles -= sc;
  }

  inline bool PartialTriang::complete(const ConfData& confdata) {
    if (_freeintfacets.empty()) {
      return true;
    }
    for (SimplicialComplex::iterator iter = _admissibles.begin(); 
    	 iter != _admissibles.end(); 
    	 ++iter) {
      PartialTriang new_partialtriang(confdata, *this, *iter);
      if (new_partialtriang.complete(confdata)) {
    	*this = std::move(new_partialtriang);
    	return true;
      }
    }
    return false;
  }

  // internal algorithms:
  inline void PartialTriang::_add_simplex(const ConfData& confdata,
					  const Simplex& simp) {
    // *this += simp;
    this->insert(simp);
    _update_admissibles(confdata, simp);
    _update_freefacets(confdata, simp);
    _update_volume(confdata, simp);
  }

  inline void PartialTriang::_add_simplex(const ConfData& confdata,
					  const Simplex& simp,
					  const SimplicialComplex& forbidden) {
    // *this += simp;
    this->insert(simp);
    _update_admissibles(confdata, simp, forbidden);
    _update_freefacets(confdata, simp);
    _update_volume(confdata, simp);
  }

  inline void PartialTriang::_update_admissibles(const ConfData& confdata,
						 const Simplex& simp) {
    _admissibles *= confdata.admtable().find(simp)->second.first;
  }
  
  inline void PartialTriang::_update_admissibles(const ConfData& confdata,
						 const Simplex& simp, 
						 const SimplicialComplex& forbidden) {
#ifdef DEBUG
    MessageStreams::debug() << message::lock
			    << "adm before: " << _admissibles << '\n'
			    << message::unlock;
    MessageStreams::debug() << message::lock
			    << "adm of new simp: " << confdata.admtable().find(simp)->second.first << '\n'
			      << message::unlock;
    MessageStreams::debug() << message::lock
			    << "forbidden: " << forbidden << std::endl
			    << message::unlock;
#endif
    _admissibles *= confdata.admtable().find(simp)->second.first;
    _admissibles -= forbidden;
#ifdef DEBUG
    MessageStreams::debug() << message::lock
			    << "adm after: " << _admissibles << '\n'
			    << message::unlock;
#endif
  }

  inline void PartialTriang::_update_freefacets(const ConfData& confdata,
						const Simplex& simp) {
    _freeintfacets ^= confdata.inctable().intfacets().find(simp)->second;
#ifdef USE_CONFFACETS    
    const facets_data& covered_conffacets(confdata.inctable().conffacets().find(simp)->second);
    for (facets_data::const_iterator conffacets_iter = covered_conffacets.begin();
    	 conffacets_iter != covered_conffacets.end();
    	 ++conffacets_iter) {
      _freeconffacets.erase(*conffacets_iter);
    }
#endif
  }

  inline void PartialTriang::_update_volume(const ConfData& confdata,
					    const Simplex& simp) {
    if (confdata.has_voltable() && !confdata.voltable().empty()) {
      _covered_volume += confdata.voltable().find(simp)->second;
    }
  }

  // stream intput/output:
  inline std::istream& PartialTriang::read(std::istream& ist) {
    MessageStreams::debug() << message::lock
			    << "std::istream& read(std::istream& ist): "
			    << "reading not supported because of pointers to global data - exiting"
			    << std::endl
			    << message::unlock;
    exit(1);
    return ist;
  }
  
  inline std::istream& operator>>(std::istream& ist, PartialTriang& pt) {
    return pt.read(ist);
  }
  
  inline std::ostream& PartialTriang::write(std::ostream& ost) const {
    ost << ContainerChars::list_start_char
	<< simplicial_complex()
	<< ContainerChars::delim_char
	<< _admissibles
	<< ContainerChars::delim_char
	<< _freeintfacets
#ifdef USE_CONFFACETS
	<< ContainerChars::delim_char
	<< _freeconffacets
#endif
	<< ContainerChars::delim_char
	<< _covered_volume
	<< ContainerChars::list_end_char;
    return ost;
  }
  
  inline std::ostream& operator<<(std::ostream& ost, const PartialTriang& pt) {
    return pt.write(ost);
  }

}; // namespace topcom

#endif

// eof PartialTriang.hh
