// -*- c++ -*-
//------------------------------------------------------------------------------
// $Id: IniFile.h,v 1.4 2003/09/13 07:01:07 vlg Exp $
//------------------------------------------------------------------------------
//                             IniFile.h
//------------------------------------------------------------------------------
//  Copyright (C) 2003  Vladislav Grinchenko <vlg@users.sourceforge.net>
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  Date: Sat Sep  6 00:17:52 EDT 2003
//------------------------------------------------------------------------------
#ifndef INIFILE_H
#define INIFILE_H

#include <string> 
#include <list>
#include <fstream>
#include <iostream>
#include <iomanip>

using std::list;
using std::string;
using std::pair;

#include <assa/Regexp.h>
#include <assa/CommonUtils.h>

/** @file IniFile.h

   A Windows-style INI configuration file management class.
*/

namespace ASSA {

	class IniFile 
	{
	public:
		/** A tuple is a name/value pair.
		 */
		typedef pair<string, string> tuple_type;

		/** A section is a logical subcategory of related
			configuration information.
		*/
		typedef pair<string, list<tuple_type> > sect_type;

		/** INI configuration is the collection of sections.
		 */
		typedef list<sect_type> config_type;

		/// Mutable iterator over the list of configuration sections
		typedef config_type::iterator config_iterator;

		/// Constant iterator over the list of configuration sections
		typedef config_type::const_iterator const_config_iterator;

		/// Mutable iterator over name/value pairs in a section
		typedef list<tuple_type>::iterator tuple_iterator;

		/// Constant iterator over name/value pairs in a section
		typedef list<tuple_type>::const_iterator const_tuple_iterator;

	public:
		/** Do-nothing constructor.
			@param fname_ Name of the INI file
		*/
		IniFile (const string& fname_);

		/** Destructor does not save cache data to the file.
			You should explicitly call sync() if you want your
			data to be saved to the file.
		*/
		~IniFile ();

		/** Compare two configurations.
			@return true if two configurations are the same; false otherwise.
		 */
		bool operator== (const IniFile& rhs_) const
			{ return (m_config == rhs_.m_config); }

		/** Compare two configurations.
			@return true if two configurations are the same; false otherwise.
		 */
		bool operator!= (const IniFile& rhs_) const
			{ return (! (*this == rhs_)); }

		/** Load configuration data from the file. The file
			name is specified in the constructor.

			@return 0 on success; -1 if there was a syntax error.
		*/
		int load ();

		/** Clear up configuration cache.
		*/
		void drop_all () { m_config.clear (); }

		/** Write cached configuration to the file.
			Filename used is the one given in the constructor.

			@return 0 on success; -1 if opening/writing to the file failed.
		*/
		int sync ();

		/** Write cached configuration to the file fname_.

			@param fname_ Name of the output file.
			@return 0 on success; -1 if opening/writing to the file failed.
		*/
		int sync (const string& fname_);

		/** Add new section.

		    @param section_ Section name to add
		*/
		void add_section (const string& section_);

		/** Remove section from cache.
			@param section_ Section to remove
			@return 0 on success; -1 if section was not found
		*/
		int drop_section (const string& section_);

		/** Add or change name/value pair in the section.

			@param section_ Section name 
			@param newkey_ Name/value pair
			@return 0 on success; -1 if section is not found
		*/
		int set_pair (const string& section_, const tuple_type& newkey_);

		/** Remove name/value pair from the section in cache.
			@param section_ Section that holds name/value pair.
			@param name_ Name part of name/value pair.
			@return 0 on success; -1 if section_ or name_ was not found
		*/
		int drop_pair (const string& section_, const string& name_);

		/** Find and return a value of the name/value pair in the section
			section_.

			@param section_ Section name to search for name/value
			@param name_    Name part of name/value pair
			@return value part of name/value; or an empty string if not found.
		*/
		string get_value (const string& section_, const string& name_) const;

		/** Find section by its name

			@param section_ Section name to earch for
			@return An iterator pointing to sect_type of the section if 
			it is found or pointing to sect_end() if not.
		*/
		config_iterator find_section (const string& section_);
		
		/** Find section by its name

			@param section_ Section name to earch for
			@return An iterator pointing to the sect_type
			of the section if it is found or pointing to sect_end() if not.
		*/
		const_config_iterator find_section (const string& section_) const;

		/** Return iterator to the first section.
		 */
		const_config_iterator sect_begin () const { return m_config.begin (); }

		/** Return iterator past the last section.
		 */
		config_iterator sect_end () { return m_config.end (); }

		/** Return number of sections in the cache
		 */
		unsigned int size () const { return m_config.size (); }

		/** Dump cache to the log file.
		 */
		void dump () const;

	private:
		/** Remove square brakets around section name.
			@param text_ (IN/OUT) String to work on
			@return 0 on success; -1 on error
		*/
		int trim_section_name (string& text_);

	private:
		/// INI file name
		string m_fname;
		
		/// File stream
		std::fstream m_stream;

		/// Cache holds the entire INI file in memory
		config_type m_config;

		/// Section header match
		Regexp m_section_pttrn;
		
		/// Name/value pair match
		Regexp m_tuple_pttrn;

		/// Comment match
		Regexp m_comment_pttrn;
	};

	inline int IniFile::
	trim_section_name (string& text_) 
	{
		return (Utils::ltrim (text_, "[") == 0 &&
				Utils::rtrim (text_, "]") == 0) ? 0 : -1;
	}

	inline int IniFile::
	sync ()
	{
		trace_with_mask ("IniFile::sync", INIFILE);
		sync (m_fname);
	}


} // @end namespace

#endif // INIFILE_H
