/*

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.

*/

/*!
	\since v.1.4.0
	\file
	\brief Интерфейс хранилища цепочек.
*/

#if !defined( _OESS_1__DB__STORAGE__CHAIN_STORAGE_HPP_ )
#define _OESS_1__DB__STORAGE__CHAIN_STORAGE_HPP_

#include <memory>

#include <cpp_util_2/h/nocopy.hpp>

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

#include <oess_1/io/h/stream.hpp>
#include <oess_1/io/h/mem_buf.hpp>

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

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

#include <oess_1/db/storage/h/chain_id.hpp>
#include <oess_1/db/storage/h/config.hpp>

namespace oess_1 {

namespace db {

namespace storage {

//
// no_chain_length_limit
//

/*!
	\since v.1.4.0
	\brief Константа, которая указывает на отсутствие ограничений на
		длину извлекаемой цепочки.

	Предназначена для использования в качестве параметра \a max_length
	метода chain_storage_t::chain_load().
*/
const oess_1::uint_t no_chain_length_limit =
		static_cast< oess_1::uint_t >( -1 );

//
// chain_storage_t
//

/*!
	\since v.1.4.0
	\brief Интерфейс для работы с уже открытым хранилищем цепочек.

	Объект, реализующий этот интерфейс необходимо получать с помощью
	функций create_chain_storage() и open_chain_storage().

	Предполагается, что деструктор будет закрывать БД. Поэтому, чтобы
	завершить работу с БД нужно просто уничтожить объект chain_storage_t.
*/
class OESS_1__DB__TYPE	chain_storage_t
	:	private cpp_util_2::nocopy_t
	{
	public :
		virtual ~chain_storage_t();

	//! \name Методы для работы с транзакциями.
	//! \{
		//! Начать транзакцию.
		/*!
			\throw oess_1::logic_ex_t если транзакция уже начата или
				хранилище открыто в режиме read-only.
		*/
		virtual void
		trx_start() = 0;

		//! Зафиксировать текущую транзакцию.
		/*!
			\throw oess_1::logic_ex_t если нет текущей транзакции.
		*/
		virtual void
		trx_commit() = 0;

		//! Откатить текущую транзакцию.
		/*!
			\throw oess_1::logic_ex_t если нет текущей транзакции.
		*/
		virtual void
		trx_rollback() = 0;
	//! \}

	//! \name Методы для работы с цепочками.
	//! \{
		//! Создать цепочку.
		/*!
			\return идентификатор созданной цепочки.
		*/
		virtual chain_id_t
		chain_create(
			//! Отсюда нужно брать значение сохраняемого объекта.
			oess_1::io::istream_t & from,
			//! Длина сохраняемого в цепочке объекта.
			oess_1::uint_t length ) = 0;

		//! Уничтожить цепочку.
		virtual void
		chain_destroy(
			//! Идентификатор уничтожаемой цепочки.
			const chain_id_t & chain ) = 0;

		//! Загрузить цепочку в память.
		/*!
			Если цепочка короче \a max_length, то в \a to помещается
			все значение цепочки. В противном случае в \a to загружается
			max_length байт.
		*/
		virtual void
		chain_load(
			//! Идентификатор загружаемой цепочки.
			const chain_id_t & chain,
			//! Приемник загружаемого значения.
			oess_1::io::ostream_t & to,
			//! Максимальный объем загружаемого значения.
			oess_1::uint_t max_length ) = 0;

		//! Перезаписать значение цепочки.
		/*!
			Если новое значение цепоки длинее/короче старого значения,
			то цепочка в хранилище автоматически будет расширена/усечена.
		*/
		virtual void
		chain_update(
			//! Идентификатор изменяемой цепочки.
			const chain_id_t & chain,
			//! Отсюда нужно брать значение объекта.
			oess_1::io::istream_t & from,
			//! Новая длина сохраняемого в цепочке объекта.
			oess_1::uint_t length ) = 0;
	//! \}
	};

//! \name Средства для манипуляции хранилищем цепочек.
//! \{

//
// create_chain_storage
//

/*!
	\since v.1.4.0
	\brief Создание нового хранилища.

	Создает необходимые файлы хранилища. Если эти файлы уже существовали,
	то они уничтожаются.

	В новое хранилище сразу записывается первая цепочка. Предполагается,
	что в этой цепочке будет находиться описатель хранилища. Далее идентификатор
	этой цепочке будет возвращаться при каждом открытии хранилища.

	Возвращается указатель на штатную реализацию интерфейса
	chain_storage_t. Созданная БД является открытой и с ней можно
	осуществлять работу.
*/
OESS_1__DB__FUNC(std::auto_ptr< chain_storage_t >)
create_chain_storage(
	//! Физическое имя создаваемой БД.
	const std::string & physic_name,
	//! Конфигурация для новой БД.
	const config_t & config,
	//! Отсюда нужно брать значение объекта для первой цепочки.
	oess_1::io::istream_t & from,
	//! Длина объекта, который должен быть сохранен в
	//! первой цепочке БД.
	oess_1::uint_t length,
	//! Приемник идентификатора первой цепочки хранилища.
	chain_id_t & first_chain );

//
// open_chain_storage
//

/*!
	\since v.1.4.0
	\brief Открытие существующего хранилища.

	Открывает указанное хранилище и считывает оттуда объект,
	который был записан первым при создании хранилища.

	Возвращается указатель на штатную реализацию интерфейса
	chain_storage_t. Созданная БД является открытой и с ней можно
	осуществлять работу.

	\par Обработка конфигурационной информации.
		Конфигурационная информация считывается из конфигурационного
		файла хранилища. Неизменяемые параметры конфигурации берутся
		из прочитанного конфигурационного файла. Остальные параметры
		обрабатываются следующим образом: если значение параметра в
		\a config отличается от значения этого параметра по-умолчанию,
		то используется значением из \a config. В противном случае
		используется значение из конфигурационного файла.
*/
OESS_1__DB__FUNC(std::auto_ptr< chain_storage_t >)
open_chain_storage(
	//! Физическое имя открываемой БД.
	const std::string & physic_name,
	//! Конфигурация для БД.
	const config_t & config,
	//! true, если БД открывается в режиме read-only.
	bool is_read_only,
	//! true, если разрешается автоматическое восстановление БД после сбоя.
	bool is_auto_repair_enabled,
	//! Приемник значение объекта из первой цепочки в БД.
	oess_1::io::ostream_t & to,
	//! Ограничение на длину объекта, который считывается из
	//! первой цепочки в БД.
	oess_1::uint_t length,
	//! Приемник идентификатора первой цепочки хранилища.
	chain_id_t & first_chain );

//
// is_chain_storage_exists
//

/*!
	\since v.1.4.0
	\brief Проверка существования хранилища.

	Хранилище считается существующим, если:
	- есть хотя бы один trace-файл, и
	- есть основной файл хранилища или "грязный" основной файл хранилища.
*/
OESS_1__DB__FUNC(bool)
is_chain_storage_exists(
	//! Физическое имя БД, существование которой будет проверяться.
	const std::string & physic_name );

//
// destroy_chain_storage
//

/*!
	\since v.1.4.0
	\brief Уничтожение хранилища.

	Уничтожаются все файлы, которые имеют отношение к указанному
	хранилищу.
*/
OESS_1__DB__FUNC(void)
destroy_chain_storage(
	//! Физическое имя уничтожаемой БД.
	const std::string & physic_name );

//! \}

/*! \name Средства для упрощения сохранения в хранилище цепочек
	сериализуемых объектов.
	\{ */

//! Создание цепочки для объекта.
template< class T >
chain_id_t
create_in_storage(
	chain_storage_t & storage, const T & what )
	{
		oess_1::io::mem_buf_t stream;
		oess_1::stdsn::oent_std_t oent( stream );
		oent << what;
		stream.set_pos( 0 );

		return storage.chain_create( stream, stream.size() );
	}

//! Загрузка объекта из цепочки.
template< class T >
void
load_from_storage(
	chain_storage_t & storage,
	const chain_id_t & chain,
	T & what,
	oess_1::uint_t length = no_chain_length_limit )
	{
		oess_1::io::mem_buf_t stream;
		storage.chain_load( chain, stream, length );

		stream.set_pos( 0 );
		oess_1::stdsn::ient_std_t ient( stream );
		ient >> what;
	}

//! Обновление значения объекта в цепочке.
template< class T >
void
update_in_storage(
	chain_storage_t & storage,
	const chain_id_t & chain,
	const T & what )
	{
		oess_1::io::mem_buf_t stream;
		oess_1::stdsn::oent_std_t oent( stream );
		oent << what;
		stream.set_pos( 0 );

		return storage.chain_update( chain, stream, stream.size() );
	}

/*! \} */

} /* namespace storage */

} /* namespace db */

} /* namespace oess_1 */

#endif

