/*

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__IMPL__HEADERS_HPP_ )
#define _OESS_1__DB__STORAGE__IMPL__HEADERS_HPP_

#include <set>
#include <vector>

#include <cpp_util_2/h/nocopy.hpp>

#include <smart_ref_3/h/smart_ref.hpp>

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

#include <oess_1/db/storage/impl/h/segment_header_item.hpp>

namespace oess_1 {

namespace db {

namespace storage {

namespace impl {

//
// common_header_t
//

/*!
	\since v.1.4.0
	\brief Тип общего заголовка всего файла.

	Для объекта поддерживается понятие "модифицированности".
	Объект считается модифицированным, если для него были вызваны
	методы: init_new(), remove_free_area(), return_free_area().
	Сделано это для того, чтобы просле проведения операций над
	заголовком можно было определить, следует ли записывать новое
	значение заголовка в файл.
*/
class	common_header_t
	{
	public :
		//! Инициализирующий конструктор.
		common_header_t(
			//! Параметры хранилища.
			const page_index_calculator_t & calculator );

		//! Получить идентфикатор области, в которой есть
		//! свободные блоки.
		/*!
			\throw oess_1::logic_ex_t, если больше нет областей
				со свободными блоками.
		*/
		area_ordinal_t
		get_free_area() const;

		//! Изъять указаный сегмент из списка областей, в которых
		//! есть свободные блоки.
		/*!
			Устанавливатся признак модификации объекта.
		*/
		void
		remove_free_area(
			const area_ordinal_t & area );

		//! Возвратить указанный сегмент в список областей, которые
		//! имеют свободные блоки.
		/*!
			Устанавливатся признак модификации объекта.
		*/
		void
		return_free_area(
			const area_ordinal_t & area );

		//! Проинициализировать заголовок для нового файла хранилища.
		/*!
			Устанавливатся признак модификации объекта.
		*/
		void
		init_new();

		//! Прочитать значение заголовка из указанного потока.
		/*!
			Признак модификации объекта сбрасывается.
		*/
		oess_1::io::ibinstream_t &
		read_from(
			oess_1::io::ibinstream_t & from );

		//! Записать значение заголовка в указанный поток.
		/*!
			Признак модификации объекта не изменяется.
		*/
		oess_1::io::obinstream_t &
		write_to(
			oess_1::io::obinstream_t & to ) const;

		//! Был ли объект модифицирован?
		bool
		is_changed() const;

		//! Сбросить флаг модифицированности объекта.
		void
		force_not_changed();

		//! Есть ли еще свободные области?
		/*!
			\return true, если есть.
		*/
		bool
		has_free_areas() const;

	private :
		//! Параметры хранилища.
		page_index_calculator_t	m_calculator;

		//! Тип множества номеров областей со свободными блоками.
		typedef std::set< area_ordinal_t > area_ordinal_set_t;

		//! Множество номеров областей во свободными блоками.
		area_ordinal_set_t	m_free_areas;

		//! true, если объект был модифицирован.
		bool	m_is_changed;

		//! Идентификатор первой области, в которой есть свободные блоки.
		/*!
			Значение определено только, если есть свободные области
			в m_free_area.

			\note Для оптимизации метода get_free_area.
		*/
		area_ordinal_t	m_first_free_area;
	};

//! Сохранить в указанный поток.
inline oess_1::io::obinstream_t &
operator<<(
	oess_1::io::obinstream_t & to,
	const common_header_t & what )
	{
		return what.write_to( to );
	}

//! Прочитать из указанного потока.
inline oess_1::io::ibinstream_t &
operator>>(
	oess_1::io::ibinstream_t & from,
	common_header_t & what )
	{
		return what.read_from( from );
	}

//
// ref_common_header_t
//

SMART_REF_3_DECL_REF( common_header_t, class )

//
// area_header_t
//

/*!
	\since v.1.4.0
	\brief Тип общего заголовка всего файла.

	Для объекта поддерживается понятие "модифицированности".
	Объект считается модифицированным, если для него были вызваны
	методы: init_new(), remove_free_segment(), return_free_segment().
	Сделано это для того, чтобы просле проведения операций над
	заголовком можно было определить, следует ли записывать новое
	значение заголовка в файл.
*/
class	area_header_t
	{
	public :
		/*!
			\since v.1.4.0
			\brief Вспомогательный буфер для выполнения операций ввода-вывода.

			Профилирование показало, что операции чтения/записи заголовка
			выполняются гораздо эффективнее, если работать с буфером в ОП.
			Но динамически создавать буфер для каждой операции очень дорого.
			Так же следует учитывать, что операция чтения/записи заголовка
			может выполняться на контексте одной нити для одного хранилища
			только одна. Поэтому можно заранее создать для хранилища
			объект-буфер и использовать его для выполнения всех операций
			ввода/вывода заголовков областей этого хранилища.

			Класс io_buffer_t предназначен для того, чтобы объект,
			управляющий заголовками сегмента, создал его однократно, а затем
			передавал во все операции чтения/записи.
		*/
		class	io_buffer_t
			:	private cpp_util_2::nocopy_t
			{
				public :
					//! Инициализирующий конструктор.
					io_buffer_t(
						const page_index_calculator_t & calculator );
					~io_buffer_t();

					//! Получить объем буфера.
					inline segment_ordinal_t
					size() const
						{
							return m_buffer.size();
						}

					//! Получить доступ к значению буфера.
					inline segment_ordinal_t *
					get()
						{
							return &m_buffer[ 0 ];
						}

				private :
					//! Значение буфера.
					std::vector< segment_ordinal_t >	m_buffer;
			};
		//! Инициализирующий конструктор.
		area_header_t(
			//! Параметры хранилища.
			const page_index_calculator_t & calculator,
			//! Номер данной области.
			const area_ordinal_t & ordinal );

		//! Порядковый номер этой области.
		const area_ordinal_t &
		area_ordinal() const;

		//! Получить идентфикатор сегмента, в котором есть
		//! свободные блоки.
		/*!
			\throw oess_1::logic_ex_t, если больше нет сегментов
				со свободными блоками.
		*/
		segment_ordinal_t
		get_free_segment() const;

		//! Изъять указаный сегмент из списка сегментов, в которых
		//! есть свободные блоки.
		/*!
			Устанавливатся признак модификации объекта.
		*/
		void
		remove_free_segment(
			const segment_ordinal_t & segment );

		//! Возвратить указанный сегмент в список сегментов, которые
		//! имеют свободные блоки.
		/*!
			Устанавливатся признак модификации объекта.
		*/
		void
		return_free_segment(
			const segment_ordinal_t & segment );

		//! Проинициализировать заголовок для нового файла хранилища.
		/*!
			Устанавливатся признак модификации объекта.
		*/
		void
		init_new();

		//! Прочитать значение заголовка из указанного потока.
		/*!
			Признак модификации объекта сбрасывается.
		*/
		oess_1::io::ibinstream_t &
		read_from(
			oess_1::io::ibinstream_t & from,
			io_buffer_t & io_buffer );

		//! Записать значение заголовка в указанный поток.
		/*!
			Признак модификации объекта не изменяется.
		*/
		oess_1::io::obinstream_t &
		write_to(
			oess_1::io::obinstream_t & to,
			io_buffer_t & io_buffer ) const;

		//! Был ли объект модифицирован?
		bool
		is_changed() const;

		//! Сбросить флаг модифицированности объекта.
		void
		force_not_changed();

		//! Есть ли еще свободные сегменты?
		/*!
			\return true, если есть.
		*/
		bool
		has_free_segments() const;

	private :
		//! Параметры хранилища.
		page_index_calculator_t	m_calculator;

		//! Порядковый номер этой области.
		area_ordinal_t m_area_ordinal;

		//! Тип множества номеров сегментов со свободными блоками.
		typedef std::set< segment_ordinal_t > segment_ordinal_set_t;

		//! Множество номеров сегментов во свободными блоками.
		segment_ordinal_set_t	m_free_segments;

		//! true, если объект был модифицирован.
		bool	m_is_changed;

		//! Идентификатор первого сегмента, в котором есть свободные блоки.
		/*!
			Значение определено только, если есть свободные сегменты
			в m_free_segments.

			\note Для оптимизации метода get_free_segment.
		*/
		segment_ordinal_t	m_first_free_segment;
	};

//
// ref_area_header_t
//

SMART_REF_3_DECL_REF( area_header_t, class )

//
// segment_header_t
//

/*!
	\since v.1.4.0
	\brief Заголовок логического сегмента.

	Для объекта поддерживается понятие "модифицированности".
	Объект считается модифицированным, если для него были вызваны
	методы: init_new(), remove_free_segment(), return_free_segment().
	Сделано это для того, чтобы просле проведения операций над
	заголовком можно было определить, следует ли записывать новое
	значение заголовка в файл.
*/
class	segment_header_t
	{
	public :

		/*!
			\since v.1.4.0
			\brief Вспомогательный буфер для выполнения операций ввода-вывода.

			Профилирование показало, что операции чтения/записи заголовка
			выполняются гораздо эффективнее, если работать с буфером в ОП.
			Но динамически создавать буфер для каждой операции очень дорого.
			Так же следует учитывать, что операция чтения/записи заголовка
			может выполняться на контексте одной нити для одного хранилища
			только одна. Поэтому можно заранее создать для хранилища
			объект-буфер и использовать его для выполнения всех операций
			ввода/вывода заголовков сегментов этого хранилища.

			Класс io_buffer_t предназначен для того, чтобы объект,
			управляющий заголовками сегмента, создал его однократно, а затем
			передавал во все операции чтения/записи.
		*/
		class	io_buffer_t
			:	private cpp_util_2::nocopy_t
			{
				public :
					//! Инициализирующий конструктор.
					io_buffer_t(
						const page_index_calculator_t & calculator );
					~io_buffer_t();

					//! Получить объем буфера.
					inline oess_1::uint_t
					size() const
						{
							return m_buffer.size();
						}

					//! Получить доступ к значению буфера.
					inline oess_1::uint_t *
					get()
						{
							return &m_buffer[ 0 ];
						}

				private :
					//! Значение буфера.
					std::vector< oess_1::uint_t >	m_buffer;
			};

		//! Инициализирующий конструктор.
		segment_header_t(
			//! Параметры хранилища.
			const page_index_calculator_t & calculator,
			//! Порядковый номер области этого сегмента.
			const area_ordinal_t & area_ordinal,
			//! Порядковый номер этого сегмента.
			const segment_ordinal_t & ordinal );

		//! Порядковый номер области этого сегмента.
		const area_ordinal_t &
		area_ordinal() const;

		//! Порядковый номер этого сегмента.
		const segment_ordinal_t &
		segment_ordinal() const;

		//! Выделить блок из сегмента.
		/*!
			Устанавливатся признак модификации объекта.
		*/
		chain_id_t
		allocate_block();

		//! Сделать блок свободным.
		/*!
			Устанавливатся признак модификации объекта.
		*/
		void
		deallocate_block(
			const chain_id_t & block_id );

		//! Получить ссылку на следущий блок для указанного блока.
		/*!
			\throw oess_1::logic_ex_t, если \a block_id является
				свободным.
		*/
		chain_id_t
		next(
			const chain_id_t & block_id ) const;

		//! Установить ссылку на следующий блок для указанного блока.
		/*!
			Устанавливатся признак модификации объекта.

			\throw oess_1::logic_ex_t, если \a block_id является
				свободным.
		*/
		void
		change_next(
			//! Блок, для которого устанавливается значение ссылки.
			const chain_id_t & block_id,
			//! Следующий элемент цепочки.
			const chain_id_t & next );

		//! Проинициализировать заголовок для нового сегмента.
		/*!
			Устанавливатся признак модификации объекта.
		*/
		void
		init_new();

		//! Прочитать значение заголовка из указанного потока.
		/*!
			Признак модификации объекта сбрасывается.
		*/
		oess_1::io::ibinstream_t &
		read_from(
			oess_1::io::ibinstream_t & from,
			io_buffer_t & io_buffer );

		//! Записать значение заголовка в указанный поток.
		/*!
			Признак модификации объекта не изменяется.
		*/
		oess_1::io::obinstream_t &
		write_to(
			oess_1::io::obinstream_t & to,
			io_buffer_t & io_buffer ) const;

		//! Был ли объект модифицирован?
		bool
		is_changed() const;

		//! Сбросить флаг модифицированности объекта.
		void
		force_not_changed();

		//! Есть ли свободные блоки?
		/*!
			\return true, если есть.
		*/
		bool
		has_free_blocks() const;

	private :
		//! Параметры хранилища.
		page_index_calculator_t	m_calculator;

		//! Порядковый номер области этого сегмента.
		area_ordinal_t m_area_ordinal;

		//! Порядковый номер сегмента в файле.
		segment_ordinal_t	m_segment_ordinal;

		//! true, если объект был модифицирован.
		bool	m_is_changed;

		//! Общее количество блоков в сегменте, включая неиспользуемые.
		unsigned int m_total_block_count;

		//! Количество блоков, которые могут быть использованы.
		/*!
			Это количество блоков, которые не попадают на заголовок
			сегмента.
		*/
		unsigned int m_useful_block_count;

		//! Идентификатор первого блока данного сегмента.
		/*!
			Нужен для того, чтобы определить позицию описателя этого
			блока в m_items.
		*/
		chain_id_t	m_first_chain_id;

		//! Идентификатор последнего блока этого сегмента.
		chain_id_t	m_last_chain_id;

		//! Идентификатор первого свободного блока данного сегмента.
		/*!
			Содержит корректное значение только, если есть свободные блоки.
		*/
		chain_id_t	m_first_free_id;

		//! Количество свободных блоков.
		unsigned int	m_free_count;

		//! Тип описателя одного блока.
		/*!
			Полем first является признак занятости блока (true, если
				блок свободен).

			Полем second является идентификатор следующего блока в
			цепочке.
		*/
		typedef std::pair< bool, chain_id_t >	block_info_t;

		//! Описатели блоков данного сегмента.
		/*!
			Количество элементов должно быть m_useful_block_count.
		*/
		std::vector< block_info_t >	m_items;

		//! Вычислить порядковый номер блока в векторе описателей.
		unsigned int
		item_index( const chain_id_t & id ) const;
	};

//
// ref_segment_header_t
//

SMART_REF_3_DECL_REF( segment_header_t, class )

} /* namespace impl */

} /* namespace storage */

} /* namespace db */

} /* namespace oess_1 */

#endif

