#include "Tutorial.h"

void Serialize()
{
	/// .Binary serialization
	
	/// Serialization is a mechanism that converts structured data to/from binary stream. In
	/// U++, loading and storing of data is performed by single code, in most cases represented
	/// by method `Serialize`. Serialization is performed directly with basic `Stream`. To this
	/// end, `Stream` features a single boolean representing the direction of serialization
	/// process. The direction can be checked using `IsLoading` and `IsStoring` methods and
	/// changed with `SetStoring` and `SetLoading` methods. Direction is usually set properly
	/// by derived classes (e.g. FileOut sets it to storing, FileIn to loading).
	///
	/// Shortcut to calling `Serialize` method is `operator%`, which is templated overload that
	/// calls `Serialize` for given variable (primitive types have direct overload in `Stream` class):
	
	StringStream ss;
	
	int x = 123;
	Color h = White();
	
	ss % x % h;
	
	StringStream ss2(ss.GetResult());

	int x2;
	Color h2;
	
	ss2 % x2 % h2;
	
	DUMP(x2);
	DUMP(h2);
	
	/// When serialization fails to load the data (e.g. because of wrong structure or not
	/// enough data in the stream), `Stream::LoadError` is invoked, which can trigger the
	/// exception if the stream is `LoadThrowing`:
	
	ss2.Seek(0);
	ss2.LoadThrowing();
	try {
		ss2 % x2 % h2 % x2;
	}
	catch(LoadingError) {
		LOG("Deserialization has failed");
	}
	
	/// Examples so far serve mostly like basic demonstration of serialization. In practice,
	/// the implementation is usually represented by `Serialize` method of class that is to be
	/// compatible with this concept. To that end, it is a good idea to provide means for
	/// future expansion of such class:
	
	
	struct MyFoo {
		int    number;
		Color  color;
		
		void Serialize(Stream& s) {
			int version = 0;
			s / version; // allow backward compatibility in the future
			s.Magic(31415); // put magic number into the stream to check for invalid data
			s % number % color;
		}
	};
	
	MyFoo foo;
	foo.number = 321;
	foo.color = Blue();
	
	/// `StoreAsFile`, `StoreAsString`, `LoadFromFile` and `LoadFromString` are convenience
	/// functions that simplify storing / loading objects to / from the most common forms of
	/// storage:
	
	String data = StoreAsString(foo);
	MyFoo foo2;
	LoadFromString(foo2, data);
	DUMP(foo2.number);
	DUMP(foo2.color);
	
	/// Now if `MyFoo` was to be extended to `MyFoo2` and we wanted to maintain the ability to
	/// load it from binary data stored by original `MyFoo`, we can branch on previously stored
	/// `version`:

	struct MyFoo2 {
		int    number;
		Color  color;
		String text;
		
		void Serialize(Stream& s) {
			int version = 1;
			s / version;
			s % number % color;
			if(version >= 1)
				s % text;
		}
	};
	MyFoo2 foo3;
	LoadFromString(foo3, data);
	DUMP(foo3.number);
	DUMP(foo3.color);
	
	/// Note: `operator/` is Stream method with several overloads optimized for small value -
	/// in this case `int` is stored as single byte if possible (and as 5 bytes if not).
	
	///
}
