/*

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.

*/

/*
	ObjESSty-1
*/

/*!
	\since v.1.0.9
	\file
	\brief Базовый класс образа загруженного в ОП содержимого
		раздела БД.
*/

#include <algorithm>
#include <functional>
#include <map>

#include <cpp_util_2/h/nocopy.hpp>

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

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

#include <oess_1/db/cln/h/slice_image_base.hpp>
#include <oess_1/db/cln/h/slice_index_base.hpp>

namespace oess_1
{

namespace db
{

namespace cln
{

namespace impl
{
	/*!
		\since v.1.0.9
		\brief Реальные данные класса
			%oess_1::db::cln::slice_image_base_t.

		Предназначен для скрытия реализации класса
		slice_image_base_t с целью увеличения скорости
		компиляции зависимых от slice_image_base_t
		файлов.
	*/
	struct	slice_image_base_data_t :
		private cpp_util_2::nocopy_t
	{
		//! Карта активных индексов раздела.
		/*! Ключем является указатель на объект
			индекса. Значением -- битовые флаги,
			переданные в метод slice_image_base_t::attach(). */
		typedef std::map<
				slice_index_base_t *,
				unsigned int >
			index_map_t;

		//! База данных, с которой происходит работа.
		oess_1::db::cln::db_t &	m_db;
		//! Раздел, с которым происходит работа.
		std::string	m_slice_name;
		//! Активные индексы.
		index_map_t	m_indexes;

		//! Инициализирующий конструктор.
		slice_image_base_data_t(
			oess_1::db::cln::db_t & db,
			const std::string & slice_name )
		:
			m_db( db )
		,	m_slice_name( slice_name )
		{}
		/*! Автоматически уничтожает те индексы, для
			которых установлен битовый флаг
			slice_image_base_t::auto_destroy_index. */
		~slice_image_base_data_t();
	};

	//! Предикат для std::for_each.
	/*!
		\since v.1.0.9

		Уничтожат индекс, если для него был задан
		битовый флаг slice_image_base_t::auto_destroy_index.
	*/
	static void
	index_destroyer(
		slice_image_base_data_t::index_map_t::value_type & o )
	{
		if( slice_image_base_t::auto_destroy_index & o.second )
			delete o.first;
	}

	slice_image_base_data_t::~slice_image_base_data_t()
	{
		std::for_each( m_indexes.begin(), m_indexes.end(),
			index_destroyer );
	}

} /* namespace impl */

namespace
{
	/*! \name Детали реализации slice_image_base_t */
	/*! \{ */

	typedef impl::slice_image_base_data_t	data_t;

	//! Предикат для std::for_each.
	/*!
		\since v.1.0.9

		Вызывает для каждого очередного индекса
		метод slice_index_base_t::on_insert().
	*/
	class	call_on_insert_functor_t :
		public std::unary_function<
			data_t::index_map_t::value_type,
			void >
	{
		//! Идентификатор добавляемого в индекс объекта.
		const oess_1::ent_id_t &	m_id;
		//! Добавляемый в индекс объект.
		const oess_1::stdsn::serializable_t &	m_o;
	public :
		call_on_insert_functor_t(
			const oess_1::ent_id_t & id,
			const oess_1::stdsn::serializable_t & o )
		:
			m_id( id )
		,	m_o( o )
		{}

		result_type
		operator()( argument_type a )
		{
			a.first->on_insert( m_id, m_o );
		}
	};

	//! Предикат для std::for_each.
	/*!
		\since v.1.0.9

		Вызывает для каждого очередного индекса
		метод slice_index_base_t::on_erase().
	*/
	class	call_on_erase_functor_t :
		public std::unary_function<
			data_t::index_map_t::value_type,
			void >
	{
		//! Идентификатор изымаемого из индекса объекта.
		const oess_1::ent_id_t &	m_id;
	public :
		call_on_erase_functor_t(
			const oess_1::ent_id_t & id )
		:
			m_id( id )
		{}

		result_type
		operator()( argument_type a )
		{
			a.first->on_erase( m_id );
		}
	};

	//! Предикат для std::for_each.
	/*!
		\since v.1.0.9

		Вызывает для каждого очередного индекса
		метод slice_index_base_t::on_update().
	*/
	class	call_on_update_functor_t :
		public std::unary_function<
			data_t::index_map_t::value_type,
			void >
	{
		//! Идентификатор обновляемого в индексе объекта.
		const oess_1::ent_id_t &	m_id;
		//! Обновляемый в индексе объект.
		const oess_1::stdsn::serializable_t &	m_o;
	public :
		call_on_update_functor_t(
			const oess_1::ent_id_t & id,
			const oess_1::stdsn::serializable_t & o )
		:
			m_id( id )
		,	m_o( o )
		{}

		result_type
		operator()( argument_type a )
		{
			a.first->on_update( m_id, m_o );
		}
	};

	/*!
		Вызвать метод slice_index_base_t::on_clear() у
		очередного индекса. */
	void
	call_on_clear(
		data_t::index_map_t::value_type & o )
	{
		o.first->on_clear();
	}

	/*! \} */
}

//
// slice_image_base_t
//

slice_image_base_t::~slice_image_base_t()
{
}

slice_image_base_t::slice_image_base_t(
	oess_1::db::cln::db_t & db,
	const std::string & slice_name,
	unsigned int flags )
throw( std::exception )
:
	m_data( new data_t( db, slice_name ) )
{
	m_data->m_db.slice_create( slice_name );
}

void
slice_image_base_t::attach(
	slice_index_base_t & index,
	unsigned int flags )
{
	m_data->m_indexes[ &index ] = flags;
	index.on_attach();
}

void
slice_image_base_t::detach(
	slice_index_base_t & index )
{
	impl::slice_image_base_data_t::index_map_t::iterator it(
		m_data->m_indexes.find( &index ) );
	if( it != m_data->m_indexes.end() )
	{
		m_data->m_indexes.erase( it );
		index.on_detach();
	}
}

oess_1::db::cln::db_t &
slice_image_base_t::query_db() const
{
	return m_data->m_db;
}

const std::string &
slice_image_base_t::slice_name() const
{
	return m_data->m_slice_name;
}

void
slice_image_base_t::lo_clear()
{
	std::for_each( m_data->m_indexes.begin(),
		m_data->m_indexes.end(),
		call_on_clear );
}

ent_id_t
slice_image_base_t::lo_insert( const oess_1::stdsn::serializable_t & o )
throw( std::exception )
{
	oess_1::ent_id_t id = m_data->m_db.ent_create(
		m_data->m_slice_name, o );

	// Об этом объекте должны все узнать.
	std::for_each(
		m_data->m_indexes.begin(),
		m_data->m_indexes.end(),
		call_on_insert_functor_t( id, o ) );

	return id;
}

void
slice_image_base_t::lo_erase( const oess_1::ent_id_t & id )
throw( std::exception )
{
	// Пытаемся удалить объект из БД и, если это
	// получится, удалим все описатели этого объекта.
	m_data->m_db.ent_destroy( id );

	std::for_each(
		m_data->m_indexes.begin(),
		m_data->m_indexes.end(),
		call_on_erase_functor_t( id ) );
}

void
slice_image_base_t::lo_update(
	const oess_1::ent_id_t & id,
	const oess_1::stdsn::serializable_t & o )
throw( std::exception )
{
	// Пытаемся обновить объект в БД и, если это
	// получится, обновим все описатели этого объекта.
	m_data->m_db.ent_update( id, o );

	std::for_each(
		m_data->m_indexes.begin(),
		m_data->m_indexes.end(),
		call_on_update_functor_t( id, o ) );
}

void
slice_image_base_t::lo_refresh_indexes(
	const oess_1::ent_id_t & id,
	const oess_1::stdsn::serializable_t & o )
{
	// Об этом объекте должны все узнать.
	std::for_each(
		m_data->m_indexes.begin(),
		m_data->m_indexes.end(),
		call_on_insert_functor_t( id, o ) );
}

} /* namespace cln */

} /* namespace db */

} /* namespace oess_1 */
