/*

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.

*/

#include <iostream>
#include <fstream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <cstdio>

#include <time.h>

#include <map>
#include <string>

#include <auto_ptr_3/h/obj_ptr.hpp>

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

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

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

using namespace std;

//
// app_data_t
//
class	app_data_t :
	public oess_1::stdsn::serializable_t
{
	OESS_SERIALIZER( app_data_t )
	public :
		app_data_t();
		app_data_t(
			const std::string & s );
		app_data_t(
			const app_data_t & o );
		virtual ~app_data_t();

		app_data_t &
		operator=(
			const app_data_t & o );

		const std::string &
		query_data() const;

		void
		set_data( const std::string & s );

	private :
		std::string	m_data;
};

app_data_t::app_data_t()
{
}

app_data_t::app_data_t(
	const std::string & s ) :
	m_data( s )
{
}

app_data_t::app_data_t(
	const app_data_t & o )
{
	(*this) = o;
}

app_data_t::~app_data_t()
{
}

app_data_t &
app_data_t::operator=(
	const app_data_t & o )
{
	if( &o != this ) {
		m_data = o.m_data;
	}

	return *this;
}

const std::string &
app_data_t::query_data() const
{
	return m_data;
}

void
app_data_t::set_data( const std::string & s )
{
	m_data = s;
}

//
// recovery_data_t
//
class	recovery_data_t :
	public oess_1::stdsn::serializable_t
{
	OESS_SERIALIZER( recovery_data_t )
	public :
		recovery_data_t();
		recovery_data_t(
			const app_data_t & app_data,
			oess_1::uint_t & live_time );
		recovery_data_t(
			const recovery_data_t & o );
		virtual ~recovery_data_t();

		recovery_data_t &
		operator=(
			const recovery_data_t & o );

		// Возвращает 0, если время жизни истекло
		oess_1::uint_t
		decrement_live_time(
			oess_1::uint_t delta );

	private :
		app_data_t	m_app_data;
		oess_1::uint_t	m_live_time;
		oess_1::uint_t	m_time_to_death;
};

recovery_data_t::recovery_data_t() :
	m_live_time( 0 ),
	m_time_to_death( 0 )
{
}

recovery_data_t::recovery_data_t(
	const app_data_t & app_data,
	oess_1::uint_t & live_time ) :
	m_app_data( app_data ),
	m_live_time( live_time ),
	m_time_to_death( live_time )
{
}

recovery_data_t::recovery_data_t(
	const recovery_data_t & o ) :
	m_live_time( 0 ),
	m_time_to_death( 0 )
{
	(*this) = o;
}

recovery_data_t::~recovery_data_t()
{
}

recovery_data_t &
recovery_data_t::operator=(
	const recovery_data_t & o )
{
	if( &o != this ) {
		m_app_data = o.m_app_data;
		m_live_time = o.m_live_time;
		m_time_to_death = o.m_time_to_death;
	}

	return *this;
}

oess_1::uint_t
recovery_data_t::decrement_live_time(
	oess_1::uint_t delta )
{
	if( delta > m_time_to_death )
		delta = m_time_to_death;

	return ( m_time_to_death -= delta );
}

#include "main.ddl.cpp"

//
// app_t
//
class	app_t {
	public :
		app_t(
			// Уже открытая восстановочная база данных,
			oess_1::db::cln::db_t * db,
			const std::string & slice_name );
		~app_t();

		void
		next_work_time(
			oess_1::uint_t delta );

		enum {
			e_max_obj_count = 5000, //50000,
			e_max_live_time = 5
		};

	private :
		typedef std::map< oess_1::ent_id_t, recovery_data_t >
			recovery_data_map_t;

		oess_1::db::cln::db_t *	m_db_ptr;
		std::string	m_slice_name;

		recovery_data_map_t	m_contents;

		void
		process_loaded_data(
			oess_1::uint_t delta );

		void
		create_new_objects();
};

app_t::app_t(
	oess_1::db::cln::db_t * db,
	const std::string & slice_name )
	:	m_db_ptr( db )
	,	m_slice_name( slice_name )
	{
		m_db_ptr->slice_create( slice_name );

		std::cout << "Load DB content..." << std::endl;
		oess_1::ent_id_t ent_id;
		do
		{
			ent_id = m_db_ptr->ent_find_next( m_slice_name, ent_id );
			if( ent_id )
			{
				recovery_data_t d;
				m_db_ptr->ent_load( ent_id, d );

				m_contents.insert( recovery_data_map_t::
					value_type( ent_id, d ) );

//				std::cout << ent_id << " ";
			}
		} while( ent_id );

		std::cout << "\nDB content loaded: "
			<< m_contents.size() << std::endl;
	}

app_t::~app_t()
{
}

void
app_t::next_work_time(
	oess_1::uint_t delta )
{
	clock_t s = clock(), f;

	process_loaded_data( delta );

	f = clock();
	std::cout << "\ntime 1: "
	    << ((double)( f - s )) / CLOCKS_PER_SEC
	    << "\n" << std::endl;
	s = f;

	create_new_objects();

	f = clock();
	std::cout << "\ntime 2: "
	    << ((double)( f - s )) / CLOCKS_PER_SEC
	    << "\n" << std::endl;

	std::cout << "Capacity: " << m_contents.size() << std::endl;

}

void
app_t::process_loaded_data(
	oess_1::uint_t delta )
{
	std::cout << "remove processed data... " << std::flush;
	unsigned int removed_objs = 0;

//	oess_1::db::cln::transaction_t trx( *m_db_ptr );

	for( recovery_data_map_t::iterator
		it = m_contents.begin();
		it != m_contents.end(); ) {
		recovery_data_t & d = (*it).second;

		if( !d.decrement_live_time( delta ) ) {
			const oess_1::ent_id_t & id = (*it).first;
//			std::cout << id << " ";

			m_db_ptr->ent_destroy( id );
			m_contents.erase( it++ );

			++removed_objs;
		}
		else
			++it;
	}

//	trx.commit();

	std::cout << removed_objs << std::endl;
}

void
app_t::create_new_objects()
{
	int obj_to_create = 0;
	if( m_contents.size() < e_max_obj_count )
		{
			obj_to_create = e_max_obj_count - m_contents.size();
			obj_to_create = ( rand() % obj_to_create );
		}

	std::cout << "Create new " << obj_to_create
		<< " object(s)..." << std::flush;

//	oess_1::db::cln::transaction_t trx( *m_db_ptr );

	char sz_app_data[ 32 ];
	for( int i = 0; i != obj_to_create; ++i ) {
		sprintf( sz_app_data, "app_data #%d", i );

		oess_1::uint_t live_time = 1 + ( rand() % e_max_live_time );

		app_data_t app_data( sz_app_data );
		recovery_data_t d( app_data, live_time );

		oess_1::ent_id_t ent_id = m_db_ptr->ent_create(
			m_slice_name, d );

		m_contents.insert( recovery_data_map_t::
			value_type( ent_id, d ) );

//		std::cout << "[" << ent_id << ", " << live_time << "] ";
	}

//	trx.commit();

	std::cout << std::endl;
}

int
main( int, char ** ) {
	try {
		const std::string phys_db_name( "./sample/app_recovery/db" );

		oess_1::db::cln::local_db_t db( phys_db_name,
				oess_1::db::storage::load_cfg_file(
						"./sample/app_recovery/template.cfg" ) );

		app_t	app( db.query_db(), "slice_1" );

		for( int i = 0; i != 15; ++i )
		{
			app.next_work_time( 1 );
		}

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

	return 1;

}
