// 
// Top-level API for serializing and deserializing arbitrary classes
//

#ifndef GENERS_GENERICIO_HH_
#define GENERS_GENERICIO_HH_

#include "geners/binaryIO.hh"
#include "geners/IOPointeeType.hh"
#include "geners/static_check.h"
#include "geners/StrippedType.hh"
#include "geners/ArrayAdaptor.hh"
#include "geners/ClearIfPointer.hh"

namespace gs {
    /**
    // Generic top-level function which can be used to write out
    // almost anything. Intended mainly for use inside "write"
    // methods of user-developed classes and templates. Returns
    // "true" if the argument item is successfully written out.
    */
    template <class Stream, class Item>
    inline bool write_item(Stream& os, const Item& item,
                           const bool writeClassId=true)
    {
        char* ps = 0;
        return process_const_item<GenericWriter>(item, os, ps, writeClassId);
    }

    /**
    // A function for overwriting existing objects (which usually live
    // on the stack). This function actually skips couple levels of
    // indirection which would be generated by a call to "process_item".
    */
    template <class Stream, class Item>
    inline void restore_item(Stream& is, Item* item,
                             const bool readClassId=true)
    {
        typedef std::vector<ClassId> State;
        assert(item);
        State state;
        const bool status = GenericReader<Stream, State, Item*,
            Int2Type<IOTraits<int>::ISPOINTER> >::process(
                item, is, &state, readClassId);
        if (is.fail())
            throw IOReadFailure("In gs::restore_item: input stream failure");
        if (!status)
            throw IOInvalidData("In gs::restore_item: invalid input stream data");
    }

    /**
    // Function for returning objects on the heap. This function
    // requires explicit specification of its first template
    // parameter, the type of the item to read. This function
    // either succeeds or throws an exception which inherits
    // from std::exception.
    */
    template <class Item, class Stream>
    inline CPP11_auto_ptr<Item> read_item(Stream& is, const bool readClassId=true)
    {
        typedef std::vector<ClassId> State;
        Item* item = 0;
        State state;
        const bool status = GenericReader<Stream, State, Item*,
            Int2Type<IOTraits<int>::ISNULLPOINTER> >::process(
                item, is, &state, readClassId);
        CPP11_auto_ptr<Item> ptr(item);
        if (is.fail())
            throw IOReadFailure("In gs::read_item: input stream failure");
        if (!status || item == 0)
            throw IOInvalidData("In gs::read_item: invalid input stream data");
        return ptr;
    }

    /**
    // Generic top-level function for writing arrays. Note that
    // the length of the array is not written out and that the
    // length must be known in advance in the scope from which
    // the companion function, "read_array", is called. "true"
    // is returned upon success, "false" on failure.
    */
    template <class Stream, class Item>
    inline bool write_array(Stream& os, Item* items, const std::size_t length)
    {
        char* ps = 0;
        return process_const_item<GenericWriter>(
            ArrayAdaptor<Item>(items, length), os, ps, false);
    }

    /**
    // Function for deserializing arrays. The length of the array
    // must be known in the scope from which this function is invoked.
    */
    template <class Stream, class Item>
    inline void read_array(Stream& is, Item* items, const std::size_t length)
    {
        typedef std::vector<ClassId> State;
        State state;
        ArrayAdaptor<Item> adap(items, length);
        const bool st = process_item<GenericReader>(adap, is, &state, false);
        if (is.fail())
            throw IOReadFailure("In gs::read_array: input stream failure");
        if (!st)
            throw IOInvalidData("In gs::read_array: invalid input stream data");
    }
}

#include "geners/GenericIO.icc"

#endif // GENERS_GENERICIO_HH_
