/*

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 Штатная реализация интерфейса хранилища цепочек.
*/

#include <vector>
#include <algorithm>

#include <oess_1/defs/h/ex.hpp>
#include <oess_1/defs/log/h/err.hpp>

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

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

#include <oess_1/debug/h/time_meter.hpp>

namespace oess_1 {

namespace db {

namespace storage {

namespace impl {

//
// make_outer_err_code
//
inline oess_1::err_code_t
make_outer_err_code(
	const std::string & physic_name,
	const std::string & method_name,
	const oess_1::err_code_t & nested )
	{
		return oess_1::err_code_t(
				nested.m_code,
				nested.m_file,
				nested.m_line,
				"storage: " + physic_name +
				", method: " + method_name + ", nested exception: '"
				+ nested.m_desc + "'" );
	}

#define CHAIN_STORAGE__START_METHOD_WRAPPER(method_name) \
const char * wrp_method_name = #method_name; \
try {

#define CHAIN_STORAGE__FINISH_METHOD_WRAPPER } \
catch( const oess_1::logic_ex_t & x ) \
	{ \
		throw oess_1::logic_ex_t(\
				make_outer_err_code(\
						m_physic_name, wrp_method_name, \
						x.query_err_code() ) ); \
	} \
catch( const oess_1::physic_ex_t & x ) \
	{ \
		m_is_ok = false; \
		throw oess_1::physic_ex_t(\
				make_outer_err_code(\
						m_physic_name, wrp_method_name, \
						x.query_err_code() ) ); \
	}

//
// first_chain_id
//

/*!
	\return идентификатор первой цепочки, которая будет создана в
		новой, пустой БД.
*/
chain_id_t
first_chain_id(
	const config_t & config )
	{
		const page_index_calculator_t calculator(
				config.page_size(), config.chain_link_size() );
		return calculator.first_chain_id();
	}

//
// std_chain_storage_t
//

std_chain_storage_t::std_chain_storage_t(
	const std::string & physic_name,
	const std::string & main_file_name,
	const std::string & work_file_name,
	const config_t & config,
	bool is_read_only,
	bool is_auto_repair_enabled )
	:	m_physic_name( physic_name )
	,	m_main_file_name( main_file_name )
	,	m_work_file_name( work_file_name )
	,	m_config( config )
	,	m_calculator( config.page_size(), config.chain_link_size() )
	,	m_is_read_only( is_read_only )
	,	m_is_ok( false )
	,	m_is_trx( false )
	,	m_trace_stream(
			oess_1::db::storage::trx::create_std_trace_stream(
					config.trace_mode() ) )
	,	m_main_file_logic_length( 0 )
	,	m_cache( create_std_cache() )
	,	m_dirty_pages( create_std_dirty_page_storage() )
	{
		CHAIN_STORAGE__START_METHOD_WRAPPER(
				std_chain_storage_t::std_chain_storage_t )

		if( !is_read_only )
			m_trace_stream->open( physic_name, config, false );

		try_open_main_file( is_auto_repair_enabled );

		initialize_open_storage();

		CHAIN_STORAGE__FINISH_METHOD_WRAPPER
	}

std_chain_storage_t::~std_chain_storage_t()
	{
		try
			{
				cleanup();

				if( m_is_ok && !m_is_read_only )
					// Рабочий файл нужно переименовать обратно.
					oess_1::file::file_t::rename_file(
							m_work_file_name, m_main_file_name, true );
			}
		catch( const std::exception & x )
			{
				OESS_1_LOG_ERR( x.what() << std::endl )
			}
	}

void
std_chain_storage_t::trx_start()
	{
		if( m_is_trx )
			OESS_THROW_LOGIC(
					oess_1::db::storage::err::c_trans_started,
					m_physic_name << ": transaction already started" )

		if( m_is_read_only )
			OESS_THROW_LOGIC(
					oess_1::db::storage::err::c_read_only_mode,
					m_physic_name << ": storage open in read-only mode; "
					"transaction cannot be started" )

		m_is_trx = true;
	}

void
std_chain_storage_t::trx_commit()
	{
		ensure_trx_started();

		CHAIN_STORAGE__START_METHOD_WRAPPER(
				std_chain_storage_t::trx_commit )

		lo_trx_commit();

		CHAIN_STORAGE__FINISH_METHOD_WRAPPER
	}

void
std_chain_storage_t::trx_rollback()
	{
		ensure_trx_started();

		CHAIN_STORAGE__START_METHOD_WRAPPER(
				std_chain_storage_t::trx_rollback )

		lo_trx_rollback();

		CHAIN_STORAGE__FINISH_METHOD_WRAPPER
	}

chain_id_t
std_chain_storage_t::chain_create(
	oess_1::io::istream_t & from,
	oess_1::uint_t length )
	{
		chain_id_t result = invalid_chain_id;

		ensure_trx_started();

		CHAIN_STORAGE__START_METHOD_WRAPPER(
				std_chain_storage_t::chain_create )

		result = lo_chain_create( from, length );

		CHAIN_STORAGE__FINISH_METHOD_WRAPPER

		return result;
	}

void
std_chain_storage_t::chain_destroy(
	const chain_id_t & chain )
	{
		ensure_trx_started();

		CHAIN_STORAGE__START_METHOD_WRAPPER(
				std_chain_storage_t::chain_destroy )

		lo_chain_destroy( chain );

		CHAIN_STORAGE__FINISH_METHOD_WRAPPER
	}

void
std_chain_storage_t::chain_load(
	const chain_id_t & chain,
	oess_1::io::ostream_t & to,
	oess_1::uint_t max_length )
	{
		CHAIN_STORAGE__START_METHOD_WRAPPER(
				std_chain_storage_t::chain_load )

		lo_chain_load( chain, to, max_length );

		CHAIN_STORAGE__FINISH_METHOD_WRAPPER
	}

void
std_chain_storage_t::chain_update(
	const chain_id_t & chain,
	oess_1::io::istream_t & from,
	oess_1::uint_t length )
	{
		ensure_trx_started();

		CHAIN_STORAGE__START_METHOD_WRAPPER(
				std_chain_storage_t::chain_update )

		lo_chain_update( chain, from, length );

		CHAIN_STORAGE__FINISH_METHOD_WRAPPER
	}

void
std_chain_storage_t::lo_trx_commit()
	{
		oess_1::uint_t dirty_page_count = 0;
   
		m_cache->copy_all_dirties( *m_dirty_pages );
		dirty_page_count = m_dirty_pages->size();
   
		if( dirty_page_count )
			{
				// Есть грязные страницы, которые должны быть сначала
				// вытеснены в журнал транзакции, а затем в основной файл.

				// Если режим сохранения восстановочной информации
				// trace_no, то формировать журнал транзакции не имеет
				// смысла.
				if( trace_no != m_config.trace_mode() )
					make_transaction_log();

				commit_transaction_to_main_file();
				m_dirty_pages->clear();
				m_cache->clear_all_dirties();
			}

		m_is_trx = false;
	}

void
std_chain_storage_t::lo_trx_rollback()
	{
		m_cache->throw_away_all_dirties();
		m_dirty_pages->clear();

		m_header_processor.reset();

		m_is_trx = false;
	}

chain_id_t
std_chain_storage_t::lo_chain_create(
	oess_1::io::istream_t & from,
	oess_1::uint_t length ) 
	{
		chain_id_t result = write_chain(
				*m_page_accessor, get_header_processor(),
				invalid_chain_id, from, length );

		// Т.к. исключений не было, то изменения в заголовках
		// нужно зафиксировать.
		get_header_processor().operation_finished();

		return result;
	}

void
std_chain_storage_t::lo_chain_destroy(
	const chain_id_t & chain )
	{
		deallocate_chain(
				*m_page_accessor, get_header_processor(), chain );

		// Т.к. исключений не было, то изменения в заголовках
		// нужно зафиксировать.
		get_header_processor().operation_finished();
	}

void
std_chain_storage_t::lo_chain_load(
	const chain_id_t & chain,
	oess_1::io::ostream_t & to,
	oess_1::uint_t length )
	{
		load_chain(
				*m_page_accessor, get_header_processor(),
				chain, to, length );

		get_header_processor().operation_finished( true );
	}

void
std_chain_storage_t::lo_chain_update(
	const chain_id_t & chain,
	oess_1::io::istream_t & from,
	oess_1::uint_t length )
	{
		chain_id_t result = write_chain(
				*m_page_accessor, get_header_processor(),
				chain, from, length );
		if( result != chain )
			OESS_THROW_LOGIC(
					oess_1::db::storage::err::c_unexpected,
					"chain_id changed after chain update! "
					"chain_id before: " << chain
					<< ", chain_id after: " << result )

		// Т.к. исключений не было, то изменения в заголовках
		// нужно зафиксировать.
		get_header_processor().operation_finished();
	}

void
std_chain_storage_t::cleanup()
	{
		try
			{
				m_cache->cleanup();
				m_dirty_pages->clear();
      
				m_main_file.close();
				m_trace_stream->close();
			}
		catch( const std::exception & x )
			{
				OESS_1_LOG_ERR( x.what() << std::endl )
			}
	}
void
std_chain_storage_t::try_open_main_file(
	bool is_repair_enabled )
	{
		if( !oess_1::file::file_t::is_file_exists( m_main_file_name ) )
			if( !m_is_read_only && is_repair_enabled )
				try_recover_main_file();
			else
				OESS_THROW_PHYSIC(
						oess_1::db::storage::err::c_storage_damage_possible,
						"storage damaged and recovery disabled: "
						<< m_physic_name )
		else
			try_open_existing_main_file();
	}

void
std_chain_storage_t::try_recover_main_file()
	{
		std::auto_ptr< oess_1::db::storage::trx::committer_t >
				committer( oess_1::db::storage::trx::
						create_std_file_committer(
								m_physic_name,
								m_config.trace_mode() ) );

		m_trace_stream->reverse_replay( *committer, 0, 0 );

		// Если исключений не возникло, значит основной файл был
		// восстановлен успешно. Осталось его открыть.
		try_open_existing_main_file();
	}

void
std_chain_storage_t::try_open_existing_main_file()
	{
		std::string file_to_open;
		if( m_is_read_only )
			// В режиме read-only нужно открывать файл с исходным именем.
			file_to_open = m_main_file_name;
		else
			{
				// Основной файл нужно переименовать и открывать уже
				// переименованную копию.
				file_to_open = m_work_file_name;

				oess_1::file::file_t::rename_file(
						m_main_file_name, file_to_open, true );
			}

		m_main_file.open( file_to_open, m_is_read_only );
	}

void
std_chain_storage_t::initialize_open_storage()
	{
		m_cache->reinit( m_config.cache_size(), m_config.page_size() );

		m_main_file_logic_length = m_main_file.length();

		m_page_accessor = create_std_page_accessor(
				m_config, m_main_file,
				m_main_file_logic_length,
				*m_cache, *m_dirty_pages, false );

		m_is_ok = true;
	}

void
std_chain_storage_t::ensure_trx_started()
	{
		if( !m_is_trx )
			OESS_THROW_LOGIC(
					oess_1::db::storage::err::c_trans_not_started,
					m_physic_name << ": transaction must be started" )
	}

void
std_chain_storage_t::make_transaction_log()
	{
		oess_1::db::storage::trx::trace_stream_item_t trace_item(
				m_main_file_logic_length );
		oess_1::db::storage::trx::make_item(
				*m_dirty_pages, m_config, trace_item );

		m_trace_stream->append( trace_item );
	}

//
// page_to_main_file_actor_t
//

/*!
	Функтор, который выталкивает грязную страницу в 
	основной файл БД.
*/
class	page_to_main_file_actor_t
	:	public dirty_page_actor_t
	{
	private :
		const config_t &	m_config;
		oess_1::file::file_t	&	m_to;

	public :
		page_to_main_file_actor_t(
			const config_t & config,
			oess_1::file::file_t & to )
			:	m_config( config )
			,	m_to( to )
			{}

		virtual void
		operator()(
			oess_1::uint_t page_ordinal,
			const oess_1::char_t * page,
			const oess_1::char_t * previous,
			oess_1::uint_t page_size )
			{
				m_to.seek( m_config.page_size() * page_ordinal );
				m_to.write( page, page_size );
			}
	};

void
std_chain_storage_t::commit_transaction_to_main_file()
	{
		page_to_main_file_actor_t	dumper( m_config, m_main_file );
		m_dirty_pages->for_each( dumper );

		// Логическая длина файла теперь должна точно совпадать
		// с физической длиной.
		if( m_main_file_logic_length != m_main_file.length() )
			OESS_THROW_PHYSIC( oess_1::db::storage::err::c_unexpected,
				"logic length must be equal to physic length of main file. "
				"Logic length: " << m_main_file_logic_length
				<< ", physic length: " << m_main_file.length() )
	}

chain_id_t
std_chain_storage_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 )
	{
		chain_id_t current = head;
		chain_id_t prev = invalid_chain_id;
		chain_id_t result = head;

		oess_1::uint_t remaining = length;

		while( remaining )
			{
				// Если блока для очередной порции данных нет,
				// то он должен быть создан.
				if( invalid_chain_id == current )
					{
						current = header_processor.allocate_block();
						if( invalid_chain_id == result )
							// Мы создавали новую цепочку и этот блок
							// будет ее началом.
							result = current;
					}

				// Если мы стоим не на первом элементе цепочки,
				// но нужно, чтобы предыдущий элемент цепочки ссылался
				// на это блок, если этой ссылки еще нет.
				if( invalid_chain_id != prev &&
						current != header_processor.get_next( prev ) )
					header_processor.change_next( prev, current );

				// Записываем очередную порцию и переходим к следующему
				// блоку цепочки.
				remaining = write_block(
						page_accessor, current, from, remaining );

				prev = current;
				current = header_processor.get_next( current );

				// Если значение объекта закончилось, а цепочка еще
				// продолжается, то цепочку нужно оборвать.
				if( !remaining && current != invalid_chain_id )
					header_processor.change_next( prev, invalid_chain_id );
			}

		// Оставшийся хвост нужно удалить.
		if( current != invalid_chain_id )
			deallocate_chain( page_accessor, header_processor, current );

		return result;
	}

void
std_chain_storage_t::deallocate_chain(
	page_accessor_t & page_accessor,
	header_processor_t & header_processor,
	const chain_id_t & head )
	{
		chain_id_t current = head;
		chain_id_t next = invalid_chain_id;

		while( invalid_chain_id != current )
			{
				next = header_processor.get_next( current );
				header_processor.deallocate_block( current );
				current = next;
			}
	}

void
std_chain_storage_t::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 )
	{
		chain_id_t current = head;

		oess_1::uint_t remaining = length;

		if( remaining > m_config.chain_link_size() )
			{
				// Все равно придется определять расположение второго
				// блока. Поэтому принуждаем загрузить заголовок сегмента,
				// в котором расположен первый блок, в кэш.
				chain_id_t second_link = header_processor.get_next( current );
				// Сразу можно прочитать первый блок.
				remaining = load_block( page_accessor, current, to, remaining );
				current = second_link;

				while( remaining && invalid_chain_id != current )
					{
						remaining = load_block(
								page_accessor, current, to, remaining );
						if( remaining )
							current = header_processor.get_next( current );
					}
			}
		else
			// Сразу загружаем единственный блок.
			load_block( page_accessor, current, to, remaining );
	}

oess_1::uint_t
std_chain_storage_t::write_block(
	page_accessor_t & page_accessor,
	const chain_id_t & block,
	oess_1::io::istream_t & from,
	oess_1::uint_t length )
	{
		// Определяем, сколько сможем записать.
		oess_1::uint_t portion_size = m_config.chain_link_size();
		if( portion_size > length )
			portion_size = length;

		// Определяем, куда будет помещен блок.
		oess_1::uint_t page = m_calculator.chain_link_page( block );
		oess_1::uint_t offset = m_calculator.chain_link_page_offset( block );

		// Сохраняем блок.
		oess_1::char_t * block_place = page_accessor.write_access( page ) 
				+ offset;
		std::fill( block_place, block_place + m_config.chain_link_size(),
				'\0' );
		from.read( block_place, portion_size );

		return length - portion_size;
	}

oess_1::uint_t
std_chain_storage_t::load_block(
	page_accessor_t & page_accessor,
	const chain_id_t & block,
	oess_1::io::ostream_t & to,
	oess_1::uint_t length )
	{
		// Определяем, сколько сможем прочитать.
		oess_1::uint_t portion_size = m_config.chain_link_size();
		if( portion_size > length )
			portion_size = length;

		// Определяем, откуда будет взят блок.
		oess_1::uint_t page = m_calculator.chain_link_page( block );
		oess_1::uint_t offset = m_calculator.chain_link_page_offset( block );

		// Извлекаем блок.
		const oess_1::char_t * block_place = page_accessor.read_access( page ) 
				+ offset;
		to.write( block_place, portion_size );

		return length - portion_size;
	}

header_processor_t &
std_chain_storage_t::get_header_processor()
	{
		if( !m_header_processor.get() )
			m_header_processor = create_std_header_processor(
					m_config, *m_page_accessor, false );

		return *m_header_processor;
	}

} /* namespace impl */

/*!
	Удаляет указанный файл, если этот файл существует.
*/
static void
remove_if_exists(
	const std::string & file_name )
	{
		if( oess_1::file::file_t::is_file_exists( file_name ) )
			oess_1::file::file_t::remove( file_name );
	}

/*!
	Удаляет файлы, которые могли остаться от предыдущей БД.
*/
static void
remove_old_files(
	const std::string & physic_name,
	const config_t & config )
	{
		remove_if_exists( config.make_cfg_file_name( physic_name ) );
		remove_if_exists( config.make_main_file_name( physic_name ) );
		remove_if_exists( config.make_unclear_file_name( physic_name ) );
		remove_if_exists( config.make_tmp_main_file_name( physic_name ) );
		remove_if_exists(
				config.make_tmp_unclear_file_name( physic_name ) );

		// Так же нужно удалить trace-stream.
		std::auto_ptr<
				oess_1::db::storage::trx::trace_stream_t >
				trace_stream( oess_1::db::storage::trx::
						create_std_trace_stream( config.trace_mode() ) );
		trace_stream->destroy( physic_name );

		// И все существующие snapshot-файлы.
		std::auto_ptr<
				oess_1::db::storage::trx::snapshot_collection_t >
				snapshots( oess_1::db::storage::trx::
						create_std_snapshot_collection( physic_name ) );
		snapshots->destroy();
	}

/*!
	Создает новые файлы, которые необходимы для открытия БД.
*/
static void
make_new_files(
	const std::string & physic_name,
	const config_t & config )
	{
		// Создаем конфигурационный файл.
		create_cfg_file( config.make_cfg_file_name( physic_name ), config );

		// Создаем временный основной файл хранилища.
		oess_1::file::file_t main_file;
		main_file.create( config.make_tmp_main_file_name( physic_name ) );

		// Так же нужно создать trace-stream.
		std::auto_ptr<
				oess_1::db::storage::trx::trace_stream_t >
				trace_stream( oess_1::db::storage::trx::
						create_std_trace_stream( config.trace_mode() ) );
		trace_stream->create( physic_name, config );
	}

/*!
	Открыть хранилище, используюя вместо основного файла
	временный файл. В открытое хранилище в рамках транзакции
	записывается первая цепочка.
*/
static void
open_and_fill_tmp_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 )
	{
		// БД должна быть открыта, но использоваться должны
		// временные файлы.
		impl::std_chain_storage_t storage( physic_name,
				config.make_tmp_main_file_name( physic_name ),
				config.make_tmp_unclear_file_name( physic_name ),
				config, false, false );

		// Производим первоначальное заполнение БД.
		storage.trx_start();
		first_chain = storage.chain_create( from, length );

		// У первой цепочки должен быть фиксированный идентификатор.
		if( first_chain != impl::first_chain_id( config ) )
			OESS_THROW_PHYSIC(
					oess_1::db::storage::err::c_unexpected,
					"unexpected first chain id: " << first_chain
					<< "; expected value: "
					<< impl::first_chain_id( config ) )

		storage.trx_commit();
	}

/*!
	Удалить временные файлы, которые использовались для
	создания БД.

	На самом деле временные файлы переименовываются в
	основные файлы БД.
*/
static void
remove_tmp_files(
	const std::string & physic_name,
	const config_t & config )
	{
		oess_1::file::file_t::rename_file(
				config.make_tmp_main_file_name( physic_name ),
				config.make_main_file_name( physic_name ) );
	}

/*!
	Создает БД и производит ее первоначальное заполнение.
*/
static void
create_and_fill_db(
	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 )
	{
		remove_old_files( physic_name, config );
		make_new_files( physic_name, config );

		open_and_fill_tmp_storage( physic_name,
				config, from, length, first_chain );

		remove_tmp_files( physic_name, config );
	}

//
// create_chain_storage
//

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 )
	{
		create_and_fill_db(
				physic_name, config, from, length, first_chain );

		return std::auto_ptr< chain_storage_t >(
				new impl::std_chain_storage_t(
						physic_name,
						config.make_main_file_name( physic_name ),
						config.make_unclear_file_name( physic_name ),
						config, false, false ) );
	}

/*!
	\brief Производит объединение прочитанной из конфигурационного
		файла конфигурации и указанной пользователем конфигурации.

	Правила объединения см. в open_chain_storage().
*/
config_t
merge_configs(
	//! Прочитанная из конфигурационного файла информация.
	const config_t & loaded,
	//! Указанная пользовалем информация.
	const config_t & user )
	{
		config_t result( loaded );

		if( config_t::default_cache_size != user.cache_size() )
			result.set_cache_size( user.cache_size() );

		if( config_t::default_trace_file_size_limit !=
				user.trace_file_size_limit() )
			result.set_trace_file_size_limit(
					user.trace_file_size_limit() );

		if( config_t::default_trace_stream_buffered_pages !=
				user.trace_stream_buffered_pages() )
			result.set_trace_stream_buffered_pages(
					user.trace_stream_buffered_pages() );

		if( config_t::default_trace_stream_compression !=
				user.trace_compression() )
			result.set_trace_compression( user.trace_compression() );

		return result;
	}

//
// open_chain_storage
//

OESS_1__DB__FUNC(std::auto_ptr< chain_storage_t >)
open_chain_storage(
	const std::string & physic_name,
	const config_t & config,
	bool is_read_only,
	bool is_auto_repair_enabled,
	oess_1::io::ostream_t & to,
	oess_1::uint_t length,
	chain_id_t & first_chain )
	{
		// Загружаем сохраненную рядом с БД информацию и объединяем ее
		// с указанной нам конфигурацией.
		config_t stored_cfg( load_cfg_file(
				config.make_cfg_file_name( physic_name ) ) );
		config_t used_cfg( merge_configs( stored_cfg, config ) );

		std::auto_ptr< chain_storage_t > storage( 
				new impl::std_chain_storage_t(
						physic_name,
						config.make_main_file_name( physic_name ),
						config.make_unclear_file_name( physic_name ),
						used_cfg, is_read_only, is_auto_repair_enabled ) );

		first_chain = impl::first_chain_id( used_cfg );
		storage->chain_load( first_chain, to, length );

		return storage;
	}

//
// is_chain_storage_exists
//

OESS_1__DB__FUNC(bool)
is_chain_storage_exists(
	const std::string & physic_name )
	{
		config_t config;

		if( !oess_1::file::file_t::is_file_exists(
				config.make_cfg_file_name( physic_name ) ) )
			// Нет конфигурационного файла.
			return false;

		// Для того, чтобы определить наличие trace-файла, нужно
		// загрузить конфигурацию.
		config = load_cfg_file( config.make_cfg_file_name( physic_name ) );

		std::auto_ptr<
				oess_1::db::storage::trx::trace_stream_t >
				trace_stream( oess_1::db::storage::trx::
						create_std_trace_stream( config.trace_mode() ) );
		if( trace_stream->is_exists( physic_name ) )
			{
				// Наличие основного или "грязного" основного файла
				// необходимо проверять только, если режим трассирования
				// отличается от trace_all. В режиме trace_all
				// отсутствующий основной файл может быть восстановлен
				// по trace-файлам.
				if( trace_all == config.trace_mode() )
					return true;

				if( oess_1::file::file_t::is_file_exists(
						config.make_main_file_name( physic_name ) ) ||
						oess_1::file::file_t::is_file_exists(
								config.make_unclear_file_name(
										physic_name ) ) )
					// Есть либо основной, ни "грязный" основной файл.
					return true;
			}

		return false;
	}

//
// destroy_chain_storage
//

OESS_1__DB__FUNC(void)
destroy_chain_storage(
	const std::string & physic_name )
	{
		config_t config;

		remove_old_files( physic_name, config );
	}

} /* namespace storage */

} /* namespace db */

} /* namespace oess_1 */

