#ifndef XSTORABLE_INCLUDED
#define XSTORABLE_INCLUDED

////////////////////////////////////////////
//
// Utility classes to support persistence
//
// of C++ objects
//
// 1998 (C) Aleksandras Gluchovas
//
// e-mail: alex@nomagiclt.com
//
// NOTE: only this header file is required
//
// some poetry:
//
// YOU CAN USE, REDISTRIBUTE AND MODIFY THIS 
// SOFTWARE WITHOUT ANY RESTRICTIONS,
// PROVIDED THAT THE ABOVE COPYRIGHT REMAINS
// UNMODIFIED. AUTHOR DOESN'T ASSUME ANY 
// RESPONSIBILITY FOR SOFTWARE OR HARDWARE
// DAMAGES CAUSED BY THIS SOFTWARE.
//
////////////////////////////////////////////

/*** Sample program ***/

/*
class Dummy
{
	double mValue0;
	double mValue1;
public:

	Dummy() 
		: mValue0( 5.0 ),
		  mValue1( 5.0 )
	 {}

	Dummy( double value )
		: mValue0( value ),
		  mValue1( 3 )
	{}

	void Serialize( XSStream& stm )
	{
		stm << storeVar( mValue0 )
			<< storeVar( mValue1 );
	}

	void DumpMe()
	{
		cout << " mValue0 = " << mValue0 
		     << " mValue1 = " << mValue1 << " ";
	}
};

void main()
{
	XSStream stm;
	Dummy    dummy( 14 );

#if TEST_STORING

	stm.SetLoading( 0, "storage.bin" ); 

	stm << storeObj( dummy );

#else // TEST_LOADING

	stm.SetLoading( 1, "storage.bin" ); 

	stm << storeObj( dummy );

 	dummy.DumpMe();
#endif

	cout << "Done." << endl;
}*/

/*** End of sample program ***/
#ifdef __GNUWIN32__
  #include <g++-3/iterator>
#endif

#include <stdio.h>
#include <wx/string.h>

class XSStream;

// base class for storable objects (and their wrappers if
// derivation is not acceptable)

class XStorable
{
public:
	virtual void Load(  XSStream& stm ) = 0;
	virtual void Store( XSStream& stm ) = 0;
};

// storage stream class

class XSStream
{
protected:
	FILE* mpOs;
	FILE* mpIs;

public:
	XSStream()
		: mpOs( (FILE*)-1 ), mpIs( (FILE*)-1 )
	{}

	void CloseHandles()
	{
		if ( mpOs != (FILE*)-1 ) fclose(mpOs);
		if ( mpIs != (FILE*)-1 ) fclose(mpIs);

		mpOs = (FILE*)-1;
		mpIs = (FILE*)-1;
	}

	~XSStream()
	{
		CloseHandles();
	}

	bool IsStoring()
	{
		return ( mpOs != 0 );
	}

	// method should be called to enbale functionality of this stream object

	bool SetLoading( bool isLoading, const char* fileName )
	{
		CloseHandles();

		if ( isLoading )
		{
			mpIs = fopen( fileName, "rb" );

			if ( mpIs == (FILE*)-1 ) return 0;
		}
		else
		{
			mpOs = fopen( fileName, "wb" );

			if ( mpOs == (FILE*)-1 ) return 0;
		}

		return 1;
	}

	void Write( void* buf, size_t len )
	{
		fwrite( buf, sizeof(char), len, mpOs );
	}

	void Read( void* buf, size_t len )
	{
		fread( buf, sizeof(char), len, mpIs );
	}

	XSStream& operator<< ( const XStorable& obj )
	{
		XStorable& xobj = const_cast<XStorable&>(obj);

		if ( IsStoring() ) xobj.Store( *this );
					  else xobj.Load ( *this );

		return *this;
	}
};

// wrapper object for basic types
// FOR NOW:: no byte swapping

template <class Type>
class XStorableType : public XStorable
{
	Type* mpValue;

public:

	XStorableType()
		: mpValue(0)
	{}

	XStorableType( Type& value )
		: mpValue( &value )
	{}

	virtual void Load( XSStream& stm )
	{
		stm.Read( mpValue, sizeof( Type ) );
	}

	virtual void Store( XSStream& stm )
	{
		stm.Write( mpValue, sizeof( Type ) );
	}
};

// wrapper object for standard string class

class XStorableStr : public XStorable
{
	wxString* mpStr;

public:
	XStorableStr( wxString& str )
		: mpStr ( &str )
	{}

	virtual void Load( XSStream& stm )
	{
		size_t len;
		stm.Read( &len, sizeof( size_t ) );

		mpStr->erase();

		size_t i;
		for( i = 0; i != len; ++i )
		{
			char ch;
			stm.Read( &ch, 1 );

			mpStr->append( ch );
		}
	}	

	virtual void Store( XSStream& stm )
	{
		size_t len = mpStr->length();

		stm.Write( &len, sizeof( size_t ) );

		size_t i;
		for( i = 0; i != len; ++i )
		{
			char ch = (*mpStr)[i];

			stm.Write( &ch, 1 );
		}
	}
};

// wrapper object around the actual persistant object
// which is not necessarely derived from XStorable

template <class ObjectT>
class XStorableObjProxy : public XStorable
{
private:
	ObjectT* mpObj;

public:

	XStorableObjProxy() : mpObj(0) {}

	XStorableObjProxy( ObjectT& obj )
		: mpObj( &obj )
	{}

	virtual void Load( XSStream& stm )
	{
		mpObj->Serialize( stm );
	}

	virtual void Store( XSStream& stm )
	{
		mpObj->Serialize( stm );
	}
};

// class enables persistance of objects contained in STL containers

template <class ContT>
class XStorableContainer : public XStorable
{
	ContT* mpCont;

	XStorableContainer( ContT& cont )
		: mpCont( &cont )
	{}

	virtual void Load( XSStream& stm )
	{
		size_t len;
		stm.Read( &len, sizeof( size_t ) );

		mpCont->erase( mpCont->begin(), mpCont->end() );

		size_t i;
		for( i = 0; i != len; ++i )
		{
			mpCont->push_back( ContT::value_type() );

			mpCont->back().Serialize( stm );
		}
	}

	virtual void Store( XSStream& stm )
	{
		size_t len = mpCont->size();

		stm.Write( &len, sizeof( size_t ) );

        #ifdef __GNUWIN32__
            typename ContT::iterator i;
        #endif
        #ifndef __GNUWIN32__
            ConT::iterator i;
        #endif
            
		for( i = mpCont->begin(); i != mpCont->end(); ++i )
		
			(*i).Serialize( stm );
	}
};

// helper global template functions

template <class T>
XStorableType<T> storeVar( T& obj )
{
	return XStorableType<T>( obj );
}

template <class T>
XStorableObjProxy<T> storeObj( T& obj )
{
	return XStorableObjProxy<T>( obj );
}

#endif
