/* Copyright (c) 1997-2005
   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_STRUCT_H
#define _POLYMAKE_GENERIC_STRUCT_H "$Project: polymake $$Id: GenericStruct.h 7315 2006-04-02 21:37:53Z gawrilow $"

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

#define RePeAt(...) __VA_ARGS__

#define DeclFIELD_(type,name,type_fix) \
name##_sUpEr_;					\
struct name##_StRuCt_ : name##_sUpEr_ { 	\
   typedef name##_sUpEr_ super;			\
   typedef type_fix##type field_type;					\
   field_type name;							\
   field_type& get() { return name; }					\
   const field_type& get() const { return name; }			\
   static const char* field_name() { return #name; }			\
}; typedef name##_StRuCt_

#define DeclFIELD(type,name) DeclFIELD_(type,name,/**/)

#define DeclSTRUCT(name,_fields)	 \
struct name##_InTeRnAlS_ {		 \
   typedef pm::StructTop R##_fields fields; \
};					 \
struct name : name##_InTeRnAlS_::fields

#define GenSTRUCT(name,_fields) DeclSTRUCT(name,ePeAt(_fields)), GenericStruct<name>

namespace pm {

struct StructTop { typedef void super; };

template <typename Struct>
class GenericStruct : public Generic<Struct>
{
public:
   typedef GenericStruct generic_type;
   // FIXME: move the next line into spec_object_traits when merging with the 'shared' branch!
   typedef is_composite generic_tag;
};

template <typename Struct, typename _super=typename Struct::super>
struct StructHelper : StructHelper<typename Struct::super> {
   typedef typename pm::concat_list<typename StructHelper<typename Struct::super>::fields, typename Struct::field_type>::type fields;
};

template <typename Struct>
struct StructHelper<Struct,StructTop> {
   typedef Struct first_limb;
   typedef typename Struct::field_type fields;
};

template <typename Struct, typename Limb, typename _up=typename Struct::super>
struct StructFieldDown : StructFieldDown<typename Struct::super, Limb> { };

template <typename Struct, typename Limb>
struct StructFieldDown<Struct, Limb, Limb> {
   typedef Struct type;
};

template <typename Limb>
struct StructFieldDown<StructTop, Limb, void> {
   typedef StructTop type;
};

template <typename Struct, typename Visitor, typename Me> inline
void visitGenStruct_down(Visitor& v, Me& me, Struct*)
{
   typedef typename deref<Me>::type my_type;
   visitGenStruct_down(v << me.get(), static_cast<typename inherit_const<typename StructFieldDown<Struct,my_type>::type, Me>::type&>(me), (Struct*)0);
};

template <typename Struct, typename Visitor> inline
void visitGenStruct_down(Visitor&, StructTop&, Struct*) { }

template <typename Struct, typename Visitor> inline
void visitGenStruct_down(Visitor&, const StructTop&, Struct*) { }

template <typename Visitor, typename Struct> inline
void visitGenStruct(Visitor& v, Struct& s)
{
   typedef typename deref<Struct>::type my_type;
   visitGenStruct_down(v, static_cast<typename inherit_const<typename StructHelper<my_type>::first_limb, Struct>::type&>(s), (my_type*)0);
}

template <typename Operation, typename Struct> inline
void applyToGenStruct(Struct& s1, Struct& s2, const Operation& op)
{
   typedef typename inherit_const<typename Struct::super, Struct>::type super;
   typedef typename inherit_const<typename Struct::field_type, Struct>::type field;
   typedef binary_op_builder<Operation, field*, field*> opb;
   applyToGenStruct(static_cast<super&>(s1), static_cast<super&>(s2), op);
   opb::create(op)(s1.get(),s2.get());
}

template <typename Operation> inline
void applyToGenStruct(const Operation&, const StructTop&, const StructTop&) { }

template <typename Struct>
struct spec_object_traits< GenericStruct<Struct> >
   : spec_or_model_traits<Struct,is_composite> {
   typedef typename StructHelper<Struct>::fields elements;

   template <typename Me, typename Visitor>
   static void visit_elements(Me& x, Visitor& v)
   {
      visitGenStruct(v,x);
   }
};

} // end namespace pm

namespace polymake {
   using pm::GenericStruct;
}

namespace std {
   template <typename Struct> inline
   void swap(pm::GenericStruct<Struct>& s1, pm::GenericStruct<Struct>& s2)
   {
      pm::applyToGenStruct(s1, s2, polymake::operations::swap());
   }
}

#if 0
namespace std_ext {
   template <typename T> struct hash;

   template <typename E>
   struct hash< pm::Vector<E> > {
      template <typename _Vector>
      size_t operator() (const pm::GenericVector<_Vector,E>& v) const
      {
	 hash<E> element_hasher;
	 size_t a=1;
	 for (typename pm::ensure_features<_Vector, pm::cons<pm::end_sensitive, pm::sparse_compatible> >::const_iterator e=ensure(v.top(), (pm::cons<pm::end_sensitive, pm::sparse_compatible>*)0).begin(); !e.at_end(); ++e)
	    a=a*element_hasher(*e)+e.index();
	 return a;
      }
   };

   template <typename E, typename Predicate>
   struct hash< pm::SparseVector<E, Predicate> > : hash< pm::Vector<E> > { };
   template <typename E, int _size>
   struct hash< pm::FixedVector<E,_size> > : hash< pm::Vector<E> > { };
}
#endif

#endif // _POLYMAKE_GENERIC_STRUCT_H

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