#include <ace/OS_main.h>
#include <ace/Refcounted_Auto_Ptr.h>
#include <ace/Null_Mutex.h>

#include <algorithm>
#include <iostream>
#include <set>

struct Msg1 {
   int sending_time;
   int seq_num;
};

int getTime( const Msg1 & m ) { return m.sending_time; }

int compareType( const Msg1 & a, const Msg1 & b ) {
   return a.seq_num - b.seq_num;
}

void apiCall( const Msg1 & m ) {
   std::cout << "api on Msg1: "
         << m.sending_time << ", " << m.seq_num << std::endl;
}

struct Msg2 {
   int update_time;
   int external_id;
};

int getTime( const Msg2 & m ) { return m.update_time; }

int compareType( const Msg2 & a, const Msg2 & b ) {
   return a.external_id - b.external_id;
}

void apiCall( const Msg2 & m ) {
   std::cout << "api on Msg2: "
         << m.update_time << ", " << m.external_id << std::endl;
}

struct Msg3 {
   int orig_time;
   int counter;
};

int getTime( const Msg3 & m ) { return m.orig_time; }

int compareType( const Msg3 & a, const Msg3 & b ) {
   return a.counter - b.counter;
}

void apiCall( const Msg3 & m ) {
   std::cout << "api on Msg3: "
         << m.orig_time << ", " << m.counter << std::endl;
}

template< class T > struct TypeTag {};

template<> struct TypeTag<Msg1> { static const int value = 0; };
template<> struct TypeTag<Msg2> { static const int value = TypeTag<Msg1>::value + 1; };
template<> struct TypeTag<Msg3> { static const int value = TypeTag<Msg2>::value + 1; };

class AbstractMessageHolder {
   public :
      virtual ~AbstractMessageHolder() {}

      virtual void callApi() = 0;
      virtual void print() const = 0;
      virtual int getTime() const = 0;
      virtual int compareWith( const AbstractMessageHolder & other ) const = 0;
      virtual int typeTag() const = 0;
      virtual const void * rawPointer() const = 0;
};

typedef ACE_Refcounted_Auto_Ptr< AbstractMessageHolder, ACE_Null_Mutex >
      AbstractMessageHolderAutoPtr;

template< class T >
class MessageHolder : public AbstractMessageHolder {
   public :
      MessageHolder( const T & value ) : msg( value ) {}

      virtual void callApi() { apiCall( msg ); }
      virtual void print() const { apiCall( const_cast< T& >( msg ) ); }
      virtual int getTime() const { return ::getTime( msg ); }
      virtual int compareWith( const AbstractMessageHolder & other ) const {
         int r = getTime() - other.getTime();
         if( !r ) {
            r = typeTag() - other.typeTag();
            if( !r ) {
               r = compareType(
                     msg,
                     *(reinterpret_cast< const T*>( other.rawPointer() )) );
            }
         }
         return r;
      }
      virtual int typeTag() const { return TypeTag<T>::value; }
      virtual const void * rawPointer() const { return &msg; }

   private :
      T msg;
};

template< class T >
AbstractMessageHolderAutoPtr makeMessageHolder( const T & o ) {
   return AbstractMessageHolderAutoPtr( new MessageHolder< T >( o ) );
}

struct MessageHolderComparator {
   bool operator()(
      const AbstractMessageHolderAutoPtr & a,
      const AbstractMessageHolderAutoPtr & b ) {
      return a->compareWith( *b ) < 0;
   }
};

typedef std::multiset<
            AbstractMessageHolderAutoPtr,
            MessageHolderComparator >
      MessageContainer;

void ApiCaller(
   const AbstractMessageHolderAutoPtr & o ) {
   o->callApi();
}

void MsgPrinter(
   const AbstractMessageHolderAutoPtr & o ) {
   o->print();
}

void fillContainer( MessageContainer & to ) {
   Msg1 a1 = { 4, 3 };
   Msg1 a2 = { 4, 2 };
   Msg1 a3 = { 2, 4 };

   Msg2 b1 = { 3, 0 };
   Msg2 b2 = { 3, 4 };
   Msg2 b3 = { 3, 1 };
   Msg2 b4 = { 2, 0 };

   Msg3 c1 = { 3, 0 };
   Msg3 c2 = { 1, 8 };
   Msg3 c3 = { 1, 9 };

   to.insert( makeMessageHolder( a1 ) );
   to.insert( makeMessageHolder( a2 ) );
   to.insert( makeMessageHolder( a3 ) );

   to.insert( makeMessageHolder( b1 ) );
   to.insert( makeMessageHolder( b2 ) );
   to.insert( makeMessageHolder( b3 ) );
   to.insert( makeMessageHolder( b4 ) );

   to.insert( makeMessageHolder( c1 ) );
   to.insert( makeMessageHolder( c2 ) );
   to.insert( makeMessageHolder( c3 ) );
}

int main( int argc, char ** argv ) {
   MessageContainer msgs;
   fillContainer( msgs );

   std::cout << "calling API..." << std::endl;
   std::for_each( msgs.begin(), msgs.end(), ApiCaller );

   std::cout << "printing messages..." << std::endl;
   std::for_each( msgs.begin(), msgs.end(), MsgPrinter );

   return 0;
}