/*
 * Copyright (C) 2007-2023 Xagasoft, All rights reserved.
 *
 * This file is part of the libbu++ library and is released under the
 * terms of the license contained in the file LICENSE.
 */

/**
 *@page howto_streams Working With Streams
 *
 * Working with libbu++ streams is simple and very straight-forward, but there
 * are a few things to understand when you pick them up for the first time.
 *
 *@section whatis What is a stream?
 * A stream is a mechanism that allows data to be transported from one place to
 * another without interruption and in order.  Examples of this include data
 * being written to a file or being read from a file, data being written to a
 * network socket or read from a network socket, and so on.
 * Every type of stream will support a different number of options, for example
 * file streams are usually seekable, that is, you can select where in the file
 * you are reading and writing, however network sockets are not seekable.  All
 * of these properties can be determined at runtime through the Stream class.
 *
 *@section filters Using filters with streams
 * Libbu++ supports a special type of Stream that is known as a Filter.  Filters
 * are actually streams, except instead of reading or writing to or from a
 * file they read or write to or from another stream.  This provides a handy
 * way of chaining any number of filters together along with end-point streams
 * such as files.  One example of this would be applying a BZip2 compression
 * filter to a file stream to compress all data being written to it.
 * Once a filter is applied the transformations that it applies to the data will
 * happen automatically as you use the stream.
 *
 * One important thing to remember about streams is that they will commonly
 * change the options that you have when it comes to interacting with the
 * stream.  This is not a bad thing, it is necesarry, but it is easy to tell
 * when this is happening.  To continue with the above example, you can seek in
 * a File stream, but not in a BZip2 filtered File stream.  This has to do with
 * the way the compression algorithms work and restrict the operations that can
 * be easily performed on the data.
 *
 * Not all changes that filters apply will be restrictive, filters such as the
 * Buffer filter will add the ability to seek (to an extent) within the buffered
 * data.
 *
 *@section difference How are libbu++ streams different form stl streams?
 * The most basic difference is that libbu++ streams are geared more towards a
 * lower level feel, giving you easy and more direct access to many features,
 * while seperating all of the formatting code used for console I/O and number
 * to text conversion, etc, in a seperate place.
 *
 * Libbu++ streams are very direct about how the data is handled.  All end-point
 * streams will always handle the data that you provide or request without any
 * modification or formatting, very much like the way libc style file access
 * works.
 *
 * The design of Libbu++ streams was, from the begining, to try to make it as
 * easy as possible to write general code that was as easy as possible to
 * extend, and as clear as possible.  We have accomplished this by making
 * streams simple, yet flexible, with a clear API and a flexible filter system
 * that something geared towards more general formatting, conversion can't
 * touch.
 *
 *@section usage Using streams directly
 * To create a stream depends on the type of stream that you're interested in,
 * each type has it's own constructors designed to create the stream and get
 * it ready for use.  We'll use the Bu::File stream as an example here.
 *
 *@code
 	// The constructor and each function call to access the stream could throw
	// an exception, it's important to catch these and handle them
	// appropriately.
	try
	{
		// sFileName is a Bu::FString object containing the name of the file to
		// open, possibly with path elements.  The second parameter works like
		// the fopen mode parameter, and can be "w" for write, "r" for read, or
		// "a" for append.  This will likely be replaced with a bitfield later.
		Bu::File f( sFileName, "w");

		// At this point we know our file is open, lets write some data to it
		// The first parameter is a pointer to the data to write, the second is
		// how many bytes to write.
		f.write( "Test data, boring old test data.\n", 33 );

		// We don't actually need to close the file explicitly, the
		// deconstructor will take care of that for us, we could if we wanted
		// to, simply by calling the f.close() function, but it isn't necesarry.
	}
	catch( Bu::FileException &e )
	{
		// Here we can report the error to the system, whatever it happened to
		// be.
		printf("Error:  %s\n", e.what() );
	}
 @endcode
 * This is a most basic example, but it covers all the basics.  You don't need
 * much more than this to write just about any file i/o program, just use the
 * Bu::Stream::read() and Bu::Stream::write() functions.
 *
 * Now lets look at how to add a stream to the mix, we'll BZip2 compress the
 * above example.
 *@code
 	// Again, we want to catch exceptions in the entire file handling block.
	try
	{
		// We create the file just like before, nothing different about this.
		Bu::File f( sFileName, "w");

		// Here, however, we create our Bu::BZip2 object, and pass it a
		// reference to the file.  Now our data will go through Bu::BZip2, then
		// into Bu::File and onto disk.  Notice that you don't have to specify
		// wether you want to read or write explicitly, the filter can usually
		// determine this for itself by examining the underlying stream.
		Bu::BZip2 bz2( f );

		// This is done exactly as before, but this time we write into bz2, not
		// f.  We already know how to do this, because both BZip2 and File
		// inherit from Bu::Stream, so they both have very similar (if not
		// identicle) API.
		bz2.write( "Test data, boring old test data.\n", 33 );

		// Just like last time, we don't have to close either stream explicitly,
		// they will be destroyed appropriately by the system, and in the
		// correct order (bz2 first, then f).
	}
	catch( Bu::FileException &e )
	{
		// Here we can report the error to the system, whatever it happened to
		// be.
		printf("Error:  %s\n", e.what() );
	}
 @endcode
 * 
 * As you can tell in this example, using streams and filters is very easy to
 * do, and even makes formerly very complex procedures take almost no time at
 * all.
 *
 *@section usingmore Accepting strings in other classes and functions
 *
 * When writing a class or function that uses an already opened stream to read
 * or write data from or to it's important to plan ahead, and probably accept
 * an object reference of type Bu::Stream.  This makes your code immediately
 * much more portable, useful, and flexible.  You don't have to change anything
 * about the way you write your class or function, use the standard Bu::Stream
 * functions like read and write just like before.
 *
 * For example, lets say you're creating a new image format, and need to be able
 * to store images on disk and read them off again, so you write the approprate
 * file fromat handlers.  By using Bu::Stream instead of Bu::File you allow
 * anyone using your image format to send that data anywhere they would like,
 * including files, sockets, memory buffers, composite archives, and even
 * through types of streams that don't exist yet.  Not only that, but since
 * every filter is a stream, anyone is now free to pass you a BZip2 filter or
 * an encryption filter, or any other chain, so that your data can be
 * compressed, and possibly encrypted, and sent over the network without any
 * changes to your original code.
 *
 * Understandably you probably want to be sure of a few basics before you go
 * writing image data to any old stream, such as ensuring the stream supports
 * writing to begin with.  This can be easily handled by taking advantage of
 * the capability accessors in Bu::Stream, these begin with "can" and "is," see
 * the documentation for more information.
 */
