/* ****************************************************************************

 * eID Middleware Project.
 * Copyright (C) 2008-2009 FedICT.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see
 * http://www.gnu.org/licenses/.

**************************************************************************** */
//
// CDataFile Class Implementation
//
// The purpose of this class is to provide a simple, full featured means to
// store persistent data to a text file.  It uses a simple key/value paradigm
// to achieve this.  The class can read/write to standard Windows .ini files,
// and yet does not rely on any windows specific calls.  It should work as
// well in a linux environment (with some minor adjustments) as it does in
// a Windows one.
//

#ifndef __CDATAFILE_H__
#define __CDATAFILE_H__

#include <vector>
#include <string>
#include "Mutex.h"

#ifndef WIN32
#include <fcntl.h>
#endif

namespace eIDMW
{

// Globally defined structures, defines, & types
//////////////////////////////////////////////////////////////////////////////////

// AUTOCREATE_SECTIONS
// When set, this define will cause SetValue() to create a new section, if
// the requested section does not allready exist.
#define AUTOCREATE_SECTIONS     (1L<<1)

// AUOTCREATE_KEYS
// When set, this define causes SetValue() to create a new key, if the
// requested key does not allready exist.
#define AUTOCREATE_KEYS         (1L<<2)

// MAX_BUFFER_LEN
// Used simply as a max size of some internal buffers. Determines the maximum
// length of a line that will be read from or written to the file or the
// report output.
#define MAX_BUFFER_LEN				512

typedef std::wstring t_Str;


// CommentIndicators
// This constant contains the characters that we check for to determine if a 
// line is a comment or not. Note that the first character in this constant is
// the one used when writing comments to disk (if the comment does not allready
// contain an indicator)
#define CommentIndicators  t_Str(L";#")

// EqualIndicators
// This constant contains the characters that we check against to determine if
// a line contains an assignment ( key = value )
// Note that changing these from their defaults ("=:") WILL affect the
// ability of CDataFile to read/write to .ini files.  Also, note that the
// first character in this constant is the one that is used when writing the
// values to the file. (EqualIndicators[0])
#define EqualIndicators   t_Str(L"=:") 

// WhiteSpace
// This constant contains the characters that the Trim() function removes from
// the head and tail of strings.
#define WhiteSpace  t_Str(L" \t\n\r")

// st_key
// This structure stores the definition of a key. A key is a named identifier
// that is associated with a value. It may or may not have a comment.  All comments
// must PRECEDE the key on the line in the config file.
typedef struct st_key
{
	t_Str		szKey;
	t_Str		szValue;
	t_Str		szComment;

	st_key()
		: szKey(L"")
		, szValue(L"")
		, szComment(L"")
	{
	}
	st_key( t_Str const& key, t_Str const& value, t_Str const& comment)
		: szKey(key)
		, szValue(value)
		, szComment(comment)
	{
	}

} t_Key;

typedef std::vector<t_Key> KeyList;
typedef KeyList::iterator KeyItor;

// st_section
// This structure stores the definition of a section. A section contains any number
// of keys (see st_keys), and may or may not have a comment. Like keys, all
// comments must precede the section.
typedef struct st_section
{
	t_Str		szName;
	t_Str		szComment;
	KeyList		Keys;

	st_section()
	{
		szName = t_Str(L"");
		szComment = t_Str(L"");
		Keys.clear();
	}

} t_Section;

typedef std::vector<t_Section> SectionList;
typedef SectionList::iterator SectionItor;



/// General Purpose Utility Functions ///////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
t_Str	GetNextWord(t_Str& CommandLine);
int		CompareNoCase(t_Str str1, t_Str str2);
void	Trim(t_Str& szStr);
int		WriteLn(FILE *stream, wchar_t* fmt, ...);


/// Class Definitions ///////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////


// CDataFile
class CDataFile
{
// Methods
public:
				// Constructors & Destructors
				/////////////////////////////////////////////////////////////////
				CDataFile();
				CDataFile(t_Str szFileName);
	virtual		~CDataFile();

				// File handling methods
				/////////////////////////////////////////////////////////////////
	bool		Load();		//Load m_szFileName without lock
	bool		LoadAndLock();	//Load m_szFileName with lock
	bool		Save();
	bool		Close(); //Close the file

				// Data handling methods
				/////////////////////////////////////////////////////////////////

				// GetValue: Our default access method. Returns the raw t_Str value
				// Note that this returns keys specific to the given section only.
	t_Str		GetValue(t_Str szKey, t_Str szSection = t_Str(L"")); 
				// GetString: Returns the value as a t_Str
	t_Str		GetString(t_Str szKey, t_Str szSection = t_Str(L"")); 
				// GetFloat: Return the value as a float
	float		GetFloat(t_Str szKey, t_Str szSection = t_Str(L""));
				// GetInt: Return the value as an int
	int			GetInt(t_Str szKey, t_Str szSection = t_Str(L""));
				// GetInt: Return the value as a long
	long 		GetLong(t_Str szKey, t_Str szSection = t_Str(L""));
				// GetBool: Return the value as a bool
	bool		GetBool(t_Str szKey, t_Str szSection = t_Str(L""));

				// SetValue: Sets the value of a given key. Will create the
				// key if it is not found and AUTOCREATE_KEYS is active.
	bool		SetValue(t_Str szKey, t_Str szValue, 
						 t_Str szComment = t_Str(L""), t_Str szSection = t_Str(L""));

				// SetFloat: Sets the value of a given key. Will create the
				// key if it is not found and AUTOCREATE_KEYS is active.
	bool		SetFloat(t_Str szKey, float fValue, 
						 t_Str szComment = t_Str(L""), t_Str szSection = t_Str(L""));

				// SetInt: Sets the value of a given key. Will create the
				// key if it is not found and AUTOCREATE_KEYS is active.
	bool		SetInt(t_Str szKey, int nValue, 
						 t_Str szComment = t_Str(L""), t_Str szSection = t_Str(L""));

				// SetLong: Sets the value of a given key. Will create the
				// key if it is not found and AUTOCREATE_KEYS is active.
	bool		SetLong(t_Str szKey, long nValue, 
				t_Str szComment = t_Str(L""), t_Str szSection = t_Str(L""));

				// SetBool: Sets the value of a given key. Will create the
				// key if it is not found and AUTOCREATE_KEYS is active.
	bool		SetBool(t_Str szKey, bool bValue, 
						 t_Str szComment = t_Str(L""), t_Str szSection = t_Str(L""));

				// Sets the comment for a given key.
	bool		SetKeyComment(t_Str szKey, t_Str szComment, t_Str szSection = t_Str(L""));

				// Sets the comment for a given section
	bool		SetSectionComment(t_Str szSection, t_Str szComment);

				// DeleteKey: Deletes a given key from a specific section
	bool		DeleteKey(t_Str szKey, t_Str szFromSection = t_Str(L""));

				// DeleteSection: Deletes a given section.
	bool		DeleteSection(t_Str szSection);
				
				// Key/Section handling methods
				/////////////////////////////////////////////////////////////////

				// CreateKey: Creates a new key in the requested section. The
	            // Section will be created if it does not exist and the 
				// AUTOCREATE_SECTIONS bit is set.
	bool		CreateKey(t_Str szKey, t_Str szValue, 
		                  t_Str szComment = t_Str(L""), t_Str szSection = t_Str(L""));
				// CreateSection: Creates the new section if it does not allready
				// exist. Section is created with no keys.
	bool		CreateSection(t_Str szSection, t_Str szComment = t_Str(L""));
				// CreateSection: Creates the new section if it does not allready
				// exist, and copies the keys passed into it into the new section.
	bool		CreateSection(t_Str szSection, t_Str szComment, KeyList Keys);

				// Utility Methods
				/////////////////////////////////////////////////////////////////
				// SectionCount: Returns the number of valid sections in the database.
	size_t		SectionCount();
				// KeyCount: Returns the total number of keys, across all sections.
	size_t		KeyCount();
				// SetFileName: For use when creating the object by hand
				// initializes the file name so that it can be later saved.
	void		SetFileName(t_Str szFileName);
				// CommentStr
				// Parses a string into a proper comment token/comment.
	t_Str		CommentStr(t_Str szComment);				

				// GetSection: Returns the requested section (if found), NULL otherwise.
	t_Section*	GetSection(t_Str szSection);

protected:
	t_Section*	GetSectionInt(t_Str szSection);
	bool		CreateSectionInt(t_Str szSection, t_Str szComment);
	bool		SetValueInt(t_Str szKey, t_Str szValue, 
						 t_Str szComment = t_Str(L""), t_Str szSection = t_Str(L""));

				// Note: I've tried to insulate the end user from the internal
				// data structures as much as possible. This is by design. Doing
				// so has caused some performance issues (multiple calls to a
				// GetSection() function that would otherwise not be necessary,etc).
				// But, I believe that doing so will provide a safer, more stable
				// environment. You'll notice that nothing returns a reference,
				// to modify the data values, you have to call member functions.
				// think carefully before changing this.

				// GetKey: Returns the requested key (if found) from the requested
				// Section. Returns NULL otherwise.
	t_Key*		GetKey(t_Str szKey, t_Str szSection);


	FILE*		m_stream;
	static CMutex 		sm_Mutex;	/**< Mutex for exclusive access */

#ifndef WIN32
	struct flock m_tFl;
#endif

// Data
public:
	long		m_Flags;		// Our settings flags.

protected:
	bool		Load(bool bLock);

	SectionList	m_Sections;		// Our list of sections
	t_Str		m_szFileName;	// The filename to write to
	bool		m_bDirty;		// Tracks whether or not data has changed.
};


}  // namespace eIDMW

#endif
