/*

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
	\since v.1.4.0
	\brief Общие типы и инструменты для восстановочной и
		трасировочной информации.
*/
#if !defined( _OESS_1__DB__STORAGE__TRX__TYPES_HPP_ )
#define _OESS_1__DB__STORAGE__TRX__TYPES_HPP_

#include <list>
#include <set>
#include <memory>

#include <cpp_util_2/h/nocopy.hpp>

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

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

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

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

namespace oess_1 {

namespace db {

namespace storage {

namespace trx {

//
// delta_info_t
//

/*!
	\since v.1.4.0
	\brief Один элемент описания изменения страницы.

	Страница разбивается на блоки одинакового размера. Затем
	блоки старого и нового значения страниц поочередно сравниваются.
	Последовательно идущие несовпадающие блоки объединяются
	в один объект delta_info_t.

	Т.о. delta_info_t может содержать несколько блоков. Расположение
	первого блока на странице задается его индексом (не смещением).
	Смещение блока вычисляется умножением индекса на размер блока.
*/
class	OESS_1__DB__TYPE	delta_info_t
	:	public oess_1::stdsn::serializable_t
	{
	OESS_SERIALIZER_EX( delta_info_t, OESS_1__DB__TYPE )
	typedef oess_1::stdsn::serializable_t	base_type_t;
	public :
		//! Конструктор по-умолчанию.
		delta_info_t();
		//! Инициализирующий конструктор.
		delta_info_t(
			//! Индекс первого блока фрагмента на странице.
			oess_1::uint_t index,
			//! Значение несовпадающего фрагмента.
			const std::string & value );
		virtual ~delta_info_t();

		//! Индекс первого блока фрагмента на странице.
		oess_1::uint_t
		index() const;

		//! Значение несовпадающего фрагмента.
		const std::string &
		value() const;

	private :
		//! Индекс первого блока фрагмента на странице.
		oess_1::uint_t	m_index;

		//! Значение несовпадающего фрагмента.
		std::string	m_value;
	};

//
// delta_info_shptr_t
//

/*!
	\since v.1.4.0
	\brief Умный указатель на delta_info_t.
*/
class	OESS_1__DB__TYPE	delta_info_shptr_t
	:	public oess_1::stdsn::shptr_t
	{
	OESS_SERIALIZER_EX( delta_info_shptr_t, OESS_1__DB__TYPE )
	OESS_1_SHPTR_IFACE( delta_info_shptr_t,
			oess_1::db::storage::trx::delta_info_t,
			oess_1::stdsn::shptr_t )
	};

//
// delta_info_list_t
//

/*!
	\since v.1.4.0
	\brief Тип списка элементов описания изменений страницы.
*/
typedef std::list< delta_info_shptr_t > delta_info_list_t;

//
// page_info_t
//

/*!
	\since v.1.4.0
	\brief Значение одной страницы основного файла.

	Содержит все изменения одной страницы trace-файла.
*/
class	OESS_1__DB__TYPE	page_info_t
	:	public oess_1::stdsn::serializable_t
	{
	OESS_SERIALIZER_EX( page_info_t, OESS_1__DB__TYPE )

	//! Псевдоним для базового типа.
	typedef oess_1::stdsn::serializable_t	base_type_t;

	public :
		//! Конструктор по-умолчанию.
		page_info_t();
		//! Полностью инициализирующий конструктор.
		page_info_t(
			//! Порядковый номер страницы в хранилище.
			oess_1::uint_t ordinal );
		virtual ~page_info_t();

		//! Получить порядковый номер страницы в хранилище.
		oess_1::uint_t
		ordinal() const;

		//! Добавить описание еще одного фрагмента.
		void
		add( const delta_info_shptr_t & delta );

		//! Получить все фрагменты.
		const delta_info_list_t &
		deltas() const;

		//! Получить суммарную длину delta на странице.
		oess_1::uint_t
		summary_delta_length() const;

	private :
		//! Порядковый номер страницы в хранилище.
		oess_1::uint_t	m_ordinal;

		//! Все несовпадающие фрагменты этой страницы.
		delta_info_list_t	m_deltas;
	};

//
// page_info_list_t
//

/*!
	\since v.1.4.0
	\brief Список значений страниц основного файла.
*/
typedef std::list< page_info_t >	page_info_list_t;

//
// trace_stream_item_t
//

/*!
	\since v.1.4.0
	\brief Описание транзакции для trace-потока.
*/
class	OESS_1__DB__TYPE	trace_stream_item_t
	:	public oess_1::stdsn::serializable_t
	{
	OESS_SERIALIZER_EX( trace_stream_item_t, OESS_1__DB__TYPE )
	typedef oess_1::stdsn::serializable_t base_type_t;

	public :
		//! Конструктор по-умолчанию.
		trace_stream_item_t();
		//! Частично инициализирующий конструктор.
		/*!
			Созданный с помощью этого конструктора объект должен
			быть затем передан в функцию make_item().
		*/
		trace_stream_item_t(
			//! Длина основного файла БД в байтах.
			oess_1::uint_t main_file_length );
		virtual ~trace_stream_item_t();

		//! Длина основного файла в байтах.
		oess_1::uint_t
		main_file_length() const;

		//! Добавить описание очередной страницы транзакции.
		void
		add(
			const page_info_t & page );

		//! Получить описания всех страниц транзакции.
		const page_info_list_t &
		pages() const;

		//! Получить суммарную длину всех delta на всех страницах.
		oess_1::uint_t
		summary_length() const;

	private :
		//! Длина основного файла БД в байтах.
		oess_1::uint_t	m_main_file_length;

		//! Описания всех страниц транзакции.
		page_info_list_t	m_pages;
	};

//
// make_item.
//

/*!
	\since v.1.4.0
	\brief Выполнить формирование описания транзакции для
		записи в trace-stream.

	Анализирует все страницы в \a storage. Формирует элемент
	готовый для записи в trace-stream.
*/
OESS_1__DB__FUNC(void)
make_item(
	//! Все грязные страницы транзакции.
	dirty_page_storage_t & storage,
	//! Конфигурация БД.
	const config_t & config,
	//! Приемник для описания транзакции.
	//! Изменяется по ходу выполнения операции. Поэтому может
	//! содержать некорректное значение при возникновении
	//! исключений.
	trace_stream_item_t & receiver );

//
// image_header_t
//

/*!
	\since v.1.4.0
	\brief Заголовок образа транзакции в trace-файле.
*/
class	OESS_1__DB__TYPE	image_header_t
	:	public oess_1::stdsn::serializable_t
	{
		OESS_SERIALIZER_EX( image_header_t, OESS_1__DB__TYPE )
		typedef oess_1::stdsn::serializable_t base_type_t;

	public :
		//! Тип для хранения режима компрессии данных.
		typedef trace_compression_mode_t	compression_mode_t;

		//! Обнуляет все значения.
		image_header_t();
		//! Полностью инициализирующий конструктор.
		image_header_t(
			//! Режим компрессии данных.
			const compression_mode_t & compression,
			//! Размер данных до компрессии.
			oess_1::uint_t uncompressed_size,
			//! Размер данных после компрессии.
			oess_1::uint_t compressed_size );
		virtual ~image_header_t();

		//! Режим компрессии данных.
		const compression_mode_t &
		compression_mode() const;

		//! Размер данных до компрессии.
		oess_1::uint_t
		uncompressed_size() const;

		//! Размер данных после компрессии.
		oess_1::uint_t
		compressed_size() const;

	private :
		//! Режим компрессии данных.
		compression_mode_t m_compression_mode;

		//! Размер данных до компрессии.
		oess_1::uint_t	m_uncompressed_size;

		//! Размер данных после компрессии.
		oess_1::uint_t	m_compressed_size;
	};

//
// trace_file_number_t
//

/*!
	\since v.1.4.0
	\brief Тип номера одного trace-файла.
*/
typedef oess_1::uint_t trace_file_number_t;

//
// trace_file_number_set_t
//

/*!
	\since v.1.4.0
	\brief Тип множества номеров trace-файлов.
*/
typedef std::set< trace_file_number_t >	trace_file_number_set_t;

//
// snapshot_collection_t
//

/*!
	\since v.1.4.0
	\brief Интерфейс класса для работы с коллекций snapshot-ов БД.
*/
class	OESS_1__DB__TYPE	snapshot_collection_t
	:	private cpp_util_2::nocopy_t
	{
	public :
		virtual ~snapshot_collection_t();

		//! Получить общее количество существующих snapshot-ов.
		virtual unsigned int
		size() const = 0;

		//! Получить список номеров snapshot-ов, которые уже не актуальны.
		virtual trace_file_number_set_t
		non_actual() const = 0;

		//! Получить номер актуального snapshot-а.
		/*!
			\throw oess_1::ex_t, если нет актуального snapshot-а.
		*/
		virtual trace_file_number_t
		actual() const = 0;

		//! Проверка на существование актуального snapshot-а.
		/*!
			\return true, если существует актуальный snapshot.
		*/
		virtual bool
		has_actual() const = 0;

		//! Добавить указанный временный файл в составл snapshot-ов.
		/*!
			\attention Стандартная реализация интерфейса snapshot_collection_t
			будет преобразовывать временный файл в snapshot путем простого
			переименования. Это означает, что временный файл должен располагаться
			в том же каталоге, что и остальные snapshot-файлы (т.е., для
			версии 1.4.0, в том же каталоге, что и БД).
		*/
		virtual void
		include(
			//! Имя временного файла, который уже содержит snapshot.
			const std::string & file_name,
			//! Максимальный номер trace-файла, который использовался для
			//! получения данного snapshot-а.
			trace_file_number_t last_used_trace ) = 0;

		//! Скопировать содержимое актуального snapshot-а в указанный
		//! временный файл.
		/*!
			\note В отличии от метода include() стандартная реализация
			копирует содержимое snapshot-а в указанный файл, поэтому он не обязан
			находиться в том же каталоге, что и остальные snapshot-файлы.

			\throw oess_1::ex_t, если нет актуального snapshot-а.
		*/
		virtual void
		get_actual(
			//! Имя временного файла, который нужно перезаписать.
			const std::string & file_name ) const = 0;

		//! Удалить все snapshot-ы для БД.
		virtual void
		destroy() = 0;
	};

//
// committer_t
//

/*!
	\since v.1.4.0
	\brief Тип объекта, который получает восстановленные из
		trace-stream значения страниц основного файла для
		сохранения их в новый основной файл.
*/
class	OESS_1__DB__TYPE	committer_t
	:	private cpp_util_2::nocopy_t
	{
	public :
		virtual ~committer_t();

		/*!
			Вызывается объектом trace_stream_t перед методом initialize()
			для того, чтобы определить точное множество trace-файлов для
			обратного воспроизведения. Если для БД поддерживались snapshot-ы,
			то часть trace-файлов можно будет проигнорировать т.к. их
			содержимое уже есть в snapshot-е. Но trace_stream_t не знает
			про существование snapshot-ов и о том, нужны ли они данному
			committer-у. Поэтому выбор конкретного подмножества trace-файлов
			для воспроизведения перекладывается на committer. Для этого
			trace_stream_t обращается к методу reduce_traces_set() и
			передает все множество найденых trace-файлов. Если committer может
			уменьшить объем этого множества, то он должен поместить новое
			множество в \a actual_traces. Если уменьшить множество
			trace-файлов не удается, то в \a actual_traces нужно скопировать
			исходное множество trace-файлов.
		*/
		virtual void
		reduce_traces_set(
			//! Полное множество существующих trace-файлов.
			const trace_file_number_set_t & all_traces,
			//! Приемник актуального множества trace-файлов, которое
			//! должно быть использовано для восстановления БД.
			trace_file_number_set_t & actual_traces ) = 0;

		/*!
			Вызывается объектом trace_stream_t перед началом
			восстановления БД для того, чтобы сообщить коммитеру о
			самых важных параметрах восстанавливаемого файла.
		*/
		virtual void
		initialize(
			//! Общее количество страниц в файле.
			oess_1::uint_t total_page_count,
			//! Размер одной страницы в файле.
			oess_1::uint_t page_size,
			//! Размер одного блока на странице.
			//! Гарантируется, что page_size будет кратен block_size.
			oess_1::uint_t block_size ) = 0;

		/*!
			Вызывается объектом trace_stream_t после завершения
			успешного восстановления БД.
		*/
		virtual void
		finalize() = 0;

		/*!
			Вызвается объектом trace_stream_t при необходимости
			сохранения значения фрагмента страницы в восстанавливаемом
			основном файле.

			Размер блока задается в методе initialize().

			\note Не гарантируется, что при воспроизведении trace-файла
			страницы будут отдаваться committer-у в порядке возрастания
			их номеров. Например, вместо 0-й страницы committer-у может
			быть передана страница 12034. Committer сам должен определить,
			что такой страницы еще нет и что нужно заполнить предшествующие
			12033 страницы нулевыми значениями.
			Так же не гарантируется, что фрагменты будут восстанавливаться
			в порядке возрастания их индексов на странице.
		*/
		virtual void
		store_block(
			//! Порядковый номер страницы.
			oess_1::uint_t ordinal,
			//! Порядковый номер (индекс) блока на странице.
			oess_1::uint_t block_index,
			//! Значение блока.
			const std::string & block_value ) = 0;

		/*!
			Должен возвращать true, если ситуация, когда в актуальном
			можестве trace-файлов (возвращаемом методом reduce_traces_set())
			допускается отсутствие полной информации о содержимом БД.
			Например, если для восстановления БД кроме trace-файлов используются
			еще и snapshot-ы, то части trace-файлов (которые были сохранены
			в snapshot-е) может просто не быть. В этом случае процедура
			обратного восспроизведения определит, что восстановочной
			информации недостаточно (т.к. она не знает про существование
			snapshot-ов). trace_stream_t вызовет данный метод и, если метод,
			возвратит false, то trace_stream_t породит исключение о
			недостаточности восстановочной информации. Если же данный метод
			возвратит true, то процедура обратного воспроизведения будет
			считаться успешно завершенной.
		*/
		virtual bool
		is_insufficient_trace_info_allowed() const = 0;
	};

//
// trace_stream_t
//

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

	Методы append и reverse_replay можно вызывать только после
	успешного обращения к методам open или create.

	Методы is_exists и destroy можно вызывать вне зависимости от
	того, открыт ли trace-stream или нет.
*/
class	OESS_1__DB__TYPE	trace_stream_t
	:	private	cpp_util_2::nocopy_t
	{
	public :
		virtual ~trace_stream_t();

		/*!
			Открыть trace-stream.

			Если нет ни одного трассировочного файла, то порождается
			исключение.
		*/
		virtual void
		open(
			//! Физическое имя БД.
			const std::string & physic_name,
			//! Конфигурация БД.
			const config_t & config,
			//! Открывать в режиме read-only?
			bool is_read_only ) = 0;

		/*!
			Создать trace-stream.

			Порождает исключение, если уже есть хотя бы один трассировочный
			файл. В этом случае сначала нужно вызвать метод destroy.
		*/
		virtual void
		create(
			//! Физическое имя создаваемой БД.
			const std::string & physic_name,
			//! Конфигурация создаваемой БД.
			const config_t & config ) = 0;

		/*!
			Удалить trace-stream.

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

		/*!
			Проверить существование trace-stream.

			\return true, если существует хотя бы один trace-файл.
		*/
		virtual bool
		is_exists(
			//! Физическое имя БД для которой проверяется существование
			//! trace-stream.
			const std::string & physic_name ) = 0;

		/*!
			Дописать в trace-stream описание очередной транзакции.

			Начинает дописывать транзакцию в последний из существующих
			trace-файлов и автоматически создает столько trace-файлов,
			сколько нужно для сохранения транзакции.
		*/
		virtual void
		append(
			const trace_stream_item_t & item ) = 0;

		/*!
			Выполнить обратное воспроизведение trace-файлов.

			Каждая сформированная страница основного файла
			передается на фиксацию объекту \a committer-у.

			Обратный просмотр завершается когда обнаруживается,
			что все страницы основного файла были перезаписаны и
			в оставшихся trace-файлах находятся уже устаревшие
			значения.

			В \a used_files возвращаются номера trace-файлов,
			значения которых были использованны для восстановления
			значений БД. В \a unused_files возращаются имена
			trace-файлов, значения которых полностью устарели.
		*/
		virtual void
		reverse_replay(
			committer_t & committer,
			trace_file_number_set_t * used_files = 0,
			trace_file_number_set_t * unused_files = 0,
			//! Необязательный поток для выдачи информации о
			//! ходе обратного воспроизведения trace-файлов.
			std::ostream * verbose_stream = 0 ) = 0;

		/*!
			Закрыть trace-stream.
		*/
		virtual void
		close() = 0;
	};

//
// create_std_trace_stream
//

/*!
	\since v.1.4.0
	\brief Создать штатную реализацию интерфейса %trace_stream_t.

	Создается реализация, необходимая для указанного режима сохранения
	восстановочной информации.

	\return Указатель на динамически созданный объект, ответственность
		за уничтожение которого лежит на вызывающей стороне. 
*/
OESS_1__DB__FUNC(std::auto_ptr< trace_stream_t >)
create_std_trace_stream(
	//! Желаемый режим сохранения восстановочной информации.
	trace_mode_t trace_mode );

//
// create_std_file_committer
//

/*!
	\since v.1.4.0
	\breif Создать штатную реализацию интерфейса %committer_t.

	\return Указатель на динамически созданный объект, ответственность
		за уничтожение которого лежит на вызывающей стороне.

	Создается реализация, необходимая для указанного режима сохранения
	восстановочной информации.

	\par Для режима oess_1::db::storage::trace_all
	Штатная реализация %committer_t создает файл с именем
	<physic_name>.replay в который записывает содержимое восстановленой
	БД. Если восстановление завершается успешно (у коммитера вызывается
	метод finalize()), то переименовывает этот файл в
	<physic_name>.oess.dat.

	\par Для режима oess_1::db::storage::trace_last
	Штатная реализация %committer_t открывает "грязный" файл хранилища.
	В методе initialize "грязный" файл, при необходимости, доводиться
	до нужного размера. В методе finalize (т.е. если воспроизведение
	trace-файла прошло успешно), переименовывает "грязный" файл в
	основной файл БД.

	\par Для режима oess_1::db::storage::trace_no
	В этом режиме восстановление БД невозможно, поэтому вместо
	возвращения объекта-committer-а порождается исключение.
*/
OESS_1__DB__FUNC(std::auto_ptr< committer_t >)
create_std_file_committer(
	//! Имя физической БД, для которой выполняется восстановление.
	const std::string & physic_name,
	//! Режим сохранения восстановочной информации для БД.
	trace_mode_t trace_mode );

//
// create_std_snapshot_collection
//

/*!
	\since v.1.4.0
	\brief Получить стандартную реализацию %std_snapshot_collection_t.
*/
OESS_1__DB__FUNC(std::auto_ptr< snapshot_collection_t >)
create_std_snapshot_collection(
	//! Физическое имя БД для которой нужно искать snapshot-ы.
	const std::string & physic_name );

//
// trace_file_extension
//

/*!
	\since v.1.4.0
	\brief Получить стандартное расширение, которое используется
		для trace-файлов.

	\return значение ".trace".
*/
OESS_1__DB__FUNC(std::string)
trace_file_extension();

//
// create_trace_file_name
//

/*!
	\since v.1.4.0
	\brief Создать имя указанного trace-файла для указанной БД.
*/
OESS_1__DB__FUNC(std::string)
create_trace_file_name(
	const std::string & physic_name,
	trace_file_number_t trace_file_number );

//
// snapshot_file_extension
//

/*!
	\since v.1.4.0
	\brief Получить стандартное расширение, которое используется
		для snapshot-файлов.

	\return значение ".snapshot".
*/
OESS_1__DB__FUNC(std::string)
snapshot_file_extension();

//
// create_snapshot_file_name
//

/*!
	\since v.1.4.0
	\brief Создать имя указанного snapshot-файла для указанной БД.
*/
OESS_1__DB__FUNC(std::string)
create_snapshot_file_name(
	const std::string & physic_name,
	trace_file_number_t snapshot_file_number );

} /* namespace trx */

} /* namespace storage */

} /* namespace db */

} /* namespace oess */

#endif

