/*

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__STD_CHAIN_STORAGE_HPP_ )
#define _OESS_1__DB__STORAGE__IMPL__STD_CHAIN_STORAGE_HPP_

#include <auto_ptr_3/h/obj_ptr.hpp>

#include <oess_1/file/h/file.hpp>

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

#include <oess_1/db/storage/trx/h/types.hpp>

#include <oess_1/db/storage/impl/h/cache.hpp>
#include <oess_1/db/storage/impl/h/page_accessor.hpp>
#include <oess_1/db/storage/impl/h/header_processor.hpp>

namespace oess_1 {

namespace db {

namespace storage {

namespace impl {

//
// std_chain_storage_t
//

/*!
	\since v.1.4.0
	\brief Штатная реализация интерфейса %chain_storage_t.
*/
class	std_chain_storage_t
	:	public chain_storage_t
	{
	public :
		/*!
			Инициализирующий конструктор.

			Предпринимает попытку открытия БД.
		*/
		std_chain_storage_t(
			//! Физическое имя БД, с которой происходит работа.
			const std::string & physic_name,
			//! Имя основного файла БД.
			const std::string & main_file_name,
			//! Имя для основного файла БД на время работы.
			//! Игнорируется, если БД открывается в режиме read-only.
			const std::string & work_file_name,
			//! Конфигурация для работы.
			const config_t & config,
			//! true, если БД открывается в режиме read-only.
			bool is_read_only,
			//! Нужно ли пытаться восстанавливать БД, если обнаруживается,
			//! что она повреждена.
			bool is_auto_repair_enabled );

		virtual ~std_chain_storage_t();

	//! \name Реализация унаследованных методов.
	//! \{
		virtual void
		trx_start();

		virtual void
		trx_commit();

		virtual void
		trx_rollback();

		virtual chain_id_t
		chain_create(
			oess_1::io::istream_t & from,
			oess_1::uint_t length );

		virtual void
		chain_destroy(
			const chain_id_t & chain );

		virtual void
		chain_load(
			const chain_id_t & chain,
			oess_1::io::ostream_t & to,
			oess_1::uint_t max_length );

		virtual void
		chain_update(
			const chain_id_t & chain,
			oess_1::io::istream_t & from,
			oess_1::uint_t length );
	//! \}

	private :
		//! Физическое имя БД, с которой происходит работа.
		const std::string	m_physic_name;
		//! Имя основного файла БД.
		const std::string	m_main_file_name;
		//! Имя для основного файла БД на время работы.
		//! Игнорируется, если БД открывается в режиме read-only.
		const std::string	m_work_file_name;
		//! Конфигурация для работы.
		const config_t	m_config;
		//! Параметры хранилища.
		page_index_calculator_t	m_calculator;

		//! true, если БД открывается в режиме read-only.
		bool m_is_read_only;

		//! true, если с БД все нормально.
		/*!
			Если этот атрибут содержит true и БД открыта в режиме
			read-write, то в деструкторе происходит переименование
			рабочего файла в исходное имя.
		*/
		bool m_is_ok;

		//! true, если транзакция начата.
		bool	m_is_trx;

		/*!
			\brief trace-stream для этого хранилища.
		*/
		std::auto_ptr<
				oess_1::db::storage::trx::trace_stream_t >
			m_trace_stream;

		/*!
			Открытый основной файл хранилища.
		*/
		oess_1::file::file_t	m_main_file;
		/*!
			Логическая длина файла.

			Во время проведения транзакции, когда физически страницы
			в основной файл не вытесняются, логическая длина не
			совпадает с физической длиной файла.

			После подтверждения или отката транзакции логическая длина
			должна быть равна физической длине файла.
		*/
		oess_1::uint_t	m_main_file_logic_length;

		//! Кэш основного файла.
		std::auto_ptr< cache_t >	m_cache;

		//! Хранилище грязных страниц транзакции.
		std::auto_ptr< dirty_page_storage_t > m_dirty_pages;

		//! Объект для доступа к страницам хранилища, который будет
		//! использоваться вспомогательными объектами при проведении
		//! транзакций.
		std::auto_ptr< page_accessor_t > m_page_accessor;

		//! Объект для управления заголовками сегментов, областей и
		//! всего хранилища.
		/*!
			Создается при первом обращении к нему. Уничтожается при
			первом откате транзакции.
		*/
		std::auto_ptr< header_processor_t > m_header_processor;

	//! \name Низкоуровневые двойники public-методов.
	//! \{
		//! Выполнить фиксацию транзакции.
		void
		lo_trx_commit();

		//! Выполнить откат транзакции.
		void
		lo_trx_rollback();

		//! Создать новую цепочку и сохранить в нее значение объекта.
		chain_id_t
		lo_chain_create(
			oess_1::io::istream_t & from,
			oess_1::uint_t length );

		//! Удалить существующую цепочку.
		void
		lo_chain_destroy(
			const chain_id_t & head );

		//! Загрузить значение цепочки.
		void
		lo_chain_load(
			const chain_id_t & chain,
			oess_1::io::ostream_t & to,	
			oess_1::uint_t max_length );

		//! Обновить значение цепочки.
		void
		lo_chain_update(
			const chain_id_t & chain,
			oess_1::io::istream_t & from,
			oess_1::uint_t length );
	//! \}

		/*!
			Выполнение полной очистки ресурсов без выпуска наружу
			исключений (перехватываются только производные от
			std::exception исключения).

			Значение атрибута m_is_ok не изменяется.
		*/
		void
		cleanup();

		/*!
			Попробовать открыть основной файл.

			Если основного файла нет, а хранилище открывается
			в режиме read-write и восстановление возможно, то
			инициирует восстановление основного файла.
		*/
		void
		try_open_main_file(
			bool is_repair_enabled );

		/*!
			Попробовать восстановить основной файл по trace-stream.

			Файл восстанавливается сразу в unclear-файл.
		*/
		void
		try_recover_main_file();
		
		/*!
			Попробовать открыть существующий основной файл.
			Если хранилище открывается в режиме read-write, то
			основной файл сначала переименовывается в unclear-файл,
			затем открывается unclear-файл.

			В режиме read-only сразу открывается основной файл
			под своим исходным именем.
		*/
      void
		try_open_existing_main_file();
		
		/*!
			Выполнить дополнительную инициализацию объекта после того,
			как trace-stream и основной файлы открыты.
		*/
		void
		initialize_open_storage();

		/*!
			Породить логическое исключение, если нет начатой транзакции.
		*/
		void
		ensure_trx_started();

		/*!
			Вытолкнуть все содержимое из m_dirty_pages в журнал транзакции.
		*/
		void
		make_transaction_log();

		/*!
			Вытолкнуть все содержимое из m_dirty_pages в основной файл БД.

			После завершения этого метода логическая длина файла должна
			совпадать с физической длиной.
		*/
		void
		commit_transaction_to_main_file();

//FIX ME: должен быть метод ensure_valid_chain_id, который бы порождал
//исключение, если chain_id == invalid_chain_id.

		/*!
			Создать/записать/перезаписать цепочку.

			Если \a head == invalid_chain_id, то производит создание
			новой цепочки и возвращает ее идентификатор.

			Если \a head != invalid_chain_id, то производит перезапись
			уже существующей цепочки. При необходимости осуществляет либо
			расширение цепочки (если новый размер объекта больше предыдущего),
			либо усечение. При усечении оставшаяся неиспользованной
			часть цепочки освобождается.
		*/
		chain_id_t
		write_chain(
			page_accessor_t & page_accessor,
			header_processor_t & header_processor,
			const chain_id_t & head,
			//! Новое значение объекта.
			oess_1::io::istream_t & from,
			//! Новая длина объекта.
			oess_1::uint_t length );

		/*!
			Освободить цепочку.
		*/
		void
		deallocate_chain(
			page_accessor_t & page_accessor,
			header_processor_t & header_processor,
			const chain_id_t & head );

		/*!
			Загрузить значение цепочки, но не более length байт.
		*/
		void
		load_chain(
			page_accessor_t & page_accessor,
			header_processor_t & header_processor,
			const chain_id_t & head,
			//! Приемник значения объекта.
			oess_1::io::ostream_t & to,
			//! Максимальная длина объекта.
			oess_1::uint_t length );

		/*!
			Записать значение очередного блока в нужную страницу хранилища.

			\return количество байт в \a from после записи очередного
				блока.
		*/
		oess_1::uint_t
		write_block(
			page_accessor_t & page_accessor,
			//! Идентификатор блока, значение которого должно быть изменено.
			const chain_id_t & block,
			//! Отсюда нужно брать новое значение для блока.
			oess_1::io::istream_t & from,
			//! Общее количество байт в from.
			oess_1::uint_t length );

		/*!
			Прочитать значение очередного блока из нужной страницы.

			\return количество байт которые еще нужно прочитать.
		*/
		oess_1::uint_t
		load_block(
			page_accessor_t & page_accessor,
			//! Идентификатор блока, значение которого должно быть прочитано.
			const chain_id_t & block,
			//! Приемник значения блока.
			oess_1::io::ostream_t & to,
			//! Осталось прочитать.
			oess_1::uint_t length );

		/*!
			Получить объект header_processor, который необходим для
			выполнения очередного действия.
		*/
		header_processor_t &
		get_header_processor();
	};

} /* namespace impl */

} /* namespace storage */

} /* namespace db */

} /* namespace oess_1 */

#endif


