/*

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.

*/

/*
	ObjESSty-1
*/

/*!
	\file
	\brief Стандартная реализация интерфейса db_content_t.
*/

#if !defined( _OESS_1__DB__IMPL__DB_CONTENT__STD_DB_CONTENT_HPP_ )
#define _OESS_1__DB__IMPL__DB_CONTENT__STD_DB_CONTENT_HPP_

#include <auto_ptr_3/h/obj_ptr.hpp>

#include <oess_1/db/storage/h/stream_storage.hpp>

#include <oess_1/db/impl/db_content/h/db_content.hpp>
#include <oess_1/db/impl/db_content/h/client_map.hpp>

namespace oess_1
{

namespace db
{

namespace impl
{

namespace db_content
{

//
// trans_owner_info_t
//

/*!
	\since v.1.1.0
	\brief Хранитель информации о владельце транзакции.
*/
class	trans_owner_info_t
{
public :
	trans_owner_info_t();
	~trans_owner_info_t();

	//! Установить владельца транзакции.
	/*! Транзакция считается начатой. */
	void
	begin(
		const std::string & client_id );

	//! Завершить транзакцию.
	void
	commit();

	//! Откатить транзакцию.
	void
	rollback();

	//! Проверка того, что клиент должен быть
	//! владельцем транзакции.
	/*! Если транзакция начата и клиент не является ее
		владельцем, то порождается иключение. */
	void
	must_be_owner(
		const std::string & client_id ) const;

	//! Является ли клиент владельцем транзакции.
	/*! \return true, если транзакция начата и клиент
		является ее владельцем. */
	bool
	is_owner(
		const std::string & client_id ) const;

	//! Начата ли транзакция?
	inline bool
	is_started() const
	{
		return !m_owner.empty();
	}

	//! Принудительный возврат в исходное состояние.
	inline void
	cleanup()
	{
		m_owner = "";
	}

private :
	//! Идентификатор владельца транзакции.
	/*! Пустое значение указывает на то, что
		транзакция не начата. */
	std::string	m_owner;
};

//
// std_db_content_t
//

/*!
	\brief Стандартная реализация интерфейса db_content_t.
*/
class	std_db_content_t :
	public db_content_t
{
	public :
		std_db_content_t();
		virtual ~std_db_content_t();

	//! \name Реализация унаследованных методов.
	//! \{
		virtual void
		open(
			const std::string & name,
			const oess_1::db::storage::config_t & config,
			bool is_read_only,
			bool is_auto_repair_enabled );

		virtual void
		client_attach(
			const std::string & client_id );

		virtual void
		client_detach(
			const std::string & client_id );

		virtual oess_1::uint_t
		client_count() const;

		virtual void
		slice_create(
			const std::string & client_id,
			const std::string & name );

		virtual void
		slice_destroy(
			const std::string & client_id,
			const std::string & name );

		virtual bool
		slice_is_exists(
			const std::string & client_id,
			const std::string & name );

		virtual std::set< std::string >
		slice_names(
			const std::string & client_id );

		virtual oess_1::ent_id_t
		ent_create(
			const std::string & client_id,
			const std::string & slice_name,
			oess_1::io::istream_t & ent_value,
			oess_1::uint_t ent_size );

		virtual void
		ent_destroy(
			const std::string & client_id,
			const oess_1::ent_id_t & ent_id );

		virtual void
		ent_destroy_all(
			const std::string & client_id,
			const std::string & slice_name );

		virtual void
		ent_load(
			const std::string & client_id,
			const oess_1::ent_id_t & ent_id,
			oess_1::io::ostream_t & ent_value );

		virtual void
		ent_update(
			const std::string & client_id,
			const oess_1::ent_id_t & ent_id,
			oess_1::io::istream_t & ent_value,
			oess_1::uint_t ent_size );

		virtual oess_1::ent_id_t
		ent_find_next(
			const std::string & client_id,
			const std::string & slice_name,
			const oess_1::ent_id_t & ent_id );

		virtual void
		transaction_begin(
			const std::string & client_id );

		virtual void
		transaction_commit(
			const std::string & client_id );

		virtual void
		transaction_rollback(
			const std::string & client_id );
	//! \}

	private :
		//! Физическое хранилище БД.
		/*!
			Существует только, если БД открыта.
		*/
		auto_ptr_3::obj_ptr_t< oess_1::db::storage::stream_storage_t >
				m_storage;

		//! Флаги для метода %check_for_client().
		enum
		{
			//! Проверка только открытия БД и подключения клиента.
			connection_only = 0,
			//! Клиент должен быть владельцем транзакции.
			must_be_transaction_owner = 1
		};

		//! true, если БД открыта.
		bool	m_is_open;

		//! Существует только если БД открыта.
		std::auto_ptr< client_map_t >	m_client_map_ptr;

		//! Описание владельца транзакции.
		/*! \since v.1.1.0 */
		trans_owner_info_t	m_transaction_owner;

		//! Счетчик начатых транзакций.
		/*! \since v.1.1.0

			Увеличивается в sys_trans_start() и уменьшается
			в sys_trans_finish(). */
		unsigned int	m_trans_deep;

		//! Начать системную транзакцию в хранилище.
		/*! \since v.1.1.0
			Начинает системную транзакцию, если
			m_trans_deep == 0. Затем увеличивает m_trans_deep. */
		void
		sys_trans_start();

		//! Завершить системную транзакцию.
		/*!
			\since v.1.1.0

			Уменьшает m_trans_deep и завершает системную
			транзакцию только если m_trans_deep == 0.
		*/
		void
		sys_trans_finish();

		//! Попытаться осторожно откатить системную транзакцию.
		/*!
			\since v.1.4.0

			Если m_trans_deep отличен от нуля, то уменьшает m_trans_deep.
			Если m_trans_deep после этого обнуляется, то откатывает
			транзакцию в хранилище.

			Данный метод предназначен для отката системной транзакции,
			в которой было поймано какое-нибудь исключение. Поэтом
			исключения из careful_sys_trans_rollback() не выпускаются.
		*/
		void
		careful_sys_trans_rollback();

		//! Очистка всех ресурсов без выпуска исключений наружу.
		void
		cleanup();

		//! Порождает исключение, если БД не открыта.
		void
		must_be_open() const;

		//! Проверить возможность клиента выполнять
		//! операции над БД.
		/*! \sice v.1.1.0
			Вызывает must_be_open(), затем проверяет
			подключен ли клиент к БД.

			Если установлен флаг must_be_transaction_owner,
			то проверяется, является ли клиент владельцем
			транзакции.

			\throw oess_1::ex_t если клиент не может работать
			с БД (БД не открыта, клиент не подключен, ...). */
		void
		check_for_client(
			//! Идентификатор проверяемого клиента.
			const std::string & client_id,
			//! Битовые флаги, определяющие набор
			//! выполняемых действий.
			unsigned int actions ) const;

		//! Найти раздел, в котором нужно искать следующую сущность.
		/*!
			Если ищется первая сущность, то должен быть определен
			параметр \a slice_name. Если первая сущность уже была
			найдена, то должен быть передан корректный \a ent_id --
			в этом случае раздел берется по идентификатору
			ent_id_t::m_slice_id.
		*/
		oess_1::db::storage::entity_stream_t &
		slice_for_entity_search(
			const std::string & slice_name,
			const oess_1::ent_id_t & ent_id );
};

} /* namespace db_content */

} /* namespace impl */

} /* namespace db */

} /* namespace oess_1 */

#endif
