/******************************************************************************
 * $Id: gfx_config_file.hh,v 1.38 2006/08/03 02:17:21 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Functions for a configuration file based on key/value tuples.
 * @remarks	Similar to GNOME's gnome_config*().
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 ******************************************************************************/

#ifndef GFX_CONFIG_FILE_HH
#define GFX_CONFIG_FILE_HH 1

namespace gfx {

////////////////////////////////////////////////////////////////////////////////
/// @class ConfigFile
/// @verbatim
///
/// Overview:
/// ---------
/// This class is for storing and reading a program's configuration to a file
/// (and it could be used as a simple database in lieue of DB or GDBM).
/// The configuration data consisting of type/key/value tuples similar in concept
/// to GNOME's gnome_config*().  A tuple's value has a type (int, float, string).
///
/// NOTE/WARNING: If auto-flush is disabled (for speed), client must periodically call Flush().
/// Write() just updates a STL map, it doesn't write to the configuration file!
///
/// How to use:
/// -----------
/// The configuration file is opened by the constructor and closed by the destructor.
/// Example of opening the configuration file:
///
///    ConfigFile config( "ProgramName" );
///    if ( config.IfEmpty() ) return;          // configuration is new (ok)
///    if ( ! config.IfValid() ) { Error(); }   // error
///
/// Each tuple consists of a key (name), its value, and its value's type.
/// To store a tuple, call Write().
/// To retrieve a tuple, call Read().
/// To remove a tuple, call Remove().
///
///    config.Write( "highscore", (int)    536000  );
///    config.Write( "game",      (string) "PacMan" );
///    config.Write( "offset",    (double) 0.123   );
///    config.Read(  "highscore", highscore );
///    config.Remove( "highscore" );
///
/// Access() is provided in order to write a a common function that
/// stores and retrieves tuples depending on a variable:
///
///    (don't write "access" as it's in unistd.h)
///
///    int acc = ConfigFile::eAccess_READ;
///    int acc = ConfigFile::eAccess_WRITE;
///    config.Access( acc, "highscore", &highscore );
///
/// Methods that modify the configuration will automatically update (flush)
/// changes to the configuration file.  But optionally for speed, autoflush
/// can be disabled by a constructor arg.  Disabling autoflush is faster but
/// then the user is responsible for calling Flush() or else changes will be lost!
///
/// Restrictions/limitations:
/// -------------------------
/// o Keys must not contain any whitespace (but string values can).
/// o If the end-user writes comments to the configuration file
///   (or does any change for that matter), they'll be discarded/overwritten
///   when the program modifies the configuration.
///
/// Implementation notes:
/// ---------------------
/// The configuration file consists of a set of lines containing tuples.
/// A tuple consists of a key, its value, its value's type (int,double,string).
///
/// For example, starting with a new configuration file, this code...
///
///    Write( "game",      "PacMan"        );
///    Write( "highscore", (int)    536000 );
///    Write( "offset",    (double) 0.123  );
///
/// ...would produce these lines:
///
/// string game 6 PacMan
/// int highscore 536000
/// double offset 0.123
/// END
///
/// String values have a length field, hence the 6 for "PacMan".
///
/// "END" may indeed appear more than once.  The tuples after the first "END"
/// are removed tuples.  This will happen if the configuration file once had
/// more tuples than it currently does.
///
/// @endverbatim
class ConfigFile
{
    PREVENT_COPYING(ConfigFile)
public:
				ConfigFile( const string& appname, const string& fname = "", bool autoflush = true );
				~ConfigFile() { }
	bool		IfValid( void ) { return mValid; }
	bool		IfEmpty( void ) { return mTupleMap.empty(); }
	bool		Write( const string& key, const int&    val );
	bool		Write( const string& key, const double& val );
	bool		Write( const string& key, const string& val );
	bool		Read( const string& key, int&    val /*OUT*/ );
	bool		Read( const string& key, double& val /*OUT*/ );
	bool		Read( const string& key, string& val /*OUT*/ );
	enum		eAccess { eAccess_WRITE, eAccess_READ };
	bool		Access( ConfigFile::eAccess acc, const string& key, int&    val /*IN/OUT*/ );
	bool		Access( ConfigFile::eAccess acc, const string& key, double& val /*IN/OUT*/ );
	bool		Access( ConfigFile::eAccess acc, const string& key, string& val /*IN/OUT*/ );
	bool		Remove( const string& key );
	bool		Flush( void );

private:
#ifdef _WIN32
public: // workaround for VC++ 6 broken friend declaration
#endif

    // This class is copyable and the default copy ctor suffices
	class ConfigTuple
	{
	public:
				ConfigTuple( void );
				~ConfigTuple();
		void	Clear( void );
		bool	Write( fstream& fs );
		bool	Read( fstream& fs, const string& type );
		enum eType
		{
			eType_INVALID,
			eType_INT,
			eType_DOUBLE,
			eType_STRING
		};
		ConfigTuple::eType	mType;          ///< type of the value
		string				mKey;           ///< its name
		int					mValInt;		///< valid only if mType is int
		double				mValDouble;		///< valid only if mType is double
		string				mValString;		///< valid only if mType is string
		DECL_TYPESIG( TYPESIG_LIB_CONFIG_TUPLE )
	};

private:
	bool        Parse( void );

	bool                        mValid;			///< if configuration is OK
	bool                        mAutoflush;		///< if false the user must call Flush()
	string                      mAppname;		///< might determine name of configuration file
	fstream                     mStream;		///< configuration file
	map<string,ConfigTuple>     mTupleMap;      ///< map of tuples

friend bool CompareTuples( const ConfigFile::ConfigTuple& t1, const ConfigFile::ConfigTuple& t2 );
};

bool CompareTuples( const ConfigFile::ConfigTuple& t1, const ConfigFile::ConfigTuple& t2 );

} // namespace gfx

#endif // GFX_CONFIG_FILE_HH
