/* Copyright (c) 1997-2006
   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 7386 2006-06-11 22:31:15Z gawrilow $"

#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 Options=void, typename Traits=std::ostream::traits_type>
class ostream_wrapper
   : public GenericOutputImpl< ostream_wrapper<Options,Traits> >,
     public GenericIOoptions< ostream_wrapper<Options,Traits>, Options> {
public:
   typedef std::basic_ostream<char,Traits> ostream;
   typedef GenericOutputImpl<ostream_wrapper> generic_impl;
protected:
   ostream* os;
public:
   ostream_wrapper(ostream& os_arg) : os(&os_arg) { }

   template <typename Data>
   void fallback(const Data& x) { *os << x; }

   void fallback(const char *s, size_t n) { os->write(s,n); }

   void finish() const { }

   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 bool in_composite=extract_int_param<Options, OpeningBracket>::value == '(' ||
                                     extract_int_param<Options, SeparatorChar>::value == ' ' ||
                                     extract_bool_param<Options, SparseRepresentation>::value;
      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 || in_composite)
                  ? '<' : 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 || in_composite)
                  ? '>' : 0,
         sep= object_traits<Object>::IO_separate_elements_with_eol ||
              object_traits<typename Object::value_type>::IO_ends_with_eol
              ? '\n' : ' ';

      typedef typename replace_params<
	      typename remove_bool_param<Options, SparseRepresentation>::type,
	         cons< OpeningBracket< int2type<opening> >,
		 cons< ClosingBracket< int2type<closing> >,
		       SeparatorChar< int2type<sep> > > > >::type
         cursor_options;
      typedef ostream_composite_cursor<cursor_options, Traits> type;

#ifdef POLY_DEBUG
      static void dump()
      {
	 std::cout << "Object: " << typeid(Object).name()
		   << "\nOptions: " << typeid(Options).name()
		   << "\n  top_level=" << top_level
		   << "\n  in_composite=" << in_composite
		   << "\n  separator=" << int(object_traits<Object>::IO_separator)
		   << "\n  eol=" << object_traits<Object>::IO_ends_with_eol
		   << "\n-> opening=" << char(opening)
		   << "\n   closing=" << char(closing)
		   << "\n   sep=" << char(sep)
		   << std::endl;
      }
#endif
   };

   template <typename ObjectRef>
   struct sparse_cursor {
      typedef typename deref<ObjectRef>::type Object;
      typedef typename replace_params<typename list_cursor<Object>::cursor_options,
				      HideDimension< bool2type<object_traits<Object>::IO_hide_dim> > >::type
         cursor_options;
      typedef ostream_sparse_cursor<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>::value,
	 ignored= list_accumulate_unary<list_count, ignore_in_composite, typename object_traits<Object>::elements>::value;
      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_params<
	      typename remove_bool_param<Options, SparseRepresentation>::type,
	         cons< OpeningBracket< int2type<opening> >,
		 cons< ClosingBracket< int2type<closing> >,
		       SeparatorChar< int2type<sep> > > > >::type
         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* x) const
   {
      return typename sparse_cursor<Object>::type(*os, x->dim());
   }

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

   template <typename Object>
   bool prefer_sparse_representation(const Object& x) const
   {
      return os->width()>0 || generic_impl::prefer_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<Options,Traits>&
operator<< (ostream_wrapper<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

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

public:
   ostream_composite_cursor(typename super::ostream& os_arg, bool no_opening_by_width=false)
      : super(os_arg), pending_sep(0), width(os_arg.width())
   {
      if (opening) {
	 if (width) {
	    if (!no_opening_by_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_composite_cursor<Options, Traits> {
   typedef ostream_composite_cursor<Options, Traits> super;
protected:
   int next_index, dim;

public:
   ostream_sparse_cursor(typename super::ostream& os_arg, int dim_arg)
      : super(os_arg,true), next_index(0), dim(dim_arg)
   {
      if (!extract_bool_param<Options, HideDimension>::value && !this->width)
	 // print the dimension
	 static_cast<super&>(*this) << item2composite(dim);
   }

   template <typename Iterator>
   ostream_sparse_cursor& operator<< (const Iterator& x)
   {
      if (this->width) {
	 int i=x.index();
	 while (next_index < i) {
	    *this->os << std::setw(this->width) << '.';
	    ++next_index;
	 }
	 *this->os << std::setw(this->width);
	 static_cast<super&>(*this) << *x;
	 ++next_index;
      } else {
	 static_cast<super&>(*this) << reinterpret_cast<const indexed_pair<Iterator>&>(x);
      }
      return *this;
   }

   void finish()
   {
      if (this->width) {
	 while (next_index < dim) {
	    *this->os << std::setw(this->width) << '.';
	    ++next_index;
	 }
      } else {
	 super::finish();
      }
   }
};

} // 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:
