/* Copyright (c) 1997-2004
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
   http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
*/

#ifndef _POLYMAKE_POLY_H
#define _POLYMAKE_POLY_H "$Project: polymake $$Id: Poly.h,v 1.42 2004/11/15 20:33:12 gawrilow Exp $"

#if defined(__GNUC__) && __GNUC_MINOR__<3 && !defined(__APPLE__) && !defined(__COMO__)
#pragma interface
#endif

#include <iostream>
#include <string>
#include <stdexcept>
#include <cstdarg>

#ifndef _POLYMAKE_GENERIC_IO_H
#  include <GenericIO.h>
#endif

class Rational;

namespace pm { namespace client {

class streambuf;
class streambuf_holder;

template <typename Value, typename Options> class istream_list_cursor;
template <typename Options> class istream_composite_cursor;

class istream_common {
protected:
   typedef std::istream istream;

   istream *is;
   mutable char *end_of_seek, *end_of_data;

   istream_common(istream& is_arg)
      : is(&is_arg), end_of_seek(0), end_of_data(0) { }
public:
   ~istream_common();

   istream& fallback() const { return *is; }

   void finish() const { }
   bool at_end();

   void get_scalar(double&);
   void get_scalar(Rational&);
   void get_string(std::string&, char delim);

   int count_lines();
protected:
   void set_range(char opening, char closing);
   void discard_range(char closing);
   int count_words();
   int count_braced(char opening, char closing);
   int count_leading(char);
public:
   int get_dim();
   int skip_item();
};

template <typename Object, typename Model=typename object_traits<Object>::model>
struct composite_depth {
   static const int answer=0;
};

template <typename Object>
struct composite_depth<Object, is_composite> {
   static const int answer= composite_depth<typename n_th<typename object_traits<Object>::elements, 0>::car>::answer + 1;
};

template <typename Options=void>
class istream_base
   : public istream_common,
     public GenericInputImpl< istream_base<Options> > {
public:
   istream_base(istream& is_arg) : istream_common(is_arg) { }

   template <typename OptionInst, typename Default>
   typename extract_by_instance<Options, OptionInst, Default>::result*
   get_option(OptionInst*, Default*) const
   {
      return (typename extract_by_instance<Options, OptionInst, Default>::result*)0;
   }

   template <typename OptionInst, typename Default>
   bool get_bool_option(OptionInst*, Default*) const
   {
      typedef typename extract_by_instance<Options, OptionInst, Default>::result option;
      return option::value;
   }

   template <typename OptionInst, typename Default>
   int get_int_option(OptionInst*, Default*) const
   {
      typedef typename extract_by_instance<Options, OptionInst, Default>::result option;
      return option::value;
   }

   template <typename OptionInst>
   istream_base<typename replace_by_instance<Options, OptionInst>::result>&
   set_option(OptionInst*)
   {
      return reinterpret_cast<istream_base<typename replace_by_instance<Options, OptionInst>::result>&>(*this);
   }

   typedef ostream_wrapper_base<Options> wrapper;

   template <typename ObjectRef>
   struct list_cursor {
      typedef istream_list_cursor<typename deref<ObjectRef>::type::value_type,
				  typename wrapper::template list_cursor<ObjectRef>::cursor_options>
         type;
   };

   template <typename ObjectRef>
   struct sparse_cursor {
      typedef istream_list_cursor<pair<int, typename deref<ObjectRef>::type::value_type>,
				  typename wrapper::template list_cursor<ObjectRef>::cursor_options>
         type;
   };

   template <typename ObjectRef>
   struct composite_cursor {
      typedef istream_composite_cursor<typename wrapper::template composite_cursor<ObjectRef>::cursor_options> type;
   };

   template <typename Object>
   typename list_cursor<Object>::type begin_list(Object*) const
   {
      return typename list_cursor<Object>::type(*is);
   }

   template <typename Object>
   typename sparse_cursor<Object>::type begin_sparse(Object*) const
   {
      return typename sparse_cursor<Object>::type(*is);
   }

   template <typename Object>
   typename composite_cursor<Object>::type begin_composite(Object*) const
   {
      return typename composite_cursor<Object>::type(*is);
   }

protected:
   template <typename Object, typename Model>
   static int missing_parens(Object*, Model*) { return 0; }

   template <typename Object>
   static int missing_parens(Object*, is_composite*)
   {
      return wrapper::template composite_cursor<Object>::opening == 0;
   }
public:
   template <typename Object>
   bool has_sparse_representation(Object*)
   {
      const int own_missing=missing_parens((Object*)0, (typename object_traits<Object>::model*)0);
      return count_leading('(') == composite_depth<Object>::answer - own_missing + 1;
   }

   operator void* () const { return is->good() ? (void*)this : 0; }
   bool operator! () const { return !is->good(); }
};

template <typename Options> inline
istream_base<Options>&
operator>> (GenericInput< istream_base<Options> >& is, double& x)
{
   is.top().get_scalar(x);
   return is.top();
}

template <typename Options> inline
istream_base<Options>&
operator>> (GenericInput< istream_base<Options> >& is, Rational& x)
{
   is.top().get_scalar(x);
   return is.top();
}

template <typename Options> inline
istream_base<Options>&
getline (istream_base<Options>& is, std::string& s, char delim='\n')
{
   is.get_string(s,delim);
   return is;
}

template <typename Options> inline
istream_base<Options>&
operator>> (GenericInput< istream_base<Options> >& is, std::string& s)
{
   is.top().get_string(s,0);
   return is.top();
}

template <typename Options>
class istream_composite_cursor : public istream_base<Options> {
   typedef istream_base<Options> super;
protected:
   static const char
      opening=extract_int_param<Options, OpeningBracket>::result,
      closing=extract_int_param<Options, ClosingBracket>::result,
      separator=extract_int_param<Options, SeparatorChar>::result;

public:
   istream_composite_cursor(const istream_composite_cursor& other)
      : super(other)
   {
      other.end_of_data=0;
   }

   istream_composite_cursor(typename super::istream& is_arg)
      : super(is_arg)
   {
      if (opening) this->set_range(opening, closing);
   }

   void finish()
   {
      if (closing) this->discard_range(closing);
   }

   bool at_end()
   {
      return super::at_end() && (finish(), true);
   }
};

template <typename Value, typename Options>
class istream_list_cursor : public istream_composite_cursor<Options> {
   typedef istream_composite_cursor<Options> super;
protected:
   int _size;

   int size(is_scalar*)
   {
      return this->count_words();
   }

   int size(is_container*)
   {
      typedef typename super::wrapper::template list_cursor<Value> defs;
      return defs::opening
	     ? this->count_braced(defs::opening, defs::closing)
	     : this->count_lines();
   }

   int size(is_composite*)
   {
      typedef typename super::wrapper::template composite_cursor<Value> defs;
      return defs::opening
	     ? this->count_braced(defs::opening, defs::closing)
	     : super::separator=='\n'
	       ? this->count_lines()
	       : this->count_words();
   }

public:
   typedef Value value_type;

   istream_list_cursor(typename super::istream& is_arg)
      : super(is_arg), _size(-1)
   {
      if (!super::opening && object_traits<Value>::total_dimension==0)
	 this->set_range(0, '\n');
   }

   int size()
   {
      if (_size<0)
	 _size=size((typename object_traits<Value>::model*)0);
      return _size;
   }
};

class Object {
private:
   std::ios::openmode mode;
   int id;

   static std::iostream stream_inst;
public:
   Object() : mode(std::ios::openmode(0)) { }

   /// @param object_arg filename or 0 if the object is temporary
   Object(const char *object_arg, std::ios::openmode mode_arg=std::ios::in, const char *obj_type=0)
      : mode(std::ios::openmode(0))
   {
      init(object_arg,mode_arg,obj_type);
   }

   Object(const char *object_arg, std::ios::openmode mode_arg, const std::string& obj_type)
      : mode(std::ios::openmode(0))
   {
      init(object_arg,mode_arg,obj_type.c_str());
   }

   static void set_options(const char *options_arg);

   void init(const char *object_arg, std::ios::openmode mode_arg=std::ios::in, const char *obj_type=0);
   ~Object();

   std::ios::openmode get_mode() const { return mode; }

   struct undefined { };

   class istream_germ;
   class ostream_germ;

   istream_germ give(const char *req);
   istream_germ lookup(const char *req);
   ostream_germ take(const char *req);
   const std::string& given() const;
   bool exists(const char *req);
   bool defined(const char *req);
   std::string type();
   bool isa(const char *type);

   bool vprovide (const char *first, va_list sections);

   bool provide (const char *first, ...) {
      va_list sections;
      va_start(sections,first);
      bool answer=vprovide(first,sections);
      va_end(sections);
      return answer;
   }

   void vremove (const char *first, va_list sections);

   void remove (const char *first, ...) {
      va_list sections;
      va_start(sections,first);
      vremove(first,sections);
      va_end(sections);
   }
};

class Object::istream_germ : public istream_base<> {
   friend class Object;
   typedef istream_base<> super;
protected:
   istream_germ(streambuf *buf);
   istream_germ(streambuf *buf, std::ios::iostate exc_flags);

public:
   template <typename T>
   super& operator>> (T& x)
   {
      if (!is->eof()) static_cast<super&>(*this) >> x;
      return *this;
   }

   istream_germ(const istream_germ&);
   ~istream_germ();

   template <typename T>
   operator T ()
   {
      T x;
      static_cast<super&>(*this) >> x;
      return x;
   }
};

class Object::ostream_germ : public ostream_wrapper<> {
   friend class Object;
   typedef ostream_wrapper<> super;
protected:
   ostream_germ(streambuf *buf);

public:
   template <typename T>
   super& operator<< (const T& x)
   {
      static_cast<super&>(*this) << x;
      return static_cast<super&>(*this);
   }

   // this one is void since it makes no sense to print trailing data after an UNDEFINED value
   void operator<< (const undefined&);

   ostream_germ(const ostream_germ&);
   ~ostream_germ();
};
} } // end namespace pm::client

namespace polymake {
   typedef pm::client::Object Poly;	// the historical name
}

#endif // _POLYMAKE_POLY_H

// Local Variables:
// mode:C++
// End:
