/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CRegistry.h  -  description
                             -------------------
    begin                : Tue Mar 26 2002
    copyright            : (C) 2000-2005 by Alexey S.Parshin
    email                : alexeyp@gmail.com
 ***************************************************************************/

/***************************************************************************
   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.

   This library is distributed in the hope that it will be useful, but 
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library
   General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; if not, write to the Free Software Foundation, 
   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.

   Please report all bugs and problems to "alexeyp@gmail.com"
 ***************************************************************************/

#ifndef __CREGISTRY_H__
#define __CREGISTRY_H__

#include <sptk3/sptk.h>
#include <sptk3/CStrings.h>
#include <sptk3/istring.h>

#include <map>

/// @addtogroup utility Utility Classes
/// @{

class CRegistry;
class CRegistryTopic;

/// @brief Registry item attributes.
///
/// A map of case insensitive strings to strings.
/// You can have any number of attributes defined for any registry item.
class SP_EXPORT CRegistryItemAttributes : public std::map<istring,std::string> {
   static const std::string emptyAttribute;  ///< Const empty attribute. It's used when am attributed not found and can't be created.
public:
   /// Public constructor
   CRegistryItemAttributes() {}

   /// Attribute access by name
   /// @param name const char *, an attribute name
   std::string& operator [] (const char *name);

   /// Const attribute access by name
   /// @param name const char *, an attribute name
   const std::string& operator [] (const char *name) const;
};

/// @brief Registry item.
///
/// A single registry item that contains a name and a value
class SP_EXPORT CRegistryItem : public CRegistryItemAttributes {
   friend class CRegistryTopic;
   std::string              m_name;       ///< Registry item name

public:
   /// Public constructor
   /// Used only for building simple registry items with only one attribute - value
   /// @param buffer std::string, the input data in format "name=value"
   CRegistryItem(const std::string buffer="");

   /// Copy constructor
   /// @param item const CRegistryItem&, an item to copy from
   CRegistryItem(const CRegistryItem& item);

   /// Registry item name
   /// @returns a name of the item
   const std::string& name()  const { return m_name;  }

   /// Registry item value
   /// @returns a value of the item
   const std::string& value()  const { return (*this)["value"];  }

   /// Registry item value
   /// @returns a value of the item
   const char *c_str() const { return (*this)["value"].c_str(); }

   /// Assignment of the value
   /// @param v int, a new value
   CRegistryItem& operator = (int v);

   /// Assignment of the value
   /// @param v unsigned, a new value
   CRegistryItem& operator = (unsigned v);

   /// Assignment of the value
   /// @param v double, a new value
   CRegistryItem& operator = (double v);

   /// Assignment of the value
   /// @param s const std::string, a new value
   CRegistryItem& operator = (const std::string& s)	{ (*this)["value"] = s; return *this; }

   /// Assignment of the value
   /// @param s const char *, a new value
   CRegistryItem& operator = (const char *s)        { (*this)["value"] = s; return *this; }

   /// Returns the value with the conversion
   operator unsigned () const                       { return atoi((*this)["value"].c_str()); }

   /// Returns the value with the conversion
   operator int () const                            { return atoi((*this)["value"].c_str()); }

   /// Returns the value with the conversion
   operator double () const                         { return atof((*this)["value"].c_str()); }

   /// Returns the value with the conversion
   operator std::string ()                          { return (*this)["value"]; }

   /// Returns the value with the conversion
   operator const std::string& () const             { return (*this)["value"]; }
};

/// A map of pointers to CRegistryItem objects
typedef std::map<std::string,CRegistryItem *> CRegistryItems;

/// @brief Registry topic.
///
/// A list of CRegistryItem objects.
class SP_EXPORT CRegistryTopic : public CRegistryItems {
   friend class CRegistry;

   std::string m_name;  ///< Registry topic name

public:
   /// Constructor
   /// @param name std::string, unique topic name
   CRegistryTopic(const std::string name) { m_name = name; }

   /// Destructor
   ~CRegistryTopic() { clear(); }

   /// Cleaner
   void clear();

   /// Adds an item to the topic
   /// @param buffer std::string, the input data in fomat "name=value"
   void addItem(const std::string buffer);

   /// Registry topic name
   const std::string& name() const { return m_name; }
};

/// @brief Registry modes
///
/// Modes for the registry. User registry is stored in home directory
/// (if home directory is avalable). Program registry is stored in the program's
/// startup directory.
enum CRegistryMode { USER_REGISTRY=1, PROGRAM_REGISTRY=2 };


/// @brief Registry data
///
/// Works with INI and XML configuration-files.
/// The structure of the file allows only groups of keys, or topics (first level)
/// and keys (second level). No nested topics are allowed at that moment.
/// Class allows to read both INI and XML files and write the output file
/// in the same format, or change the output format before saving the file.
class SP_EXPORT CRegistry {
   /// The map of topics
   typedef std::map<std::string,CRegistryTopic *> CRegistryTopics;

   const CRegistryItem	m_emptyItem;  	      ///< An empty string, internal
   std::string         	m_fileName;     	   ///< The registry file name
   CRegistryTopics 		m_topics;       	   ///< The list of topics
   std::string         	m_topic;        	   ///< The current topic name
   CRegistryTopic      *m_currentTopic; 	   ///< The current topic pointer
   bool                 m_xmlMode;             ///< If true, the configuration is saved as XML

   void prepareDirectory();
public:

   /// Constructor
   /// @param fileName std::string, the registry file name w/o path
   /// @param programGroupName std::string, the name of the program group to generate a directory name for the registry files. 
   /// Should be a single phrase without '\\' or '/'
   /// @param mode CRegistryMode, see CRegistryMode for details
   CRegistry(std::string fileName,std::string programGroupName,CRegistryMode mode=USER_REGISTRY);

   /// Destructor
   virtual  ~CRegistry();

   /// Sets current topic name. If the topic doesn't exist it will be created.
   void     topic(const std::string topic);

   /// Returns current topic
   CRegistryTopic& topic() { return *m_currentTopic; }

   /// Returns current topic
   const CRegistryTopic& topic() const { return *m_currentTopic; }

   /// Sets the registry file name
   void fileName(std::string fname) { m_fileName = fname; }

   /// Returns the registry file name
   const std::string fileName() { return m_fileName; }

   /// Access to the registry item inside the current topic by item name
   CRegistryItem& operator[] (const char *name);

   /// Access to the registry item inside the current topic by item name
   CRegistryItem& operator[] (const std::string& name) { return operator[](name.c_str()); }

   /// Access to the registry item inside the current topic by item name
   const CRegistryItem& operator[] (const char *name) const ;

   /// Access to the registry item inside the current topic by item name
   const CRegistryItem& operator[] (const std::string& name) const { return operator[](name.c_str()); }

   /// Loads registry from the file. Usually it's the first action with registry.
   virtual void load();

   /// Saves registry to the file. 
   virtual void save();

   /// Loads registry from the string list
   virtual void load(const CStrings& data);

   /// Saves registry to the the string list
   virtual void save(CStrings& data);

   /// Clears the registry content
   virtual void clear();

public:   

   /// Finds out the user's home directory
   static std::string homeDirectory();

   /// Sets the mode for saving a configuration
   /// If you don't set before calling save() then
   /// the mode is kept the same as it was in config file loaded
   /// @param mode bool, true if you want the configuration saved in XML format
   void xmlMode(bool mode) { m_xmlMode = mode; }
};

/// @}

#endif
