/*
	ObjESSty-1
*/

/*
	Измерение производительности oess db при массовом
	создании и чтении объектов. Сравнивается производительность
	image с хранением объектов в памяти и без.
*/

#include <iostream>
#include <stdexcept>
#include <set>
#include <vector>
#include <map>

#include <ctime>
#include <cstdio>
#include <cstdlib>

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

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

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

class	data_t
	:	public oess_1::stdsn::serializable_t
	{
		typedef oess_1::stdsn::serializable_t	base_type_t;

		OESS_SERIALIZER( data_t )
	private :
		std::string	m_data;

	public :
		data_t()
			{}
		data_t( const char * data, std::size_t data_size )
			:	m_data( data, data_size )
			{}
		data_t( const data_t & o )
			{
				(*this) = o;
			}
		virtual ~data_t()
			{}

		data_t &
		operator=( const data_t & o )
			{
				base_type_t::operator=( o );
				m_data = o.m_data;

				return *this;
			}
	};

#include "main.ddl.cpp"

typedef
	oess_1::db::cln::slice_image_t< data_t >
	no_memory_image_t;

typedef
	oess_1::db::cln::slice_ram_storage_t< data_t >
	ram_storage_t;

typedef
	oess_1::db::cln::slice_image_t< data_t, ram_storage_t >
	memory_image_t;


class	test_suite_t
	{
	public :
		virtual void
		run() = 0;
	};

// typedef std::set< oess_1::ent_id_t >	ent_id_set_t;
typedef std::vector< oess_1::ent_id_t >	ent_id_set_t;


template< class ImageType >
class	object_creator_suite_t
	:	public test_suite_t
	{
	private :
		ImageType &	m_db;
		ent_id_set_t &	m_entities;

	public :
		object_creator_suite_t(
			ImageType & db,
			ent_id_set_t & entities )
			:	m_db( db )
			,	m_entities( entities )
			{
				std::srand( std::clock() );
			}

		virtual void
		run()
		{
			static char buf[ 160 * 5 ];
			static char filler;

			int len = 0;
			do
			{
				len = rand() % sizeof( buf );
			} while( !len );

			for( int i = 0; i != len; ++i )
				buf[ i ] = filler++;

			m_entities.push_back(
				m_db.insert( data_t( buf, len ) ) );
		}
	};

template< class ImageType >
class	object_destroyer_suite_t
	:	public test_suite_t
	{
	private :
		ImageType &	m_db;
		ent_id_set_t::const_iterator	m_current;

	public :
		object_destroyer_suite_t(
			ImageType & db,
			const ent_id_set_t & entities )
			:	m_db( db )
			,	m_current( entities.begin() )
			{}

		virtual void
		run()
		{
			m_db.erase( *(m_current++) );
		}
	};

template< class ImageType >
class	object_reader_suite_t
	:	public test_suite_t
	{
	private :
		ImageType &	m_db;
		const ent_id_set_t & m_entities;

	public :
		object_reader_suite_t(
			ImageType & db,
			const ent_id_set_t & entities )
			:	m_db( db ),
				m_entities( entities )
			{}

		virtual void
		run()
		{
			auto_ptr_3::soft_obj_ptr_t< data_t > o;
			int idx = rand() % m_entities.size();
			m_db.get( m_entities[ idx ], o );
		}
	};


void
run_and_measure(
	test_suite_t & suite )
{
	const unsigned long cycles = 10000ul;
	std::clock_t start, finish;

	start = std::clock();
	for( unsigned long i = 0; i != cycles; ++i )
		suite.run();
	finish = std::clock();

	double duration = double( finish - start ) / CLOCKS_PER_SEC;
	double price = duration / cycles;
	std::printf( "duration: %g, price: %g\n", duration, price );
}


template< class ImageType >
void
test( const std::string & physic_name )
{
	oess_1::db::cln::local_db_t db( physic_name );
	ImageType image( *db.query_db(), "test",
		oess_1::db::cln::slice_image_base_t::auto_load );

	ent_id_set_t entities;

	std::cout << "create test: " << std::endl;

	object_creator_suite_t< ImageType > creator( image, entities );
	run_and_measure( creator );

	std::cout << "read test: " << std::endl;

	object_reader_suite_t< ImageType > reader( image, entities );
	run_and_measure( reader );

	std::cout << "erase test: " << std::endl;

	object_destroyer_suite_t< ImageType > destroyer( image, entities );
	run_and_measure( destroyer );
}

int
main()
	{
		try {
			std::cout << "Test RAM-storage image..." << std::endl;
			test< memory_image_t >( "./test/bench/oess_db/create_read/mem_db" );

			std::cout << "Test NO-RAM-storage image..." << std::endl;
			test< no_memory_image_t >( "./test/bench/oess_db/create_read/no_mem_db" );

		return 0;
		}
		catch( oess_1::ex_t & x ) {
			std::cerr << "*** " << x.query_err_code() << " ***"
				<< std::endl;
		}
		catch( ... ) {
			std::cerr << "Oops! Unknown exception!" << std::endl;
		}

		return 1;
	}
