/*
	Проверка функции oess_1::db::storage::is_chain_storage_exists.
*/

#include <iostream>
#include <stdexcept>

#include <auto_ptr_3/h/obj_ptr.hpp>

#include <ace/Dirent.h>
#include <ace/Dirent_Selector.h>

#include <pcre++.h>

#include <cpp_util_2/h/lexcast.hpp>
#include <cpp_util_2/lexcasts/h/util.hpp>

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

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

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

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

//
// split_physic_name
//

/*!
	Разбить указанное имя на физическое имя БД и имя каталога.
*/
static void
split_physic_name(
	//! Исходное имя, которое, возможно, содержит имя каталога.
	const std::string & physic_name,
	//! Приемник для имени каталога.
	std::string & path_name,
	//! Приемник для имени БД.
	std::string & db_name )
	{
		// Выделяем каталог, в котором нужно искать файлы и само
		// имя БД, для которой файлы ищутся.
		try
			{
				pcrepp::Pcre name_sep_expr(
						"^(.*[\\\\/:]){0,1}([^\\\\/:]+$)" );
				if( !name_sep_expr.search( physic_name ) )
					OESS_THROW_PHYSIC(
							oess_1::db::storage::err::c_unexpected,
							physic_name 
							<< ": unable to split to <path> and <name>; "
									"Pcre::search failed!" )

				// Есть и имя каталога, и имя файла.
				path_name = name_sep_expr[ 0 ];
				db_name = name_sep_expr[ 1 ];
			}
		catch( const std::exception & x )
			{
				OESS_THROW_PHYSIC(
						oess_1::db::storage::err::c_unexpected,
						physic_name 
						<< ": unable to split to <path> and <name>; "
								"exception caught: " << x.what() )
			}
	}

//
// process_db_files
//

/*!
	Выполняет выборку имен файлов хранилища и удаляет их.
*/
static void
process_trace_files(
	//! Найденые имена.
	ACE_Dirent_Selector & selector,
	//! Каталог, в котором должны быть файлы БД.
	const std::string & path_name,
	//! Физическое имя БД, которой должны файлы.
	const std::string & physic_name )
	{
		try
			{
				// Регулярное выражение, которое будет использоваться
				// для выделения нужных нам имен файлов.
				pcrepp::Pcre db_file_regex(
						std::string( "^") + physic_name + "\\..+$" );
   
				for( int i = 0; i != selector.length(); ++i )
					{
						if( db_file_regex.search( selector[ i ]->d_name ) )
							{
								oess_1::file::file_t::remove(
										path_name + selector[ i ]->d_name );
							}
					}
			}
		catch( const std::exception & x )
			{
				OESS_THROW_PHYSIC(
						oess_1::db::storage::err::c_unexpected,
						physic_name 
						<< ": unable to filter found file names; "
								"exception caught: " << x.what() )
			}
	}

//
// remove_all_db_files
//

void
remove_all_db_files(
	const std::string & physic_name )
	{
		// Получаем имя каталога, в котором нужно искать файлы и
		// собственное имя БД.
		std::string path_name, db_name;
		split_physic_name( physic_name, path_name, db_name );

		// Получаем список файлов в указанном каталоге для того,
		// чтобы затем выбрать оттуда имена наших trace-файлов.
		ACE_Dirent_Selector selector;
		if( -1 == selector.open( path_name.c_str() ) )
			OESS_THROW_PHYSIC(
					oess_1::db::storage::err::c_trx_file_search_failed,
					"unable to search trace files in: " << path_name )

		process_trace_files( selector, path_name, db_name );
	}

//
// make_config_file
//

void
make_config_file( const std::string & physic_name,
	const oess_1::db::storage::config_t config = 
			oess_1::db::storage::config_t() )
	{
		oess_1::db::storage::create_cfg_file(
				config.make_cfg_file_name( physic_name ), config );
	}

void
test_no_files( const std::string & physic_name )
	{
		remove_all_db_files( physic_name );

		if( oess_1::db::storage::is_chain_storage_exists( physic_name ) )
			throw std::runtime_error( "test_no_files: FAILED!" );
	}

void
test_no_traces_main_file( const std::string & physic_name )
	{
		{
			remove_all_db_files( physic_name );
			make_config_file( physic_name );

			oess_1::db::storage::config_t config;

			oess_1::file::file_t file;
			file.create( config.make_main_file_name( physic_name ) );
		}

		if( oess_1::db::storage::is_chain_storage_exists( physic_name ) )
			throw std::runtime_error( "test_no_traces_main_file: FAILED!" );
	}

void
test_no_traces_unclear_file( const std::string & physic_name )
	{
		{
			remove_all_db_files( physic_name );
			make_config_file( physic_name );

			oess_1::db::storage::config_t config;

			oess_1::file::file_t file;
			file.create( config.make_unclear_file_name( physic_name ) );
		}

		if( oess_1::db::storage::is_chain_storage_exists( physic_name ) )
			throw std::runtime_error( "test_no_traces_unclear_file: FAILED!" );
	}

void
test_traces_main_file( const std::string & physic_name )
	{
		{
			remove_all_db_files( physic_name );
			make_config_file( physic_name );

			oess_1::db::storage::config_t config;

			oess_1::file::file_t file;
			file.create( config.make_main_file_name( physic_name ) );
			file.close();

			file.create( oess_1::db::storage::trx::create_trace_file_name(
					physic_name, 0 ) );
		}

		if( !oess_1::db::storage::is_chain_storage_exists( physic_name ) )
			throw std::runtime_error( "test_traces_main_file: FAILED!" );
	}

void
test_traces_unclear_file( const std::string & physic_name )
	{
		{
			remove_all_db_files( physic_name );
			make_config_file( physic_name );

			oess_1::db::storage::config_t config;

			oess_1::file::file_t file;
			file.create( config.make_unclear_file_name( physic_name ) );
			file.close();

			file.create( oess_1::db::storage::trx::create_trace_file_name(
					physic_name, 0 ) );
		}

		if( !oess_1::db::storage::is_chain_storage_exists( physic_name ) )
			throw std::runtime_error( "test_traces_unclear_file: FAILED!" );
	}

void
test_trace_mode_last_no_main_file( const std::string & physic_name )
	{
		/*
			БД не должна быть открыта, т.к. в режиме trace_last
			(который включается по-умолчанию) основной файл должен
			существовать.
		*/
		{
			remove_all_db_files( physic_name );
			make_config_file( physic_name );

			oess_1::db::storage::config_t config;

			oess_1::file::file_t file;

			file.create( oess_1::db::storage::trx::create_trace_file_name(
					physic_name, 0 ) );
		}

		if( oess_1::db::storage::is_chain_storage_exists( physic_name ) )
			throw std::runtime_error( "test_trace_mode_last_no_main_file: FAILED!" );
	}

void
test_trace_mode_no_no_main_file( const std::string & physic_name )
	{
		/*
			БД не должна быть открыта, т.к. в режиме trace_no
			(который включается по-умолчанию) основной файл должен
			существовать.
		*/
		{
			using namespace oess_1::db::storage;

			config_t config(
					hard_config_t(
							hard_config_t::default_page_size,
							hard_config_t::default_chain_link_size,
							hard_config_t::default_trx_delta_block_size,
							hard_config_t::default_trace_file_page_size,
							trace_no ) );

			remove_all_db_files( physic_name );
			make_config_file( physic_name, config );

			oess_1::file::file_t file;

			file.create( trx::create_trace_file_name(
					physic_name, 0 ) );
		}

		if( oess_1::db::storage::is_chain_storage_exists( physic_name ) )
			throw std::runtime_error( "test_trace_mode_no_no_main_file: FAILED!" );
	}

void
test_trace_mode_all_no_main_file( const std::string & physic_name )
	{
		/*
			БД должна быть открыта, т.к. в режиме trace_all
			(который включается по-умолчанию) основной файл может 
			отсутствовать.
		*/
		{
			using namespace oess_1::db::storage;

			config_t config(
					hard_config_t(
							hard_config_t::default_page_size,
							hard_config_t::default_chain_link_size,
							hard_config_t::default_trx_delta_block_size,
							hard_config_t::default_trace_file_page_size,
							trace_all ) );

			remove_all_db_files( physic_name );
			make_config_file( physic_name, config );

			oess_1::file::file_t file;

			file.create( trx::create_trace_file_name(
					physic_name, 0 ) );
		}

		if( !oess_1::db::storage::is_chain_storage_exists( physic_name ) )
			throw std::runtime_error( "test_trace_mode_all_no_main_file: FAILED!" );
	}



int
main( int, char ** )
	{
		const std::string physic_name = "test/storage/is_exists/db";
		try
			{
				test_no_files( physic_name );
				test_no_traces_main_file( physic_name );
				test_no_traces_unclear_file( physic_name );
				test_traces_main_file( physic_name );
				test_traces_unclear_file( physic_name );
				test_trace_mode_last_no_main_file( physic_name );
				test_trace_mode_no_no_main_file( physic_name );
				test_trace_mode_all_no_main_file( physic_name );

				return 0;
			}
		catch( const std::exception & x )
			{
				std::cerr << x.what() << std::endl;
			}
		catch( ... )
			{
				std::cerr << "Unknown exception!" << std::endl;
			}

		return 2;
	}
