/* db.hh
 * This file is part of cdbxx library
 * Copyright (c) 2004 by Stanislav Ievlev
 *
 * This file is covered by the GNU Library General Public License,
 * which should be included with libcdbxx as the file COPYING.
 */
#ifndef CDB_XX_DB_HH__
#define CDB_XX_DB_HH__

#include <stdexcept>
#include <string>
#include <vector>
#include <memory>

extern "C"
{
#include <cdb.h>
}

#include <cdbxx/iterator.hh>
#include <cdbxx/data_adapters.hh>

namespace cdbxx
{
	typedef const unsigned char* mem_pos; /**< position in the mmaped memory */

	/**
	 * special exception to handle situations when file for input database not found
	 */
	class open_error: public std::runtime_error
	{
		public:
	        explicit
		open_error(const std::string& filename):
		    std::runtime_error("unable to open file "+filename) {};
	};

	/**
	 * write-only database
	 */
	class out_db
	{
	public:
		out_db(int fd);
		out_db(const std::string& filename);
		~out_db();
		template <class K, class V> void insert(const std::vector<K> &key, const std::vector<V> &value);
		template <class V> void insert(const std::string &key, const std::vector<V> &value);
		template <class S> void insert(const std::string &key, const S *value);
	private:
		//hide dangerous operations
		out_db(const out_db&) {}
		out_db& operator=(const out_db&) {return *this;}
	
		void init();
		void insert_impl(const void *key, cdbi_t keylen, const void *value, cdbi_t valuelen);

		int	fd_; /**< database file descriptor */
		bool	self_; /**< are this fd our or not */
		std::auto_ptr<cdb_make> cdbm_; /**< internal database handler */
	};

	template <class K, class V>
	void out_db::insert(const std::vector<K> &key, const std::vector<V> &value)
	{
	    insert_impl(&key[0], sizeof(K)*key.size(), &value[0], sizeof(V)*value.size());
	}
	
	template <class V>
	void out_db::insert(const std::string &key, const std::vector<V> &value)
	{
	    insert_impl(key.c_str(), key.length(), &value[0], sizeof(V)*value.size());
	}

	template <class S>
	void out_db::insert(const std::string &key, const S *value)
	{
	    insert_impl(key.c_str(), key.length(), value, sizeof(S));
	}

	/**
	 * read-only (constant) database
	 */
	class in_db
	{
		static const cdbi_t start_offset_ = 2048;
	public:
		in_db(int fd);
		in_db(const std::string& filename);
		~in_db();
		iterator begin() const; /**< create iterator with the start position */
		iterator end() const; /**< create iterator with the stop position */
		template <class K> iterator find(const std::vector<K> &key) const;
		iterator find(const std::string &key) const;
	private:
		//hide dangerous operations
		in_db(const in_db&) {}
		in_db& operator=(in_db&){ return *this; }
		
		void init();
		iterator find_impl(const void *key, cdbi_t keylen) const;
		
		int fd_; /**< database file descriptor */
		bool self_; /**< are this fd our or not */
		cdbi_t end_offset_;
		mutable std::auto_ptr<cdb> cdbp_; /**< internal database handler, mutable 'cause I want to have const find method */
		//this type is mutable 'cause cdb_find change some internal structures
	};

	template <class K>
	iterator in_db::find(const std::vector<K> &key) const
	{
	    return find_impl(&key[0], key.size()*sizeof(K));
	}
}

#endif

