#ifndef STLPLUS_PERSISTENT_CONTEXTS
#define STLPLUS_PERSISTENT_CONTEXTS
////////////////////////////////////////////////////////////////////////////////

//   Author:    Andy Rushton
//   Copyright: (c) Southampton University 1999-2004
//              (c) Andy Rushton           2004 onwards
//   License:   BSD License, see ../docs/license.html

//   Core context classes used to control the persistent dump/restore operations

////////////////////////////////////////////////////////////////////////////////

#include "persistence_fixes.hpp"
#include "persistent.hpp"
#include <iostream>
#include <map>
#include <typeinfo>

////////////////////////////////////////////////////////////////////////////////

namespace stlplus
{

  ////////////////////////////////////////////////////////////////////////////////
  // Internals

  class dump_context_body;
  class restore_context_body;

  ////////////////////////////////////////////////////////////////////////////////
  // The format version number currently supported
  ////////////////////////////////////////////////////////////////////////////////

  extern unsigned char PersistentVersion;

  ////////////////////////////////////////////////////////////////////////////////
  // dump_context controls the formatting of a persistent dump
  ////////////////////////////////////////////////////////////////////////////////

  class dump_context
  {
    friend class persistent;
  public:
    //////////////////////////////////////////////////////////////////////////////

    // device must be in binary mode
    // exceptions: persistent_dump_failed
    dump_context(std::ostream& device, unsigned char version = PersistentVersion) ;
    ~dump_context(void);

    // low level output used to dump a byte
    // exceptions: persistent_dump_failed
    void put(unsigned char data) ;

    // access the device, for example to check the error status
    const std::ostream& device(void) const;

    // recover the version number of the dumped output
    unsigned char version(void) const;

    // test whether the current platform uses little-endian or big-endian addressing of bytes
    // this is used in dump/restore of integers and is exported so that other routines can use it
    bool little_endian(void) const;

    // Assist functions for Pointers
    // the return pair value is a flag saying whether this is a new pointer and the magic key to dump to file
    std::pair<bool,unsigned> pointer_map(const void* const pointer);
    // the return pair value is a flag saying whether this is a new object and the magic key to dump to file
    std::pair<bool,unsigned> object_map(const void* const pointer);

    // Assist functions for Polymorphous classes (i.e. subclasses) using callback approach
    typedef void (*dump_callback)(dump_context&,const void*);
    unsigned register_callback(const std::type_info& info, dump_callback);
    bool is_callback(const std::type_info& info) const;
    typedef std::pair<unsigned,dump_callback> callback_data;
    // exceptions: persistent_illegal_type
    callback_data lookup_callback(const std::type_info&) const ;

    // Assist functions for Polymorphous classes (i.e. subclasses) using interface approach
    unsigned register_interface(const std::type_info& info);
    bool is_interface(const std::type_info& info) const;
    // exceptions: persistent_illegal_type
    unsigned lookup_interface(const std::type_info&) const ;

    // Register all Polymorphous classes using either approach by calling an installer callback
    typedef void (*installer)(dump_context&);
    void register_all(installer);

  private:
    friend class dump_context_body;
    dump_context_body* m_body;

    // disallow copying by making assignment and copy constructor private
    dump_context(const dump_context&);
    dump_context& operator=(const dump_context&);
  };

  ////////////////////////////////////////////////////////////////////////////////
  // restore_context controls the reading of the persistent data during a restore

  class restore_context
  {
    friend class persistent;
  public:
    //////////////////////////////////////////////////////////////////////////////

    // device must be in binary mode
    // exceptions: persistent_restore_failed
    restore_context(std::istream& device) ;
    ~restore_context(void);

    // low level input used to restore a byte
    // exceptions: persistent_restore_failed
    int get(void) ;

    // access the device, for example to check the error status
    const std::istream& device(void) const;

    // access the version number of the input being restored
    unsigned char version(void) const;

    // test whether the current platform uses little-endian or big-endian addressing of bytes
    // this is used in dump/restore of integers
    bool little_endian(void) const;

    // Assist functions for Pointers
    std::pair<bool,void*> pointer_map(unsigned magic);
    void pointer_add(unsigned magic, void* new_pointer);
    std::pair<bool,void*> object_map(unsigned magic);
    void object_add(unsigned magic, void* new_pointer);

    // Assist functions for Polymorphous classes using the callback approach
    typedef void* (*create_callback)(void);
    typedef void (*restore_callback)(restore_context&,void*);
    unsigned register_callback(create_callback,restore_callback);
    bool is_callback(unsigned) const;
    typedef std::pair<create_callback, restore_callback> callback_data;
    // exceptions: persistent_illegal_type
    callback_data lookup_callback(unsigned) const ;

    // Assist functions for Polymorphous classes using the interface approach
    unsigned register_interface(persistent*);
    bool is_interface(unsigned) const;
    // exceptions: persistent_illegal_type
    persistent* lookup_interface(unsigned) const ;

    // Register all Polymorphous classes using either approach by calling an installer callback
    typedef void (*installer)(restore_context&);
    void register_all(installer);

  private:
    friend class restore_context_body;
    restore_context_body* m_body;

    typedef std::pair<unsigned,persistent*> interface_data;

    // disallow copying by making assignment and copy constructor private
    restore_context(const restore_context&);
    restore_context& operator=(const restore_context&);
  };

  ////////////////////////////////////////////////////////////////////////////////

} // end namespace stlplus

  ////////////////////////////////////////////////////////////////////////////////
#endif
