// This file functions to pack/unpack data structures
#ifndef included_PackData_hpp
#define included_PackData_hpp

#include "IO/PackData.h"

#include <map>
#include <set>
#include <string.h>
#include <vector>


/********************************************************
 * Default instantiations for std::vector                *
 ********************************************************/
template<class TYPE>
size_t packsize( const std::vector<TYPE> &rhs )
{
    size_t bytes = sizeof( size_t );
    for ( size_t i = 0; i < rhs.size(); i++ )
        bytes += packsize( rhs[i] );
    return bytes;
}
template<class TYPE>
void pack( const std::vector<TYPE> &rhs, char *buffer )
{
    size_t size = rhs.size();
    memcpy( buffer, &size, sizeof( size_t ) );
    size_t pos = sizeof( size_t );
    for ( size_t i = 0; i < rhs.size(); i++ ) {
        pack( rhs[i], &buffer[pos] );
        pos += packsize( rhs[i] );
    }
}
template<class TYPE>
void unpack( std::vector<TYPE> &data, const char *buffer )
{
    size_t size;
    memcpy( &size, buffer, sizeof( size_t ) );
    data.clear();
    data.resize( size );
    size_t pos = sizeof( size_t );
    for ( size_t i = 0; i < data.size(); i++ ) {
        unpack( data[i], &buffer[pos] );
        pos += packsize( data[i] );
    }
}


/********************************************************
 * Default instantiations for std::pair                  *
 ********************************************************/
template<class TYPE1, class TYPE2>
size_t packsize( const std::pair<TYPE1, TYPE2> &rhs )
{
    return packsize( rhs.first ) + packsize( rhs.second );
}
template<class TYPE1, class TYPE2>
void pack( const std::pair<TYPE1, TYPE2> &rhs, char *buffer )
{
    pack( rhs.first, buffer );
    pack( rhs.second, &buffer[packsize( rhs.first )] );
}
template<class TYPE1, class TYPE2>
void unpack( std::pair<TYPE1, TYPE2> &data, const char *buffer )
{
    unpack( data.first, buffer );
    unpack( data.second, &buffer[packsize( data.first )] );
}


/********************************************************
 * Default instantiations for std::map                   *
 ********************************************************/
template<class TYPE1, class TYPE2>
size_t packsize( const std::map<TYPE1, TYPE2> &rhs )
{
    size_t bytes = sizeof( size_t );
    typename std::map<TYPE1, TYPE2>::const_iterator it;
    for ( it = rhs.begin(); it != rhs.end(); ++it ) {
        bytes += packsize( it->first );
        bytes += packsize( it->second );
    }
    return bytes;
}
template<class TYPE1, class TYPE2>
void pack( const std::map<TYPE1, TYPE2> &rhs, char *buffer )
{
    size_t N = rhs.size();
    pack( N, buffer );
    size_t pos = sizeof( size_t );
    typename std::map<TYPE1, TYPE2>::const_iterator it;
    for ( it = rhs.begin(); it != rhs.end(); ++it ) {
        pack( it->first, &buffer[pos] );
        pos += packsize( it->first );
        pack( it->second, &buffer[pos] );
        pos += packsize( it->second );
    }
}
template<class TYPE1, class TYPE2>
void unpack( std::map<TYPE1, TYPE2> &data, const char *buffer )
{
    size_t N = 0;
    unpack( N, buffer );
    size_t pos = sizeof( size_t );
    data.clear();
    for ( size_t i = 0; i < N; i++ ) {
        std::pair<TYPE1, TYPE2> tmp;
        unpack( tmp.first, &buffer[pos] );
        pos += packsize( tmp.first );
        unpack( tmp.second, &buffer[pos] );
        pos += packsize( tmp.second );
        data.insert( tmp );
    }
}


/********************************************************
 * Default instantiations for std::set                   *
 ********************************************************/
template<class TYPE>
size_t packsize( const std::set<TYPE> &rhs )
{
    size_t bytes = sizeof( size_t );
    typename std::set<TYPE>::const_iterator it;
    for ( it = rhs.begin(); it != rhs.end(); ++it ) {
        bytes += packsize( *it );
    }
    return bytes;
}
template<class TYPE>
void pack( const std::set<TYPE> &rhs, char *buffer )
{
    size_t N = rhs.size();
    pack( N, buffer );
    size_t pos = sizeof( size_t );
    typename std::set<TYPE>::const_iterator it;
    for ( it = rhs.begin(); it != rhs.end(); ++it ) {
        pack( *it );
        pos += packsize( *it );
    }
}
template<class TYPE>
void unpack( std::set<TYPE> &data, const char *buffer )
{
    size_t N = 0;
    unpack( N, buffer );
    size_t pos = sizeof( size_t );
    data.clear();
    for ( size_t i = 0; i < N; i++ ) {
        TYPE tmp;
        unpack( tmp, &buffer[pos] );
        pos += packsize( tmp );
        data.insert( tmp );
    }
}


#endif
