/* 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_GENERIC_IO_H
#define _POLYMAKE_GENERIC_IO_H "$Project: polymake $$Id: GenericIO.h,v 1.3 2004/12/09 14:13:11 gawrilow Exp $"

#ifndef _POLYMAKE_SPARSE_H
#  include <sparse.h>
#endif

namespace pm {

template <typename Data, typename Input> struct make_list_reader;
template <typename Data, typename Input> struct make_sparse_reader;

#if 0
template <typename T, int min_occurs, int max_occurs> struct repeat_item {};
template <typename T> struct zero_or_one : repeat_item<T,0,1> {};
template <typename T> struct zero_or_many : repeat_item<T,0,-1> {};
template <typename T> struct one_or_many : repeat_item<T,1,-1> {};
#endif

template <typename T> struct SuppressDimension { };

template <typename Input>
class GenericInput {
public:
   Input& top() { return static_cast<Input&>(*this); }
   Input& top() const { return static_cast<Input&>(const_cast<GenericInput&>(*this)); }
};

namespace io_test {

struct fallback {
   template <typename Any>
   fallback(const Any&);
};

nothing& operator>> (const fallback&, const fallback&);

template <typename Input, typename Data>
struct input_helper : yes_no_helper {
   static yes* Test(Input&);
   static no* Test(nothing&);
   static GenericInput<Input>& input();
   static Data& data();
};

template <typename Input, typename Data>
struct has_generic_input_operator {
   typedef input_helper<Input,Data> helper;
   static const bool answer= sizeof(*helper::Test( helper::input() >> helper::data() )) == sizeof(typename helper::yes);
};

} // end namespace io_test

template <typename Input>
class GenericInputImpl : public GenericInput<Input> {
public:
   typedef GenericInputImpl generic_type;

   template <typename Data>
   Input& operator>> (Data& data)
   {
      dispatch_retrieve(data,
			(bool2type< io_test::has_generic_input_operator<Input,Data>::answer >*)0);
      return this->top();
   }

protected:
   template <typename Data>
   void dispatch_retrieve(Data& data, False*)
   {
      // no generic output operator found
      dispatch_retrieve(Concrete<Data>::get(data),
			(typename object_traits<typename Concrete<Data>::type>::model*)0);
   }

   template <typename Data>
   void dispatch_retrieve(Data& data, True*)
   {
      static_cast<GenericInput<Input>&>(*this) >> data;
   }

   template <typename Data>
   void dispatch_retrieve(Data& data, is_scalar*)
   {
      this->top().fallback() >> data;
   }

   template <typename Data>
   void dispatch_retrieve(Data& data, is_container*)
   {
      dispatch_retrieve(data,
			(int2type<object_traits<Data>::dimension>*)0);
   }

   template <typename Data>
   void dispatch_retrieve(Data& data, int2type<1>*)
   {
      retrieve_container(data,
			 (typename container_traits<typename Concrete<Data>::type>::category*)0,
			 (bool2type<check_container_feature<typename Concrete<Data>::type, sparse>::answer>*)0
			);
   }

   template <typename Data>
   void retrieve_container(Data& data, forward_iterator_tag*, False*)
   {
      retrieve_container(data,
			 (bool2type< object_traits<Data>::is_ordered >*)0,
			 (bool2type< is_mutable<typename Data::reference>::answer >*)0
			);
   }

   template <typename Data>
   void retrieve_container(Data& data, True*, True*)
   {
      retrieve_list(data);
   }

   template <typename Data>
   void retrieve_container(Data& data, True*, False*)
   {
      retrieve_set(data);
   }

   template <typename Data, bool _is_mutable>
   void retrieve_container(Data& data, False*, bool2type<_is_mutable>*)
   {
      retrieve_insertable(data);
   }

   template <typename Data>
   void retrieve_container(Data& data, random_access_iterator_tag*, False*)
   {
      retrieve_resizable_array(data);
   }

   template <typename Data, typename Category>
   void retrieve_container(Data& data, Category*, True*)
   {
      retrieve_sparse_container(data,
				this->top().get_option((SuppressDimension<void>*)0, (False*)0));
   }

   template <typename Data>
   void retrieve_sparse_container(Data& data, False*)
   {
      int d=retrieve_sparse(data);
      data.resize(d);
   }

   template <typename Data>
   void retrieve_sparse_container(Data& data, True*)
   {
      retrieve_sparse(data);
   }

   template <typename Data>
   void dispatch_retrieve(Data& data, is_composite*)
   {
      retrieve_composite(data);
   }

   template <typename Data>
   void retrieve_array(Data& data, False*);

   template <typename Data>
   void retrieve_array(Data& data, True*);

   template <typename Data>
   void retrieve_resizable_array(Data& data, False*);

   template <typename Data>
   void retrieve_resizable_array(Data& data, True*);

public:
   template <typename Data>
   void retrieve_list(Data& data)
   {
      this->template retrieve_list_as<Data>(data);
   }

   template <typename Masquerade, typename Data>
   void retrieve_list_as(Data& data);

   template <typename Data>
   void retrieve_set(Data& data);

   template <typename Data>
   void retrieve_insertable(Data& data);

   template <typename Data>
   void retrieve_array(Data& data)
   {
      retrieve_array(data, (bool2type<object_traits<Data>::allow_sparse>*)0);
   }

   template <typename Data>
   void retrieve_resizable_array(Data& data)
   {
      retrieve_resizable_array(data, (bool2type<object_traits<Data>::allow_sparse>*)0);
   }

   template <typename Data>
   int retrieve_sparse(Data& data);

   template <typename Data>
   void retrieve_composite(Data& data);

   template <typename Data>
   typename make_list_reader<Data, Input>::type
   create_list_input_iterator(Data*)
   {
      return typename make_list_reader<Data, Input>::type (this->top().begin_list((Data*)0));
   }

   template <typename Data>
   typename make_sparse_reader<Data, Input>::type
   create_sparse_input_iterator(Data*)
   {
      return typename make_sparse_reader<Data, Input>::type (this->top().begin_sparse((Data*)0));
   }
};

template <typename Input>
Input& operator>> (GenericInput<Input>& is, const nothing&) { return is.top(); }

template <typename Input>
template <typename Masquerade, typename Data>
void GenericInputImpl<Input>::retrieve_list_as(Data& data)
{
   typename Input::template list_cursor<Masquerade>::type c=this->top().begin_list((Masquerade*)0);
   typename Data::iterator dst=data.begin(), end=data.end();
   while (dst != end && !c.at_end()) {
      c >> *dst;
      ++dst;
   }

   if (c.at_end()) {
      data.erase(dst, end);
   } else {
      typename Data::value_type item;
      do {
	 c >> item;
	 data.insert(end, item);
      } while (!c.at_end());
   }
}

template <typename Input>
template <typename Data>
void GenericInputImpl<Input>::retrieve_set(Data& data)
{
   data.clear();
   typename Input::template list_cursor<Data>::type c=this->top().begin_list(&data);
   typename Data::value_type item;
   typename Data::iterator end=data.end();
   while (!c.at_end()) {
      c >> item;
      data.insert(end, item);
   }
}

template <typename Input>
template <typename Data>
void GenericInputImpl<Input>::retrieve_insertable(Data& data)
{
   data.clear();
   typename Input::template list_cursor<Data>::type c=this->top().begin_list(&data);
   typename Data::value_type item;
   while (!c.at_end()) {
      c >> item;
      data.insert(item);
   }
}

template <typename Value, typename CursorRef>
class list_reader {
protected:
   alias<CursorRef> cursor;
   typedef typename deref<CursorRef>::type cursor_type;
   Value item;
   bool _end;

   void load()
   {
      cursor_type& c=cursor;
      if (c.at_end())
	 _end=true;
      else
	 c >> item;
   }
public:
   list_reader(typename alias<CursorRef>::arg_type cursor_arg)
      : cursor(cursor_arg), _end(false) { load(); }

   typedef input_iterator_tag iterator_category;
   typedef Value value_type;
   typedef const Value& reference;
   typedef const Value* pointer;
   typedef ptrdiff_t difference_type;

   reference operator* () const { return item; }
   pointer operator-> () const { return &item; }

   bool at_end() const { return _end; }

   list_reader& operator++ () { load(); return *this; }
private:
   list_reader& operator++ (int);
};

template <typename Value, typename CursorRef>
class sparse_reader {
protected:
   alias<CursorRef> cursor;
   typedef typename deref<CursorRef>::type cursor_type;
   pair<int,Value> item;
   bool _end;

   void load(True*)
   {
      cursor_type& c=cursor;
      if (c.at_end())
	 _end=true;
      else
	 c >> item;
   }

   void load(False*)
   {
      ++item.first;
      cursor_type& c=cursor;
      if (c.at_end()) {
	 _end=true;
      } else {
	 c >> item.second;
      }
   }

   static const bool sparse_input=identical<typename cursor_type::value_type, pair<int,Value> >::answer;
   void load()
   {
      load((bool2type<sparse_input>*)0);
   }
public:
   sparse_reader(typename alias<CursorRef>::arg_type cursor_arg)
      : cursor(cursor_arg), _end(false)
   {
      if (!sparse_input) item.first=-1;
      load();
   }

   typedef input_iterator_tag iterator_category;
   typedef Value value_type;
   typedef const Value& reference;
   typedef const Value* pointer;
   typedef ptrdiff_t difference_type;

   reference operator* () const { return item.second; }
   pointer operator-> () const { return &item.second; }
   int index() const { return item.first; }

   bool at_end() const { return _end; }

   sparse_reader& operator++ () { load(); return *this; }
private:
   // better not to define it at all
   sparse_reader& operator++ (int);
};

template <typename Data, typename Input>
struct make_list_reader {
   typedef list_reader<typename Data::value_type, typename Input::template list_cursor<Data>::type> type;
};

template <typename Data, typename Input>
struct make_sparse_reader {
   typedef sparse_reader<typename Data::value_type, typename Input::template sparse_cursor<Data>::type> type;
};

template <typename Input>
template <typename Data>
void GenericInputImpl<Input>::retrieve_resizable_array(Data& data, True*)
{
   typedef typename Data::value_type V;
   if (this->top().has_sparse_representation((V*)0)) {
      data.assign(this->top().get_dim(), V());
      typedef typename Input::template sparse_cursor<Data>::type cursor_type;
      cursor_type c=this->top().begin_sparse(&data);
      for (sparse_reader<V, cursor_type&> src(c); !src.at_end(); ++src)
	 data[src.index()]=*src;

   } else {
      retrieve_resizable_array(data, (False*)0);
   }
}

template <typename Input>
template <typename Data>
void GenericInputImpl<Input>::retrieve_resizable_array(Data& data, False*)
{
   typedef typename Input::template list_cursor<Data>::type cursor_type;
   cursor_type c=this->top().begin_list(&data);
   data.resize(c.size());
   for (typename Entire<Data>::iterator dst=entire(data); !dst.at_end(); ++dst)
      c >> *dst;
   c.finish();
}

template <typename Input>
template <typename Data>
void GenericInputImpl<Input>::retrieve_array(Data& data, True*)
{
   typedef typename Data::value_type V;
   if (this->top().has_sparse_representation((V*)0)) {
      data.fill(V());
      typedef typename Input::template sparse_cursor<Data>::type cursor_type;
      cursor_type c=this->top().begin_sparse(&data);
      for (sparse_reader<V, cursor_type&> src(c); !src.at_end(); ++src)
	 data[src.index()]=*src;

   } else {
      retrieve_array(data, (False*)0);
   }
}

template <typename Input>
template <typename Data>
void GenericInputImpl<Input>::retrieve_array(Data& data, False*)
{
   typedef typename Input::template list_cursor<Data>::type cursor_type;
   cursor_type c=this->top().begin_list(&data);
   for (typename Entire<Data>::iterator dst=entire(data); !dst.at_end(); ++dst)
      c >> *dst;
   c.finish();
}

template <typename Input>
template <typename Data>
int GenericInputImpl<Input>::retrieve_sparse(Data& data)
{
   typedef typename Data::value_type V;
   int d;
   if (this->top().has_sparse_representation((V*)0)) {
      typedef typename Input::template sparse_cursor<Data>::type cursor_type;
      cursor_type c=this->top().begin_sparse(&data);
      d=assign_sparse(data, sparse_reader<V, cursor_type&>(c)).index();
   } else {
      typedef typename Input::template list_cursor<Data>::type cursor_type;
      cursor_type c=this->top().begin_list(&data);
      d=assign_sparse(data, make_unary_predicate_selector(sparse_reader<V, cursor_type&>(c), conv<V,bool>())).index();
   }
   return d;
}

template <typename T, typename CursorRef>
class composite_reader {
protected:
   alias<CursorRef> cursor;
   typedef typename deref<CursorRef>::type cursor_type;

public:
   typedef typename deref<T>::type value_type;

   composite_reader(typename alias<CursorRef>::arg_type cursor_arg) : cursor(cursor_arg) { }

   void operator<< (typename attrib<T>::plus_ref elem)
   {
      cursor_type& c=cursor;
      if (c.at_end()) {
	 operations::clear<value_type> clear;
	 clear.assign(const_cast<value_type&>(elem));
      } else {
	 c >> const_cast<value_type&>(elem);
	 c.finish();
      }
   }
};

template <typename Car, typename Cdr, typename CursorRef>
class composite_reader< cons<Car,Cdr>, CursorRef> {
protected:
   alias<CursorRef> cursor;
   typedef typename deref<CursorRef>::type cursor_type;
public:
   typedef typename deref<Car>::type value_type;

   composite_reader(typename alias<CursorRef>::arg_type cursor_arg) : cursor(cursor_arg) { }

   composite_reader<Cdr, CursorRef>& operator<< (typename attrib<Car>::plus_ref elem)
   {
      cursor_type& c=cursor;
      if (c.at_end()) {
	 operations::clear<value_type> clear;
	 clear.assign(const_cast<value_type&>(elem));
      } else {
	 c >> const_cast<value_type&>(elem);
      }
      return reinterpret_cast<composite_reader<Cdr, CursorRef>&>(*this);
   }
};

template <typename Input>
template <typename Data>
void GenericInputImpl<Input>::retrieve_composite(Data& data)
{
   typedef typename Input::template composite_cursor<Data>::type cursor_type;
   cursor_type c=this->top().begin_composite(&data);
   composite_reader<typename object_traits<Data>::elements, cursor_type&> r(c);
   object_traits<Data>::visit_elements(data, r);
}

template <typename Output>
class GenericOutput {
public:
   Output& top() { return static_cast<Output&>(*this); }
   Output& top() const { return static_cast<Output&>(const_cast<GenericOutput&>(*this)); }
};

namespace io_test {

nothing& operator<< (const fallback&, const fallback&);

template <typename Output, typename Data>
struct output_helper : yes_no_helper {
   static yes* Test(Output&);
   static no* Test(nothing&);
   static GenericOutput<Output>& output();
   static const Data& data();
};

template <typename Output, typename Data>
struct has_generic_output_operator {
   typedef output_helper<Output,Data> helper;
   static const bool answer= sizeof(*helper::Test( helper::output() << helper::data() )) == sizeof(typename helper::yes);
};

} // end namespace io_test

template <typename Output>
class GenericOutputImpl : public GenericOutput<Output> {
public:
   typedef GenericOutputImpl generic_type;

   template <typename Data>
   Output& operator<< (const Data& data)
   {
      dispatch_store(data,
		     (bool2type< io_test::has_generic_output_operator<Output,Data>::answer >*)0);
      return this->top();
   }

protected:
   template <typename Data>
   void dispatch_store(const Data& data, False*)
   {
      // no generic output operator found
      dispatch_store(Concrete<Data>::get(data),
		     (typename object_traits<typename Concrete<Data>::type>::model*)0);
   }

   template <typename Data>
   void dispatch_store(const Data& data, True*)
   {
      static_cast<GenericOutput<Output>&>(*this) << data;
   }

   template <typename Data>
   void dispatch_store(const Data& data, is_scalar*)
   {
      this->top().fallback() << data;
   }

   template <typename Data>
   void dispatch_store(const Data& data, is_container*)
   {
      dispatch_store(data,
		     (int2type<object_traits<Data>::dimension>*)0);
   }

   template <typename Data>
   void dispatch_store(const Data& data, int2type<1>*)
   {
      store_container(data,
		      (bool2type<check_container_feature<typename Concrete<Data>::type, sparse>::answer>*)0
		     );
   }

   template <typename Data>
   void store_container(const Data& data, False*)
   {
      store_list(data);
   }

   template <typename Data>
   void store_container(const Data& data, True*)
   {
      store_sparse(data);
   }

   template <typename Data>
   void dispatch_store(const Data& data, is_composite*)
   {
      store_composite(data);
   }

   template <typename Data>
   bool prefer_sparse_representation(const Data& data) const
   {
      return data.size()*2 < data.dim();
   }

public:
   template <typename Data>
   void store_list(const Data& data)
   {
      this->template store_list_as<Data>(data);
   }

   template <typename Data>
   void store_sparse(const Data& data);

   template <typename Data>
   void store_composite(const Data& data);

   template <typename Masquerade, typename Data>
   void store_list_as(const Data& data);
};

template <typename Output>
Output& operator<< (GenericOutput<Output>& os, const nothing&) { return os.top(); }

template <typename Output>
template <typename Masquerade, typename Data>
void GenericOutputImpl<Output>::store_list_as(const Data& data)
{
   typedef typename Output::template list_cursor<Masquerade>::type cursor_type;
   cursor_type c=this->top().begin_list((Masquerade*)0);
   for (typename ensure_features<Data, cons<dense, cons<end_sensitive, reference_relaxed> > >::const_iterator
	   src=ensure(data, (cons<dense, cons<end_sensitive, reference_relaxed> >*)0).begin();
	!src.at_end(); ++src)
      c << *src;
   c.finish();
}

template <typename Iterator>
class indexed_pair : public Iterator {
protected:
   indexed_pair();
   ~indexed_pair();
};

template <typename Iterator>
struct spec_object_traits< indexed_pair<Iterator> > : spec_object_traits<is_composite> {
   typedef cons<int, typename iterator_traits<Iterator>::reference> elements;
   template <typename Visitor>
   static void visit_elements(const indexed_pair<Iterator>& it, Visitor& v)
   {
      v << it.index() << *it;
   }
};

template <typename Container>
class IndexedPairs
   : public modified_container_impl< IndexedPairs<Container>,
               cons< Hidden< Container >,
               cons< Accessor< typename if_else<identical<typename Container::value_type, nothing>::answer,
						BuildUnaryIt<operations::index2element>,
						operations::masquerade_it<indexed_pair> >::result >,
		     ExpectedFeatures< reference_relaxed > > >
            > {
protected:
   ~IndexedPairs();
};

template <typename Container> inline
const IndexedPairs<Container>&
indexed_pairs(const Container& c) {
   return reinterpret_cast<const IndexedPairs<Container>&>(c);
}

template <typename Output>
template <typename Data>
void GenericOutputImpl<Output>::store_sparse(const Data& data)
{
   if (this->top().special_sparse_representation(data)) {
      typedef typename Output::template sparse_cursor<Data>::type cursor_type;
      cursor_type c=this->top().begin_sparse(&data);
      for (typename Entire<Data>::const_iterator src=entire(data);  !src.at_end(); ++src)
	 c << src;
      c << pair<int,nothing>(data.dim());
      c.finish();

   } else if (prefer_sparse_representation(data)) {
      typedef typename Output::template list_cursor< IndexedPairs<Data> >::type cursor_type;
      const IndexedPairs<Data>& vdata=indexed_pairs(data);
      cursor_type c=this->top().begin_list(&vdata);
      for (typename Entire< IndexedPairs<Data> >::const_iterator src=entire(vdata);  !src.at_end(); ++src)
	 c << *src;
      if (! this->top().get_bool_option((SuppressDimension<void>*)0, (False*)0))
	 c << item2composite(data.dim());
      c.finish();

   } else {
      store_list(data);
   }
}

template <typename T, typename CursorRef>
class composite_writer {
protected:
   alias<CursorRef> cursor;
   typedef typename deref<CursorRef>::type cursor_type;
public:
   composite_writer(typename alias<CursorRef>::arg_type cursor_arg) : cursor(cursor_arg) { }

   void operator<< (typename attrib<T>::plus_const_ref elem) {
      cursor_type& c=cursor;
      c << elem;
      c.finish();
   }
};

template <typename Car, typename Cdr, typename CursorRef>
class composite_writer< cons<Car,Cdr>, CursorRef> {
protected:
   alias<CursorRef> cursor;
   typedef typename deref<CursorRef>::type cursor_type;
public:
   composite_writer(typename alias<CursorRef>::arg_type cursor_arg) : cursor(cursor_arg) { }

   composite_writer<Cdr, CursorRef>& operator<< (typename attrib<Car>::plus_const_ref elem) {
      cursor_type& c=cursor;
      c << elem;
      return reinterpret_cast<composite_writer<Cdr, CursorRef>&>(*this);
   }
};

template <typename Output>
template <typename Data>
void GenericOutputImpl<Output>::store_composite(const Data& data)
{
   typedef typename Output::template composite_cursor<Data>::type cursor_type;
   cursor_type c=this->top().begin_composite((Data*)0);
   composite_writer<typename object_traits<Data>::elements, cursor_type&> w(c);
   object_traits<Data>::visit_elements(data, w);
}

template <typename Output, typename Data> inline
Output& operator<< (GenericOutput<Output>& os, const ignore<Data,nothing>&)
{
   return os.top();
}

template <typename Output, typename Data, typename Expected> inline
Output& operator<< (GenericOutput<Output>& os, const ignore<Data,Expected>&)
{
   const operations::clear<Expected> dummy;
   return os.top() << dummy();
}

template <typename Input, typename Data, typename Expected> inline
Input& operator>> (GenericInput<Input>& is, ignore<Data,Expected>&)
{
   if (identical<Data,nothing>::answer || !identical<Expected,nothing>::answer)
      is.top().skip_item();
   return is.top();
}

template <typename Container>
class IO_Array : public Container {
protected:
   IO_Array();
   ~IO_Array();

   template <typename Output>
   void output(Output& os) const
   {
      os.template store_list_as<IO_Array>(static_cast<const Container&>(*this));
   }
public:
   template <typename Output> friend
   Output& operator<< (GenericOutput<Output>& os, const IO_Array& x)
   {
      x.output(os.top());
      return os.top();
   }
};

template <typename Container>
struct spec_object_traits< IO_Array<Container> >
   : generic_object_traits<Container> {
   static const IO_separator_kind IO_separator=IO_sep_containers;
};

template <typename Container>
class IO_List : public Container {
protected:
   IO_List();
   ~IO_List();

   template <typename Output>
   void output(Output& os) const
   {
      os.template store_list_as<IO_List>(static_cast<const Container&>(*this));
   }
public:
   template <typename Output> friend
   Output& operator<< (GenericOutput<Output>& os, const IO_List& x)
   {
      x.output(os.top());
      return os.top();
   }
};

template <typename Container>
struct spec_object_traits< IO_List<Container> >
   : generic_object_traits<Container> {
   static const IO_separator_kind IO_separator=IO_sep_inherit;
};

template <typename Container> inline
const IO_Array<Container>& as_array(const Container& c) {
   return reinterpret_cast<const IO_Array<Container>&>(c);
}

template <typename Container> inline
const IO_List<Container>& as_list(const Container& c) {
   return reinterpret_cast<const IO_List<Container>&>(c);
}

} // end namespace pm

namespace polymake {
   using pm::GenericInput;
   using pm::GenericOutput;
   using pm::as_array;
   using pm::as_list;
}

#ifndef _POLYMAKE_OSTREAM_WRAPPER_H
#  include <ostream_wrapper.h>
#endif

#endif // _POLYMAKE_GENERIC_IO_H

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