/*
 * The Cryptonit security software suite is developped by IDEALX
 * Cryptonit Team (http://IDEALX.org/ and http://cryptonit.org).
 *
 * Copyright 2003-2006 IDEALX
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 as published by the Free Software Foundation.
 * 
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301, USA. 
 *
 * In addition, as two special exceptions:
 *
 * 1) IDEALX S.A.S gives permission to:
 *  * link the code of portions of his program with the OpenSSL library under
 *    certain conditions described in each source file
 *  * distribute linked combinations including the two, with respect to the
 *    OpenSSL license and with the GPL
 *
 * You must obey the GNU General Public License in all respects for all of the
 * code used other than OpenSSL. If you modify file(s) with this exception,
 * you may extend this exception to your version of the file(s), but you are
 * not obligated to do so. If you do not wish to do so, delete this exception
 * statement from your version, in all files (this very one along with all
 * source files).

 * 2) IDEALX S.A.S acknowledges that portions of his sourcecode uses (by the
 * way of headers inclusion) some work published by 'RSA Security Inc.'. Those
 * portions are "derived from the RSA Security Inc. PKCS #11Cryptographic
 * Token Interface (Cryptoki)" as described in each individual source file.
 */

#ifndef DirectoryService_h
#define DirectoryService_h

#include "config.h"

#if defined(HAVE_EXT_HASH_MAP)
#include <ext/hash_map>
using namespace __gnu_cxx;
#else
#if defined(HAVE_HASH_MAP)
#include <hash_map>
#else
#error Cannot find hash_map header !
#endif
#endif


#include "Entry.hh"


namespace Cryptonit
{

   /** This class abstract access to a "Directory Service" style database.
    *  A DirectoryService database contains a list of entries, where each entries
    *  contains an attribute list, attribute are formed with a values list of a
    *  specified type.
    *  The "default" implementation provide storing and accessing method using
    *  hash maps.
    */
   class DirectoryService 
   {

      private:

	 // Hash function for string class
	 struct DirectoryService_hash_str
	 {
	       inline size_t __stl_hash_string(const char* __s) const
	       {
		  unsigned long __h = 0; 
		  for ( ; *__s; ++__s)
		     __h = 5*__h + *__s;
	    
		  return size_t(__h);
	       }
	
	       size_t operator()(const std::string s1) const
	       {
		  return  __stl_hash_string( s1.c_str() );
	       }
	 };
    

	 struct DirectoryService_eqstr 
	 {
	       bool operator()(const std::string s1, const std::string s2) const {
		  return s1 == s2;
	       }
	 };


	 // Représentation interne de la base (liste des entrées)
	 hash_map<const std::string, Entry*, DirectoryService_hash_str, DirectoryService_eqstr>  entryList;


      public:

	 /** Instentiate a new DirectoryService object
	  */
	 DirectoryService();


	 /** Destroy a DirectoryService object, erasing all data contained
	  *  in the entry list.
	  */
	 virtual ~DirectoryService();


	 /** Instensiates a new DirectoryService with a simple ID ("ldap", "ldapcache",
	  *  "config") or with an URI.
	  *
	  *  @param uri : character string defining the DirectoryService type
	  */
	 static DirectoryService* factory( const std::string uri = "" );


	 /** Appends a new entry with default values in the entry list.
	  *  Returns a pointer on the new entry, or NULL if the operation
	  *  fails.
	  *
	  * @param entry_name : the entry name
	  */
	 virtual Entry* append( const std::string entry_name );


	 /** Appends a new entry with given informations.
	  *  Returns true if the operation succeed.
	  * 
	  *  @param entry_name : the entry name
	  *  @param entry : entry informations
	  */
	 virtual bool append( const std::string entry_name, Entry* entry );


	 /** Appends a new entry with a new attribute.
	  *  Returns true if the operation succeed.
	  * 
	  *  @param entry_name : the entry name
	  *  @param attribute_name : the attribute_name
	  *  @param val : attribute value represented by a character string
	  */
	 virtual bool append( const std::string entry_name, 
			      const std::string attribute_name, 
			      const std::string& val);


	 /** Appends a new entry with a new attribute of a specified type.
	  *  Returns true if the operation succeed.
	  * 
	  *  @param entry_name : the entry name
	  *  @param attribute_name : the attribute_name
	  *  @param val : attribute value represented by a character string
	  *  @param type : attribute value type (AttributeType)
	  */
	 virtual bool append( const std::string entry_name, 
			      const std::string attribute_name, 
			      const std::string& val,
			      AttributeType type);


	 /** Append a new entry with a new attribute of a specified type.
	  *  The value length has to be specified. Use this method if you
	  *  want to add binary values.
	  *  Returns true if the operation succeed.
	  * 
	  *  @param entry_name : the entry name
	  *  @param attribute_name : the attribute_name
	  *  @param val : attribute value represented by a character string
	  *  @param len : attribute value length
	  *  @param type : attribute value type (AttributeType)
	  */
	 virtual bool append( const std::string entry_name, 
			      const std::string attribute_name, 
			      char* val,
			      unsigned long len,
			      AttributeType type);


	 /** Erases an entry in the list.
	  *  Returns true if the operation succeed.
	  *
	  *  @param name : entry name to be erased
	  */
	 virtual bool erase( const std::string name );


	 /** Erases all entries in the list.
	  *  Returns true if the operation succeed.
	  */
	 virtual bool clear();



	 /** Establish connection to the data source. This function is used
	  *  for "online" connection (there is no data cached into memory).
	  *  This function has to be implemented by the backend.
	  *
	  *  @param params : array of strings interpreted by the backend
	  */
	 virtual bool bind( const std::string params[] ) { return false; }


         /** Provide a login if required fo accessing the DirectoryService
	  *  @param login : directory service login
	  */
         virtual bool setLogin( const std::string login ) { return false; }


         /** Provide a password if required fo accessing the DirectoryService
	  *  @param passwd : directory service password
	  */
         virtual bool setPassword( const std::string passwd ) { return false; }


         /** Establish connection to the data source. This function is used
	  *  for "offline" connection (all datas are cached into memory).
	  *  Writing operations need to be commited with the commit() method.
	  *  This function has to be implemented by the backend.
	  *
	  *  @param params : array of strings interpreted by the backend
	  */
	 virtual bool read( const std::string params[] ) { return false; }


	 /** Reads data from a character string buffer, and copied into memory.
	  *  Writing operations need to be commited with the commit() method.
	  *  This function has to be implemented by the backend.
	  *
	  *  @param buffer : character string buffer
	  */
	 virtual bool readFromBuffer( const std::string& buffer ) { return false; }


	 /** Commits cached data if necessary.
	  *  Returns true if the operation succeed.
	  *  This function has to be implemented by the backend.
	  *
	  *  @param params : array of strings interpreted by the backend
	  */
	 virtual bool commit( const std::string params[] ) { return false; }


	 /** Returns a pointer on a specifief entry, return NULL if it doesn't exist.
	  *
	  *  @param name : entry name
	  */
	 virtual Entry* getEntry( const std::string name );

    
	 /** Returns the total number of entries.
	  */
	 virtual unsigned int getNbEntry() const;


	 /** Finds a specified entry and fill a given attribute list.
	  *  Returns true if the entry exists and if there is at least one
	  *  required attribute provided by the searched entry.
	  *
	  *  @param entry_name : name of the searched entry
	  *  @param entry : wanted attributes in form of Entry
	  */
	 virtual bool find( const std::string entry_name, Entry& entry );


	 // ---- Recherche ----
	 // Lance une recherche sur la base, expr est interpréter par le backend
	 // Renvoie 'true' si il y a au moins une occurence. On accède à la première
	 // grâce à la fonction FindNext(), de même pour les occurences suivantes.
	 virtual bool findExpression( const std::string expr ) const { return false; }

	 // Renvoie l'occurence suivante si elle existe, retourne NULL sinon
	 // A utiliser conjointement avec FindExpression
	 // Voir si on fait une copie ou pas (AMHA oui, ce qui permet de gérer le RO)
	 virtual Entry* findNext() const { return NULL; }


	 // Remets à zéro la recherche, n'a de sens qu'avec FindExpression
	 // Pas sur de l'utilité -> la recherche peut etre RAZ dans FindExpression
	 //virtual void BeginFind();




	 class iterator;
	 friend class iterator;

	 /** Nested class for iterating inside the entryList.
	  */
	 class iterator {

	       /** Reference on the current DirectoryService object.
		*/
	       DirectoryService& ds;

	       /** Current iterator on the entryList hash map.
		*/
	       hash_map<const std::string, Entry*, DirectoryService_hash_str, DirectoryService_eqstr>::iterator itr;

	    public:

	       /** Returns the first element of the pair, aka entry name.
		*
		*  @return Entry name.
		*/
	       const std::string first() { return itr->first; }

	       /** Returns the second element of the pair, a pointer on the Entry.
		*
		*  @return a pointer on the Entry.
		*/
	       Entry* second() { return itr->second; }

	       /** Creates the iterator.
		*/
	       iterator(class DirectoryService& d) : ds(d), itr( ds.entryList.begin() ) {}

	       /** Creates the "end sentinel" iterator
		*/
	       iterator(class DirectoryService& d, bool) : ds(d), itr( ds.entryList.end() ) {}

	       /** Returns the current value, use it for iterator operation.
		*  You'd better use first() and second() method if you want to access the iterator content.
		*/
	       hash_map<const std::string, Entry*, DirectoryService_hash_str, DirectoryService_eqstr>::iterator 
	       current() const { return itr; }

	       /** Prefix incrementation.
		*/
	       hash_map<const std::string, Entry*, DirectoryService_hash_str, DirectoryService_eqstr>::iterator 
	       operator++() { return ++itr; }

	       /** Postfix incrementation.
		*/
	       hash_map<const std::string, Entry*, DirectoryService_hash_str, DirectoryService_eqstr>::iterator 
	       operator++( int ) { return itr++; }

	       /** Comparaison equality operator.
		*/
	       bool operator==(const iterator& rv) const {
		  return itr == rv.itr;
	       }

	       /** Comparaison difference operator.
		*/
	       bool operator!=(const iterator& rv) const {
		  return itr != rv.itr;
	       }
	       // 	friend ostream& operator<<(ostream& os, const iterator& it) {
	       // 	    return os << it.current();
	       // 	}

	 };
    
	 /** Returns an iterator on the begin of the entryList hash map.
	  */
	 iterator begin() { return iterator(*this); }

	 /** Returns an iterator on the end of the entryList hash map.
	  */
	 iterator end() { return iterator(*this, true);}

	
   };

} // Namespace
#endif
