/*

Copyright (c) 2002-2005, Yauheni Akhotnikau
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

- Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

- The name of the author may not be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.

*/

/*!
	\file
	\brief Интерфейс объекта, отвечающего за работу с БД на
		стороне клиента.
*/

#if !defined( _OESS_1__DB__CLN__DB_HPP_ )
#define _OESS_1__DB__CLN__DB_HPP_

#include <set>

#include <oess_1/db/h/declspec.hpp>

#include <auto_ptr_3/h/obj_ptr.hpp>

#include <oess_1/defs/h/ent_id.hpp>

#include <oess_1/io/h/binstream.hpp>

#include <oess_1/stdsn/h/serializable.hpp>

#include <oess_1/db/site/h/abstract_connector.hpp>

namespace oess_1 {

namespace db {

namespace cln {

//
// db_t
//

/*!
	\brief Интерфейс объекта, отвечающего за работу с БД на
	стороне клиента.
*/
class	OESS_1__DB__TYPE	db_t
{
	friend class transaction_t;
	public :
		db_t(
			//! Указатель на динамически-созданный объект,
			//! который будет автоматически уничтожен в
			//! деструкторе посредством delete.
			oess_1::db::site::abstract_connector_t * connector );
		virtual ~db_t();

		//! Подключиться к указанной БД на стороне сервера.
		virtual void
		attach(
			//! Логическое имя БД.
			const std::string & logic_name ) = 0;

		//! Отключиться от БД.
		virtual void
		detach() = 0;

		virtual oess_1::db::site::abstract_connector_t &
		query_connector();

		virtual const oess_1::db::site::abstract_connector_t &
		query_connector() const;

	//! \name Средства для работы с разделами.
	/*! \{ */

		//! Создать раздел в БД.
		/*! \since v.1.1.0

			Попытка создания уже существующего раздела
			ошибкой не считается. Содержимое раздела в этом
			случае не удаляется. */
		virtual void
		slice_create(
			//! Имя создаваемого раздела.
			const std::string & name ) = 0;

		//! Удалить раздел в БД.
		/*!
			\since v.1.4.0
		*/
		virtual void
		slice_destroy(
			//! Имя удаляемого раздела.
			const std::string & name ) = 0;

		//! Проверить существование раздела в БД.
		/*!
			\since v.1.4.0
			\return true, если раздел существует.
		*/
		virtual bool
		slice_is_exists(
			//! Имя проверяемого раздела.
			const std::string & name ) = 0;

		//! Получить имена существующих разделов в БД.
		/*!
			\since v.1.4.0
		*/
		virtual std::set< std::string >
		slice_names() = 0;

	/*! \} */

	//! \name Средства работы с сущностями.
	/*! \{ */

		//! Создать сущность.
		/*! \return идентификатор созданной сущности. */
		virtual oess_1::ent_id_t
		ent_create(
			//! Раздел, в котором нужно создавать сущность.
			const std::string & slice_name,
			//! Значение сущности.
			const oess_1::stdsn::serializable_t & ent ) = 0;

		//! Удалить сущность.
		virtual void
		ent_destroy(
			//! Идентификатор удаляемой сущности.
			const oess_1::ent_id_t & ent_id ) = 0;

		//! Удалить все сущности в указанном разделе.
		/*!
			\since v.1.4.0
		*/
		virtual void
		ent_destroy_all(
			//! Имя раздела, из которого нужно удалить все сущности.
			const std::string & slice_name ) = 0;

		//! Загрузить сущность.
		/*!
			\note Объект \a ent должен быть того же типа, что
				и сущность в БД.
		*/
		virtual void
		ent_load(
			//! Идентификатор загружаемой сущности.
			const oess_1::ent_id_t & ent_id,
			//! Приемник значения сущности.
			oess_1::stdsn::serializable_t & ent ) = 0;

		//! Изменить значение сущности.
		virtual void
		ent_update(
			//! Идентификатор изменяемой сущности.
			const oess_1::ent_id_t & ent_id,
			//! Новое значение сущности.
			const oess_1::stdsn::serializable_t & ent ) = 0;

		//! Найти идентификатор следующей существующей
		//! сущности.
		/*!
			Для поиска первой существующей сущности в разделе
			необходимо указать имя раздела и ent_id,
			эквивалентный NULL.

			Если ent_id не равен NULL, то считается, что
			поиск продолжается и имя раздела игнорируется.
			Если существующих сущностей больше нет, то
			возвращается эквивалентное NULL значение.
		*/
		virtual oess_1::ent_id_t
		ent_find_next(
			//! Имя раздела, в котором осуществляется поиск.
			const std::string & slice_name,
			//! Идентификатор текущей сущности.
			const oess_1::ent_id_t & ent_id ) = 0;
	/*! \} */

	/*! \name Средства для работы с raw-образами сущностей.
		Данные средства предназначены, в первую очередь, для
		предоставления возможности экспорта/импорта содержимого
		БД.
	*/
	/*! \{ */
		//! Создать сущность.
		/*!
			\since v.1.4.0
			\return идентификатор созданной сущности.
		*/
		virtual oess_1::ent_id_t
		ent_raw_create(
			//! Раздел, в котором нужно создавать сущность.
			const std::string & slice_name,
			//! Значение сущности.
			oess_1::io::ibinstream_t & image,
			//! Размер значения сущности в байтах.
			oess_1::uint_t image_size ) = 0;

		//! Загрузить двоичный образ сущности.
		/*!
			\since v.1.4.0
			\return размер двоичного образа сущности.
		*/
		virtual oess_1::uint_t
		ent_raw_load(
			//! Идентификатор загружаемой сущности.
			const oess_1::ent_id_t & ent_id,
			//! Приемник значения сущности.
			oess_1::io::obinstream_t & to,
			//! Количество байт из образа, которые нужно
			//! загрузить в приемник.
			//! Если имеет значение по-умолчанию, то в
			//! приемник помещается весь образ сущности.
			//! Если содержит 0, то метод ничего не копирует, а
			//! только возвращает размер двоичного образа объекта.
			oess_1::uint_t length_limit =
					static_cast< oess_1::uint_t >( -1 ) ) = 0;

		//! Изменить значение сущности.
		/*!
			\since v.1.4.0
		*/
		virtual void
		ent_raw_update(
			//! Идентификатор изменяемой сущности.
			const oess_1::ent_id_t & ent_id,
			//! Значение сущности.
			oess_1::io::ibinstream_t & image,
			//! Размер значения сущности в байтах.
			oess_1::uint_t image_size ) = 0;
	/*! \} */

	protected :
		//! Начать транзакцию.
		/*! \since v.1.1.0 */
		virtual void
		transaction_begin() = 0;

		//! Подтвердить транзакцию.
		/*! \since v.1.1.0 */
		virtual void
		transaction_commit() = 0;

		//! Откатить транзакцию.
		/*! \since v.1.1.0 */
		virtual void
		transaction_rollback() = 0;

	private :
		//! Объект, который осуществляет связь с сервером БД.
		auto_ptr_3::obj_ptr_t<
			oess_1::db::site::abstract_connector_t >
			m_connector_ptr;

	
		db_t( const db_t & );
		db_t &
		operator=( const db_t & );
};

//
// create_std_db
//

/*!
	Создание стандартной реализации интерфейса db_t.

	\return указатель на динамически созданный объект,
	который должен быть уничтожен посредством оператора
	delete.
*/
OESS_1__DB__FUNC(db_t *)
create_std_db(
	//! Указатель на динамически-созданный объект,
	//! который будет автоматически уничтожен в
	//! деструкторе посредством delete.
	oess_1::db::site::abstract_connector_t * connector );

//
// transaction_t
//

//! Класс, который обеспечивает начало, подтверждение и откат транзакций.
/*! \since v.1.1.0
	Конструктор автоматически начинает транзакцию. Деструктор
	инициирует откат транзакции, если до этого явно не был вызван
	метод commit().

	Пример использования:
	\code
	void f( oess_1::db::cln::db_t & db )
	{
		// Начинаем транзакцию.
		oess_1::db::cln::transaction_t trx( db );

		// Выполняем какие-то действия с базой.
		...

		// Подтверждаем транзакцию.
		trx.commit();
	}
	\endcode

	Деструктор исключений не выпускает. Для того, чтобы узнать,
	произошло ли в деструкторе исключение необходимо в конструкторе
	передать указатель на поток сообщений об ошибках:
	\code
	void f( oess_1::db::cln::db_t & db )
	{
		// Начинаем транзакцию.
		oess_1::db::cln::transaction_t trx( db, &std::cerr );

		// Создаем какой-то объект.
		oess_1::ent_id_t id = db.ent_create( "some_slice", some_object_t() );
		// Удалим его.
		db.ent_destroy( id );
		// Удалим его и вызовем исключение.
		db.ent_destroy( id );

		// При выходе из из функции по-исключению деструктор
		// вызовет откат транзакции. Но исключение из деструкторе
		// выпущено не будет.
	}
	\endcode
*/
class	OESS_1__DB__TYPE	transaction_t
{
	//! База, в которой проводится транзакция.
	db_t &	m_db;
	//! Была ли завершена транзакция?
	bool	m_is_finished;
	//! Необязательный поток ошибок для исключений,
	//! обнаруженных в деструкторе.
	std::ostream *	m_errs;

public :
	transaction_t( db_t & db,
		std::ostream * errs = 0 );
	~transaction_t();

	//! Подтвердить транзакцию.
	/*! Даже, если возникнет исключение, откат в деструкторе
		осуществляться не будет. */
	void
	commit();
};

} /* namespace cln */

} /* namespace db */

} /* namespace oess_1 */

#endif
