/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 *
 * The iWear Framework 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 as in version 2 of the License.

 * 
 * The iWear Framework 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.
 * 
 * You should have received a copy of the GNU General Public License along with
 * The iWear Framework; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __IWREMOTE_RPCSTREAM_H
#define __IWREMOTE_RPCSTREAM_H

#ifndef __IWEAR_UID_H
#include <iwear/uid.h>
#endif

#include <iwear/debug.h>

#include <iwremote/protocol.h>

#include <string>
#include <utility>
using std::string;

void sorry_this_return_type_is_not_defined();
namespace iwear
{
    namespace net
    {
/**
 * This class is base class for all classes that want to implement some RPC
 * data streaming structures with implicit typing.
 *
 * The new get/set functions reflect the size of the data to make it clearer,
 * since the "items" in the streams need to have fixed data since they might
 * need to be reassembled on a system where the size of a basic type like "int"
 * or "long" is another.
 */
class RPCStream
{
private:
protected:
public:
PH1  virtual ~RPCStream() {}
    /**
     * Parses the next stream object and returns it as bool. Throws exception
     * when failing.
     */
PH2  virtual bool get_bool( const char* = 0 ) = 0;

PH2  virtual int8_t get_int8_t( const char* = 0 ) = 0;
PH2  virtual uint8_t get_uint8_t( const char* = 0 ) = 0;

PH2  virtual int16_t get_int16_t( const char* = 0 ) = 0;
PH2  virtual uint16_t get_uint16_t( const char* = 0 ) = 0;

PH2  virtual int32_t get_int32_t( const char* = 0 ) = 0;
PH2  virtual uint32_t get_uint32_t( const char* = 0 ) = 0;

PH2  virtual int64_t get_int64_t( const char* = 0 ) = 0;
PH2  virtual uint64_t get_uint64_t( const char* = 0 ) = 0;

PH2  virtual long double get_long_double( const char* = 0 ) = 0;
PH2  virtual double get_double( const char* = 0 ) = 0;
PH2  virtual float get_float( const char* = 0 ) = 0;

PH2  virtual string get_string( const char* = 0 ) = 0;
PH2  virtual uid get_uid( const char* = 0 ) = 0;

PH1  virtual void put_bool( bool, const char* = 0 ) = 0;

PH1  virtual void put_int8_t( int8_t, const char* = 0 ) = 0;
PH1  virtual void put_uint8_t( uint8_t, const char* = 0 ) = 0;

PH1  virtual void put_int16_t( int16_t, const char* = 0 ) = 0;
PH1  virtual void put_uint16_t( uint16_t, const char* = 0 ) = 0;

PH1  virtual void put_int32_t( int32_t, const char* = 0 ) = 0;
PH1  virtual void put_uint32_t( uint32_t, const char* = 0 ) = 0;

PH1  virtual void put_int64_t( int64_t, const char* = 0 ) = 0;
PH1  virtual void put_uint64_t( uint64_t, const char* = 0 ) = 0;

PH1  virtual void put_long_double( long double, const char* = 0 ) = 0;
PH1  virtual void put_double( double, const char* = 0 ) = 0;
PH1  virtual void put_float( float, const char* = 0 ) = 0;

PH1  virtual void put_string( const string&, const char* = 0 ) = 0;
PH1  virtual void put_uid( const uid&, const char* = 0 ) = 0;


PH1  virtual std::pair<char *, uint32_t> get_buffer( void ) = 0;

PH1  virtual std::pair<const char *, uint32_t> get_buffer( void ) const= 0;

    /**
     * This sets the buffer, means the stream may use the buffer.
     */
PH1  virtual void set_buffer( char*, uint32_t ) = 0;

    /**
     * This replaces the internal buffer. The buffer must be allocated with new
     * and will be deleted by the stream.
     */
PH1  virtual void replace_buffer( char*, uint32_t ) = 0;

    /**
     * This replaces the current buffer with the one out of the providing
     * stream, and the providing streams buffer will be emptied.
     */
PH1  virtual void replace( RPCStream& ) = 0;

    /**
     * Returns the actual amount of data readable. Its in "data read units"
     * means in the amount of smallest available data units. If the stream
     * contains explicit type or size restrictions, this will be included too.
     * So different streams might return different values for the same data
     * (XML vs. binary)
     */
PH1  virtual uint32_t data( void ) = 0;

PH1  virtual size_t max_size( void ) = 0;
PH1  virtual void resize( size_t ) = 0;

PH1  template<class T> T get( const char* = 0);

    /**
     * This resets the streams pointer to the beginning, it does not empty it !
     * (This is for moving around in the packet, not for clearing it)
     */
PH1  virtual void rewind( void ) = 0;

    /**
     * Sets the pointer to right after the last portion written
     */
PH2  virtual void to_end( void ) = 0;

    /**
     * This function is beeing called on a freshly created rpcstream when it is
     * to be used for a outgoing call
     */
PH1  virtual void start_call( cid_t ) { }

    /**
     * Ends a call started with start_call
     */
PH1  virtual void end_call( void ) { }

    /**
     * This function is to be called whenever a container type is to be laid onto the stream.
     * @param the number of elements in the container
     * @param the type of the container
     * @note the type is for xml-like implementations that want to embed this
     * into the information. The first parameter is per default put onto the
     * stream as if it was a uint32_t.
     * @note For the sake of efficiency, the name is passed as a const char*
     * pointer, so string literals dont lead to temporary std::string
     * instantiation.
     */
PH2  virtual void start_container( uint32_t sz, const char* ) { put_uint32_t(sz); }

PH2  virtual uint32_t get_container( const char* ) { return get_uint32_t(); }

    /**
     * Templatized version of put
     */
PH2  template<class T> void put( const T&, const char* = 0 );

    /**
     * When the put data is of a user defined class type, its beeing put with
     * this.
     */
PH2  virtual void start_class( const char* ) { }

PH2  virtual void end_class( void ) { }
     /**
      * This function is beeing called just before a member is beeing put. The
      * next statement(s) will then deterine hat member. It can be a usual put_
      * function or a user defined type which is then also started with
      * start_class
      * @note this function is deprecated and not used at the proper places
      * anymore, in the future there will be a proper replacement.
      */
PH2  virtual void put_member( const char* ) { }

    /**
     * This function sets the flags in the parameter. They will be bitwise ORed
     * with the existing flags.
     */
PH2 virtual void set_flags( uint8_t ) = 0;
    virtual uint8_t get_flags( void ) = 0;
    /**
     * This should be called right before the packet is sent. It should
     * basically set the size into the package header structure. For others, it
     * might do something else too.
     */
PH2  virtual void finalize( void ) = 0;

    /**
     * Sets the packet sequence number. 
     * @warning This is a low level function without checks. This means that if
     * there has not been reserved some space for the sequence number at the
     * right place, otherwise some data will be overwritten
     */

     virtual void set_sequence( uint32_t ) = 0;

     virtual uint32_t get_sequence( void ) = 0;

     /**
      * @warning @see the sequence number too.
      */
     virtual void set_deferr_id( uint32_t ) = 0;

     virtual uint32_t get_deferr_id( void ) = 0;

     /**
      * Jumps to directly after the standard header
      * @param true if this packet is an outgoing one, false if otherwise
      * (might not matter for all packet types)
      */
    virtual void skip_header( ) = 0;

    /**
     * extracts the type information out of the header
     */
    virtual uint8_t get_type( void ) = 0;

    virtual uint16_t get_channel( void ) = 0;

    /**
     * Debug function to output the contents of the stream.
     */
    virtual void dump( void ) const = 0;
};

#define TEMPL_PUT(T) \
template<> \
inline void RPCStream::put( const T& t, const char*u ) \
{ \
    put_##T(t,u); \
}; \

#define TEMPL_GET(T) \
template<> \
inline T RPCStream::get( const char* u ) \
{ \
    return get_##T(u); \
}; \

typedef long double long_double;
TEMPL_GET(bool)
TEMPL_GET(int8_t)
TEMPL_GET(uint8_t)
TEMPL_GET(int16_t)
TEMPL_GET(uint16_t)
TEMPL_GET(int32_t)
TEMPL_GET(uint32_t)
TEMPL_GET(int64_t)
TEMPL_GET(uint64_t)
TEMPL_GET(long_double)
TEMPL_GET(double)
TEMPL_GET(float)
TEMPL_GET(string)
TEMPL_GET(uid)

TEMPL_PUT(bool)
TEMPL_PUT(int8_t)
TEMPL_PUT(uint8_t)
TEMPL_PUT(int16_t)
TEMPL_PUT(uint16_t)
TEMPL_PUT(int32_t)
TEMPL_PUT(uint32_t)
TEMPL_PUT(int64_t)
TEMPL_PUT(uint64_t)
TEMPL_PUT(long_double)
TEMPL_PUT(double)
TEMPL_PUT(float)
TEMPL_PUT(string)
TEMPL_PUT(uid)

#undef TEMPL_PUT
#undef TEMPL_GET

template<class T>
inline void RPCStream::put( const T&, const char* )
{
    ::sorry_this_return_type_is_not_defined();
}

template<class T>
inline T RPCStream::get( const char* )
{
    return ::sorry_this_return_type_is_not_defined();
}

}
}
#endif

