/*
	Проверка работы метода oess_1::db::cln::slice_image_t::next().
*/

#include <iostream>
#include <stdexcept>
#include <set>

#include <auto_ptr_3/h/obj_ptr.hpp>

#include <cpp_util_2/h/lexcast.hpp>

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

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

#include <oess_1/db/cln/h/db.hpp>
#include <oess_1/db/cln/h/local_db.hpp>
#include <oess_1/db/cln/h/slice_image.hpp>

const std::string physic_name = "test/oess_db/slice_image_next/test.normal";
const std::string slice_name = "test_slice";

class	data_t
	:	public oess_1::stdsn::serializable_t
	{
		OESS_SERIALIZER( data_t )
	private :
		int	m_a;
		std::string	m_b;

	public :
		data_t()
			:	m_a( 0 )
			{}
		data_t(
			int a,
			const std::string & b )
			:	m_a( a )
			,	m_b( b )
			{}

		int
		a() const { return m_a; }

		void
		set_a( int a ) { m_a = a; }

		const std::string &
		b() const { return m_b; }

		void
		set_b( const std::string & b ) { m_b = b; }
	};

#include "main.ddl.cpp"

typedef std::set< oess_1::ent_id_t > id_set_t;

void
test_slice_create( oess_1::db::cln::db_t & storage )
	{
		storage.slice_create( slice_name );
	}

void
test_empty_db( oess_1::db::cln::db_t & storage )
	{
		const std::string test_name = "test_empty_db";

		oess_1::db::cln::slice_image_t< data_t >	image(
				storage, slice_name,
				oess_1::db::cln::slice_image_base_t::auto_load );

		oess_1::ent_id_t id;
		id = image.next( id );

		if( id )
			throw std::runtime_error(
					test_name + "slice must be empty! id found: " +
					cpp_util_2::slexcast( id ) );
	}

void
test_fill_db( oess_1::db::cln::db_t & storage )
	{
		const std::string test_name = "test_fill_db";

		oess_1::db::cln::slice_image_t< data_t > image(
				storage, slice_name,
				oess_1::db::cln::slice_image_base_t::auto_load );

		id_set_t ids;

		for( int i = 0; i != 10; ++i )
			{
				ids.insert(
						image.insert(
								data_t(
										i,
										cpp_util_2::slexcast( i ) ) ) );
			}

		std::size_t found = 0;
		std::size_t enumerated = 0;

		oess_1::ent_id_t id;
		do
			{
				id = image.next( id );
				if( id )
					{
						++enumerated;
						if( ids.end() != ids.find( id ) )
							++found;
					}
			}
		while( id );

		if( found != enumerated )
			throw std::runtime_error(
					test_name + "found != enumerated. found: " +
					cpp_util_2::slexcast( found ) + ", enumerated: " +
					cpp_util_2::slexcast( enumerated ) );
		if( enumerated != ids.size() )
			throw std::runtime_error(
					test_name + "enumerated != ids.size(). enumerated: " +
					cpp_util_2::slexcast( enumerated ) + ", ids.size(): " +
					cpp_util_2::slexcast( ids.size() ) );
	}

void
test_erase_some( oess_1::db::cln::db_t & storage )
	{
		const std::string test_name = "test_erase_some";

		oess_1::db::cln::slice_image_t< data_t > image(
				storage, slice_name,
				oess_1::db::cln::slice_image_base_t::auto_load );

		id_set_t ids;
		oess_1::ent_id_t id;
		do
			{
				id = image.next( id );
				if( id )
					ids.insert( id );
			}
		while( id );

		std::size_t counter = 0;
		for( id_set_t::iterator it = ids.begin(); it != ids.end(); ++counter )
			{
				if( counter % 3 )
					{
						oess_1::ent_id_t to_erase = *it;
						ids.erase( it++ );

						image.erase( to_erase );
					}
				else
					++it;
			}

		std::size_t found = 0;
		std::size_t enumerated = 0;

		do
			{
				id = image.next( id );
				if( id )
					{
						++enumerated;
						if( ids.end() != ids.find( id ) )
							++found;
					}
			}
		while( id );

		if( found != enumerated )
			throw std::runtime_error(
					test_name + "found != enumerated. found: " +
					cpp_util_2::slexcast( found ) + ", enumerated: " +
					cpp_util_2::slexcast( enumerated ) );
		if( enumerated != ids.size() )
			throw std::runtime_error(
					test_name + "enumerated != ids.size(). enumerated: " +
					cpp_util_2::slexcast( enumerated ) + ", ids.size(): " +
					cpp_util_2::slexcast( ids.size() ) );
	}

int
main( int, char ** )
	{
		try
			{
				const oess_1::db::storage::config_t config;
   
				oess_1::db::cln::local_db_t local_db( physic_name, config,
						oess_1::db::cln::local_db_t::auto_create );
				oess_1::db::cln::db_t & storage = *( local_db.query_db() ); 

				/*!
					\attention В тестах используется жесткая привязка к
						результатам предыдущих тестов.
				*/
				test_slice_create( storage );
				test_empty_db( storage );
				test_fill_db( storage );
				test_erase_some( storage );

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

		return 2;
	}

