/* 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_OSTREAM_WRAPPER_H
#define _POLYMAKE_OSTREAM_WRAPPER_H "$Project: polymake $$Id: ostream_wrapper.h,v 1.2 2004/06/22 16:22:42 gawrilow Exp $"

#include <iomanip>

#ifndef _POLYMAKE_TYPE_MANIP_H
#  include <type_manip.h>
#endif

namespace pm {

template <typename> class OpeningBracket;
template <typename> class SeparatorChar;
template <typename> class ClosingBracket;
template <typename Options, typename Traits> class ostream_composite_cursor;
template <typename Options, typename Traits> class ostream_sparse_cursor;

template <typename T>
struct ignore_in_composite : False { };

template <>
struct ignore_in_composite<nothing> : True { };

template <typename T, typename Expected>
struct ignore_in_composite< ignore<T,Expected> > : identical<Expected,nothing> { };

template <typename T>
struct ignore_in_composite<const T> : ignore_in_composite<T> { };

template <typename T>
struct ignore_in_composite<T&> : ignore_in_composite<T> { };

template <typename Options, typename Traits=std::ostream::traits_type>
class ostream_wrapper_base : public GenericOutputImpl< ostream_wrapper_base<Options,Traits> > {
public:
   typedef std::basic_ostream<char,Traits> ostream;
protected:
   ostream* os;

   ostream_wrapper_base(ostream& os_arg) : os(&os_arg) { }

public:
   ostream& fallback() const { return *os; }

   void finish() const { }

   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>
   ostream_wrapper_base<typename replace_by_instance<Options, OptionInst>::result, Traits>&
   set_option(OptionInst*)
   {
      return reinterpret_cast<ostream_wrapper_base<typename replace_by_instance<Options, OptionInst>::result, Traits>&>(*this);
   }

   template <typename ObjectRef>
   struct list_cursor {
      typedef typename deref<ObjectRef>::type Object;
      static const bool top_level=!extract_type_param<Options, SeparatorChar>::specified;
      static const int
         opening= object_traits<Object>::IO_separator==IO_sep_inherit && !object_traits<Object>::IO_ends_with_eol
                  ? '{'
                  : !top_level && object_traits<Object>::IO_ends_with_eol
                  ? '<' : 0,
         closing= object_traits<Object>::IO_separator==IO_sep_inherit && !object_traits<Object>::IO_ends_with_eol
                  ? '}'
                  : !top_level && object_traits<Object>::IO_ends_with_eol
                  ? '>' : 0,
         sep= object_traits<Object>::IO_separate_elements_with_eol ||
              object_traits<typename Object::value_type>::IO_ends_with_eol
              ? '\n' : ' ';

      typedef typename replace_int_param<
	      typename replace_int_param<
	      typename replace_int_param<Options,
					 OpeningBracket, opening>::result,
	                                 ClosingBracket, closing>::result,
	                                 SeparatorChar, sep>::result
         cursor_options;
      typedef ostream_composite_cursor<cursor_options, Traits> type;
   };

   template <typename ObjectRef>
   struct sparse_cursor {
      typedef ostream_sparse_cursor<typename list_cursor<ObjectRef>::cursor_options, Traits> type;
   };

   template <typename ObjectRef>
   struct composite_cursor {
      typedef typename deref<ObjectRef>::type Object;
      static const int
         total= list_length<typename object_traits<Object>::elements>::answer,
	 ignored= list_count_unary<ignore_in_composite, typename object_traits<Object>::elements>::answer;
      static const bool
         top_level=!extract_type_param<Options, SeparatorChar>::specified,
         compress= ignored>0 && total-ignored<=1;
      static const int
         opening = top_level || compress ? 0 : '(',
	 closing = top_level || compress ? 0 : ')',
	 sep= compress ? 0 : object_traits<Object>::IO_ends_with_eol ? '\n' : ' ';

      typedef typename replace_int_param<
	      typename replace_int_param<
	      typename replace_int_param<Options,
					 OpeningBracket, opening>::result,
	                                 ClosingBracket, closing>::result,
	                                 SeparatorChar, sep>::result
         cursor_options;
      typedef ostream_composite_cursor<cursor_options, Traits> type;
   };

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

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

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

   template <typename Object>
   bool special_sparse_representation(const Object& x) const
   {
      return os->width()>0;
   }
};

template <typename Options, typename Traits>
class ostream_composite_cursor
   : public ostream_wrapper_base<Options, Traits> {
   typedef ostream_wrapper_base<Options, Traits> super;
protected:
   char pending_sep;
   int width;
   static const char
      sep=extract_int_param<Options, SeparatorChar>::result,
      opening=extract_int_param<Options, OpeningBracket>::result,
      closing=extract_int_param<Options, ClosingBracket>::result;

public:
   ostream_composite_cursor(typename super::ostream& os_arg)
      : super(os_arg), pending_sep(0), width(os_arg.width())
   {
      if (opening) {
	 if (width)
	    *this->os << std::setw(0) << opening;
	 else
	    *this->os << opening;
      }
   }

   template <typename T>
   ostream_composite_cursor& operator<< (const T& x)
   {
      if (pending_sep) *this->os << pending_sep;
      if (width) *this->os << std::setw(width);
      static_cast<super&>(*this) << x;
      if (sep=='\n') {
	 if (! object_traits<T>::IO_ends_with_eol) *this->os << sep;
      } else {
	 if (!width) pending_sep=sep;
      }
      return *this;
   }

   ostream_composite_cursor& operator<< (const nothing&) { return *this; }

   template <typename T>
   ostream_composite_cursor& operator<< (const ignore<T>&) { return *this; }

   void finish()
   {
      if (closing) {
	 *this->os << closing;
	 if (sep=='\n') *this->os << sep;
      }
      pending_sep=0;
   }
};

template <typename Options, typename Traits>
class ostream_sparse_cursor
   : public ostream_wrapper_base<Options, Traits> {
   typedef ostream_wrapper_base<Options, Traits> super;
protected:
   int next_index, width;

public:
   ostream_sparse_cursor(typename super::ostream& os_arg)
      : super(os_arg), next_index(0), width(os_arg.width()) { }

   template <typename Iterator>
   ostream_sparse_cursor& operator<< (const Iterator& x)
   {
      while (next_index < x.index()) {
	 *this->os << std::setw(width) << ".";
	 ++next_index;
      }
      *this->os << std::setw(width);
      static_cast<super&>(*this) << *x;
      ++next_index;
      return *this;
   }

   // print the dimension
   ostream_sparse_cursor& operator<< (const pair<int,nothing>& x)
   {
      while (next_index < x.first) {
	 *this->os << std::setw(width) << ".";
	 ++next_index;
      }
      return *this;
   } 
};

template <typename Options=void, typename Traits=std::ostream::traits_type>
class ostream_wrapper
   : public ostream_wrapper_base<Options, Traits> {
   typedef ostream_wrapper_base<Options, Traits> super;
public:
   ostream_wrapper(typename super::ostream& os_arg) : super(os_arg) { }

   template <typename T>
   bool choose_sparse_representation(const T& x) const
   {
      return this->os->width()>0 || super::choose_sparse_representation(x);
   }
};

template <typename Traits> inline
ostream_wrapper<void,Traits> wrap(std::basic_ostream<char,Traits>& os)
{
   return os;
}

extern ostream_wrapper<> cout;
extern ostream_wrapper<> cerr;

#if defined(__COMO__) || defined(__INTEL_COMPILER)

// these compiler can't resolve the expression ostream_wrapper << std::endl
enum std_manip { endl, ends };

template <typename Traits> inline
std::basic_ostream<char,Traits>& operator<< (std::basic_ostream<char,Traits>& os, std_manip manip)
{
   switch (manip) {
   case endl:
      os << std::endl; break;
   case ends:
      os << std::ends; break;
   }
   return os;
}

#else

template <typename Options, typename Traits> inline
ostream_wrapper_base<Options,Traits>&
operator<< (ostream_wrapper_base<Options,Traits>& os, std::basic_ostream<char,Traits>& (*manip)(std::basic_ostream<char,Traits>&))
{
   os.fallback() << manip;
   return os;
}

using std::endl;
using std::ends;
#endif

} // end namespace pm

namespace polymake {

using pm::cout;
using pm::cerr;
using pm::endl;
using pm::ends;
using std::ios;
using pm::wrap;

}

#endif // _POLYMAKE_OSTREAM_WRAPPER_H

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