////////////////////////////////////////////////////////////////////////
// A test & demo app for s11n[lite].
// Author: stephan@s11n.net
// License: Do As You Damned Well Please
////////////////////////////////////////////////////////////////////////

#ifdef NDEBUG
#  undef NDEBUG // we always want assert()
#endif

#include <cassert>
#include <iostream>
#include <string>

#include <list>
#include <set>
#include <map>
#include <sstream>
#include <vector>
#include <algorithm>


////////////////////////////////////////////////////////////////////////
#include <s11n.net/s11n/s11nlite.hpp> // s11n & s11nlite frameworks
////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////
// our demo Serializable
#include "simplestruct.h"
// it's s11n proxy implementation:
#include "simplestruct_s11n.h"
////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////
// misc util stuff
#include <s11n.net/s11n/s11n_debuggering_macros.hpp> // CERR
#include <s11n.net/tostring/to_string.hpp> // to/from_string()
#include <s11n.net/acme/argv_parser.hpp> // argv_parser class
#include <s11n.net/acme/algo.hpp> // free_list_entries<>()
#include <s11n.net/acme/pointer_cleaner.hpp> // poor-man's garbage collector.
////////////////////////////////////////////////////////////////////////



void test_simplestruct();
void test_list();
void test_set();
void test_multiset();
void test_map();
void test_multimap();
void test_mega();

int
main( int argc, char **argv )
{
        typedef acme::argv_parser ARGV;
        ARGV & args = ARGV::args( argc, argv );

        if( args.is_set( "d" ) ) // either -d or --d
        {
                cl::class_loader_debug_level(1);
        }
        else
        {
                CERR << "tip: use -d to enable classloader debug output.\n";
        }

        // set the Serializer we want using -s=classname
        s11nlite::serializer_class( args.get( "s", "parens" ) );

        typedef void (*testfunc)();
        typedef std::map<std::string,testfunc> FuncMap;
        FuncMap funcs;
        funcs["struct"] = test_simplestruct;
        funcs["list"] = test_list;
        funcs["set"] = test_set;
        funcs["multiset"] = test_multiset;
        funcs["map"] = test_map;
        funcs["multimap"] = test_multimap;
        funcs["mega"] = test_mega;


        std::string avail_args;
        FuncMap::iterator mit = funcs.begin(), met = funcs.end();
        for( ; met != mit ; ++mit )
        {
                avail_args += "-" + (*mit).first + " ";
        }


        bool done = false;
        mit = funcs.begin();
        for( ; met != mit ; ++mit )
        {
                if( args.is_set( (*mit).first ) )
                {
                        CERR << "Running test ["<<(*mit).first<<"]...\n";
                        (*mit).second();
                        done = true;
                }
        }
        if( done ) exit( 0 );


        CERR << "To see one of the demos, run\n\t"
             << args.get( "$0","[this_app]" )
             << "\nfollowed by any of the following arguments:\n"
             << "\t" << avail_args << "\n"
             << "Use [-d] to turn on classloader debug output.\n"
             << "Use [-s X] to change the Serializer (output format), where X is one of:\n\t"
             << "compact funtxt funxml parens simplexml\n";
        
	return 0;
};


/**
   Demonstration of s11n'ing a simplestruct.
*/
void test_simplestruct()
{
        ////////////////////// set up a simplestruct to play with:
        simplestruct s;
        s.i = 8;
        s.d = 42.42;
        s.s = "hello, s11n!";
        simplestruct * ch = new simplestruct;
        s.child = ch;
        ch->i = 7;
        ch->d = 7.7;
        ch->s = "Finally! Easy-to-use serialization in C++!";


        ////////////////////// use s11n on it:

        using namespace s11nlite; // import the functions we'll use

        CERR << "simplestruct looks like:\n";
        assert( save( s, std::cout ) );

        ////////////////////// buffer it for later reading
        // (simulate a file)
        std::ostringstream ostr;
        assert( save( s, ostr ) );



        ////////////////////// restore it's state, method #1:
        CERR << "Brute-force deserialized via an intermediary node:\n";
        simplestruct sr;
        s11nlite::node_type * n = 0;
        std::istringstream istr( ostr.str() );
        assert( n = load_node( istr ) );
        assert( deserialize( *n, sr ) );
        delete( n ); // we're done with him.
        assert( save( sr, std::cout ) );


        ////////////////////// restore state, method #2:
        CERR << "Loading new simplestruct from stream...\n";
        std::istringstream fakefile( ostr.str() );
        simplestruct * sptr = 0;
        assert( sptr = load_serializable<simplestruct>( fakefile ) );
        CERR << "It looks like:\n";
        assert( save( sptr, std::cout ) );
        delete( sptr );

}

////////////////////////////////////////////////////////////////////////
// Some containers of simplestructs...
typedef std::list<simplestruct *> StructList;
typedef std::vector<simplestruct> StructVector;
typedef std::map<std::string,simplestruct*> MapStrSimplestruct;
////////////////////////////////////////////////////////////////////////


/**
   Demonstration of s11n'ing a list/vector of simplestructs.
*/
void test_list()
{
        using namespace s11nlite;

        //////////////////// list demo...
        StructList list;
        for( int i = 1; i < 4; i++ )
        {
                // set up some dummy struct objects for our list...
                simplestruct * s = new simplestruct;
                s->i = i;
                s->d = 2.8 * i;
                s->s = tostring::to_string( 100 - i ) + " bottles of beer on the wall.";
                list.push_back( s );
        }

        /////////////////// serialize the list:
        CERR << "proxied list : " << ::classname<StructList>() <<"\n";
        // i only use a node_type here to avoid a temp file/stream
        s11nlite::node_type node;
        assert( serialize( node, list ) );
        assert( save( node, std::cout ) );
        acme::free_list_entries( list ); // clean up. we're done with this one.

        //////////////////// try to deserialize our list...
        StructList * relist = 0;
        assert( relist = deserialize<StructList>( node ) );
        node.clear(); // clean up child pointers. This will happen when he's
		      // destroyed, anyway, but i'm being pedantic.

        CERR << "Deserialized list should be indentical:\n";
        assert( save( relist, std::cout ) );

        /////////////////// just for fun, let's "cast" our list to a vector:
        CERR << "Now let's \"cast\" the "<<::classname<StructList>()
             << " to a "<<::classname<StructVector>()<<":\n";
        StructVector vec;
        assert( s11nlite::s11n_cast( *relist, vec ) );
        assert( save( vec, std::cout ) );
        acme::free_list_entries( vec ); // clean up.
        acme::free_list_entries( *relist ); // clean up.

        delete( relist );
}

/**
   Demonstration of s11n'ing a std::set of simplestructs.
*/
void test_set()
{

        typedef std::set<std::string> StringSet;
        ////////////////////////////////////////////////////////////
        // Reminder: we can't use set<simplestruct> because there is
        // no meaningful, non-arbitrary way to sort simplestructs, a
        // property which makes them useless as keys in ordered
        // containers.
        ////////////////////////////////////////////////////////////
        StringSet set;
        for( int i = 1; i < 4; i++ )
        {
                set.insert( tostring::to_string(i) + "x neat, innit?" );
        }

        CERR << "Here's our set: " << ::classname<StringSet>()<<"\n";
        using namespace s11nlite;
        assert( save( set, std::cout ) );

        node_type n;
        assert( serialize( n, set ) );

        CERR << "Deserialized, method #1:\n";
        StringSet deset;
        assert( deserialize( n, deset ) );
        assert( save( deset, std::cout ) );

        CERR << "Deserialized, method #2:\n";
        StringSet * d2 = deserialize<StringSet>( n );
        assert( d2 );
        assert( save( d2, std::cout ) );
        delete d2;

}


/**
   Demonstration of s11n'ing a std::multiset of simplestruct.
*/
void test_multiset()
{

        typedef std::multiset<std::string> StringMultiset;
        ////////////////////////////////////////////////////////////
        // Reminder: we can't use multiset<simplestruct> because there
        // is no meaningful, non-arbitrary way to sort simplestructs,
        // a property which makes them useless as keys in ordered
        // containers.
        ////////////////////////////////////////////////////////////
        StringMultiset set;
        for( int i = 1; i < 4; i++ )
        {
                for( int d = 0; d < 3; d++ )
                {
                        set.insert( "entry #" + tostring::to_string(i) );
                }
        }

        CERR << "Here's our multiset: " << ::classname<StringMultiset>()<<"\n";
        using namespace s11nlite;
        assert( save( set, std::cout ) );

        node_type n;
        assert( serialize( n, set ) );

        CERR << "Deserialized, method #1:\n";
        StringMultiset deset;
        assert( deserialize( n, deset ) );
        assert( save( deset, std::cout ) );

        CERR << "Deserialized, method #2:\n";
        StringMultiset * d2 = deserialize<StringMultiset>( n );
        assert( d2 );
        assert( save( d2, std::cout ) );
        delete d2;
}


/**
   Demonstration of s11n'ing a std::map of simplestructs.
*/
void test_map()
{
        //////////////////// map demo...

        MapStrSimplestruct map;
        for( int i = 1; i < 4; i++ )
        {
                // set up some dummy struct objects for our map...
                simplestruct * s = new simplestruct;
                s->i = i;
                s->d = 2.8 * i;
                s->s = tostring::to_string( 0 + i ) + " bottle" + (1==i ? "" : "s") + " of beer on the wall." ;
                map[tostring::to_string(i) + "x neat, innit?"] = s;
        }

        CERR << "Here's our map: " << ::classname<MapStrSimplestruct>()<<"\n";
        using namespace s11nlite;
        assert( save( map, std::cout ) );

        node_type n;
        assert( serialize( n, map ) );
        // clean up the pointers in the pairs:
        acme::free_map_entries( map );

        CERR << "Deserialized:\n";

        MapStrSimplestruct demap;
        assert( deserialize( n, demap ) );
        assert( save( demap, std::cout ) );

        CERR << "Now let's clone() it:\n";
        MapStrSimplestruct * mapptr = 0;
        assert( mapptr = s11nlite::clone< MapStrSimplestruct >( demap ) );
        assert( save( *mapptr, std::cout ) );

        // clean up the pointers in the pairs:
        acme::free_map_entries( demap );
        acme::free_map_entries( *mapptr );
        delete( mapptr );
}

/**
   Demonstration of s11n'ing a std::multimap of simplestruct.
*/
void test_multimap()
{
        //////////////////// multimap demo...
        typedef std::multimap<std::string,simplestruct *> MultimapStrSimplestruct;
        MultimapStrSimplestruct multimap;
        for( int i = 1; i < 4; ++i )
        {
                for( int x = 0; x < 2; ++x )
                {
                        // set up some dummy struct objects for our multimap...
                        simplestruct * s = new simplestruct;
                        s->i = i;
                        s->d = 2.8 * i;
                        s->s = tostring::to_string( 0 + i ) + " bottle" + (1==i ? "" : "s") + " of beer on the wall." ;
                        multimap.insert( std::make_pair(
                                                        tostring::to_string(i) + "x neat, innit?",
                                                        s ) );
                }
        }

        CERR << "Here's our multimap: " << ::classname<MultimapStrSimplestruct>()<<"\n";
        using namespace s11nlite;
        assert( save( multimap, std::cout ) );

        node_type n;
        assert( serialize( n, multimap ) );
        // clean up the pointers in the pairs:
        acme::free_map_entries( multimap );

        CERR << "Deserialized:\n";

        MultimapStrSimplestruct demultimap;
        assert( deserialize( n, demultimap ) );
        assert( save( demultimap, std::cout ) );

        CERR << "Now let's clone() it:\n";
        MultimapStrSimplestruct * multimapptr = 0;
        assert( multimapptr = s11nlite::clone< MultimapStrSimplestruct >( demultimap ) );
        assert( save( *multimapptr, std::cout ) );

        // clean up the pointers in the pairs:
        acme::free_map_entries( demultimap );
        acme::free_map_entries( *multimapptr );
        delete( multimapptr );
}


/**
   Demonstration of s11n'ing a multiply-nested containers.
*/
void test_mega()
{


	//////////////////// A non-trivial map:
        typedef std::map<std::string,StructList *> MapT;
        MapT map;

	////////////////////////////////////////////////////////////
        // Maintenance detail: pointer_cleaner works similarly to a
        // multi-pointer std::auto_ptr, to provide simple
        // garbage-collection:
        typedef acme::pointer_cleaner<simplestruct> GCS;
        typedef acme::pointer_cleaner<StructList> GCL;
        GCS gcs;
        GCL gcl;


	//////////////////// populate the map...
        simplestruct * s;
        StructList * l;
        for( int il = 0; il < 3; ++il )
        {
                l = new StructList;
                for( int is = 0; is < 4; ++is )
                {
                        s = new simplestruct;
                        s->i = 1 + (is * il);
                        s->d = 1 + (4.0 * is * il);
			s->s = "string #" + tostring::to_string<int>( is );
                        gcs.add( s );
                        l->push_back( s );
                }
                gcl.add( l );
                map[tostring::to_string<int>( il )] = l;
        }

	//////////////////// import the functions we'll use:
        using namespace s11nlite;

	//////////////////// the rest is downright EASY...

	CERR << "Our map looks like this:\n";
        assert( save( map, std::cout ) );

        CERR << "Clone it:\n";
        MapT * demap = clone<MapT>( map );
        assert( demap );
        assert( save( *demap, std::cout ) );


        typedef std::map<std::string,StructVector> CastMapT;
        CERR << "Let's try \"casting\" it to a " << ::classname<CastMapT>()<<"\n";
        CastMapT vecmap;
        assert( s11n_cast( *demap, vecmap ) );
        delete( demap ); // pointer_cleaner cleans up demap's contents later
        assert( save( vecmap, std::cout ) );

	// note that the structs in CastMapT are not pointers, so we do not
	// need to clean them up as we did for MapT.
}

