/*

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
*/

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

#if !defined( _OESS_1__DB__CLN__SLICE_IMAGE_HPP_ )
#define _OESS_1__DB__CLN__SLICE_IMAGE_HPP_

#include <map>

#include <auto_ptr_3/h/obj_ptr.hpp>

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

#include <oess_1/db/h/declspec.hpp>

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

namespace oess_1
{

namespace db
{

namespace cln
{

/*!
	\since v.1.0.9
	\brief Интерфейс хранилища содержимого раздела в ОП.

	В некоторых случаях выгодно, чтобы объект slice_image_t
	хранил в ОП все объекты раздела и их идентификаторы. В
	некоторых случаях это не возможно. Между тем, сам
	объект slice_image_t вполне может не знать, если ли значение
	объекта из раздела в ОП или нет. Для этого все, что относится
	к хранению объектов раздела в ОП инкапсулируется в
	специальные хранилища. Для хранения всеъ объектов
	создается одно хранилище. Другое хранилище создается, если
	значения объектов в ОП хранить не нужно. Класс
	slice_image_t общается с объектами хранилищами через
	интерфейс, который определен в данном базовом классе.
*/
template< class Item >
class	slice_image_storage_t
{
public :
	slice_image_storage_t()
	{}
	virtual ~slice_image_storage_t()
	{}

	//! Поместить объект в хранилище.
	virtual void
	insert(
		const oess_1::ent_id_t & id,
		const Item & value ) = 0;

	//! Изъять объект из хранилища (но не из БД).
	virtual void
	erase(
		const oess_1::ent_id_t & id ) = 0;

	//! Обновить значение объекта в хранилище (но не в БД).
	virtual void
	update(
		const oess_1::ent_id_t & id,
		const Item & value ) = 0;

	//! Взять значение объекта из хранилища.
	virtual void
	get(
		const oess_1::ent_id_t & id,
		auto_ptr_3::soft_obj_ptr_t< Item > & o ) = 0;

	//! Очистить хранилище.
	virtual void
	clear() = 0;

	//! Перейти к следующему элементу.
	/*! Если prev_id == null, то нужно вернуть идентификатор
		первого элемента. Возвращается null, если больше
		нет элементов. */
	virtual oess_1::ent_id_t
	next(
		const oess_1::ent_id_t & id ) = 0;

private :
	//! Защита от копирования.
	slice_image_storage_t(
		const slice_image_storage_t< Item > & );
	slice_image_storage_t< Item > &
	operator=(
		const slice_image_storage_t< Item > & );
};

/*!
	\since v.1.0.9
	\brief Класс хранилища объектов, который вообще не сохраняет
		значений объектов в ОП.

	Для получения значения объекта и для перечисления идентификаторов
	объектов происходит обращение к БД.

	Использование этого хранилища приводит к большим временным
	затратам при доступе к значению объекта. Еще большие временные
	затраты требуется на подключение нового индекса к уже
	загруженному образу раздела.

	Использование этого хранилища оправдано, если хранение всех
	объектов раздела в ОП не выгодно. Например, из-за большого
	количества объектов и/или большого размера каждого из объектов.
*/
template< class Item >
class	no_slice_ram_storage_t :
	public slice_image_storage_t< Item >
{
private :
	//! База данных, с которой осуществляется работа.
	oess_1::db::cln::db_t &	m_db;
	//! Имя раздела, с которым осуществляетс работа.
	std::string	m_slice_name;

public :
	//! Инициализирующий конструктор.
	no_slice_ram_storage_t(
		oess_1::db::cln::db_t & db,
		const std::string & slice_name )
	:
		m_db( db )
	,	m_slice_name( slice_name )
	{}
	virtual ~no_slice_ram_storage_t()
	{}

	/*! Ничего не нужно делать. */
	virtual void
	insert(
		const oess_1::ent_id_t & id,
		const Item & value )
	{}

	/*! Ничего не нужно делать. */
	virtual void
	erase(
		const oess_1::ent_id_t & id )
	{}

	/*! Ничего не нужно делать. */
	virtual void
	update(
		const oess_1::ent_id_t & id,
		const Item & value )
	{}

	/*! Осуществляется загрузка объекта из БД. */
	virtual void
	get(
		const oess_1::ent_id_t & id,
		auto_ptr_3::soft_obj_ptr_t< Item > & o )
	{
		o.reset( auto_ptr_3::destroyable_obj_t< Item >( new Item() ) );
		m_db.ent_load( id, *o );
	}

	/*! Ничего не нужно делать. */
	virtual void
	clear()
	{}

	/*! Осуществляет перечисление всех объектов раздела
		при помощи методов БД. */
	virtual oess_1::ent_id_t
	next(
		const oess_1::ent_id_t & id )
	{
		return m_db.ent_find_next( m_slice_name, id );
	}
};

/*!
	\since v.1.3.1
	\author V.Kostin
	\brief Класс хранилища объектов, который хранит все
	объекты в ОП.

	Использование хранилища возможно только если прогнозируемый
	объем БД приемлем для размещения в ОП.
*/
template< class Item >
class	slice_ram_storage_t :
	public slice_image_storage_t< Item >
{
private :
	//! База данных, с которой осуществляется работа.
	oess_1::db::cln::db_t &	m_db;
	//! Имя раздела, с которым осуществляетс работа.
	std::string	m_slice_name;

	//! \name Описание типов объектов, хранящихся в копии БД в ОП.
	//! \{
		typedef
			oess_1::ent_id_t
			id_t;
	//! \}

	//! \name Содержимое БД, хранящееся в ОП.
	//! \{
		typedef
			std::map< id_t, Item >
			slice_in_ram_t;

		slice_in_ram_t m_slice_in_ram;
	//! \}

public :
	//! Инициализирующий конструктор.
	slice_ram_storage_t(
		oess_1::db::cln::db_t & db,
		const std::string & slice_name )
	:
		m_db( db )
	,	m_slice_name( slice_name )
	{}
	virtual ~slice_ram_storage_t()
	{}

	/*! Добавляет сущность в буфер в ОП. */
	virtual void
	insert(
		const oess_1::ent_id_t & id,
		const Item & value )
	{
		m_slice_in_ram.insert(
			slice_in_ram_t::value_type( id, value ) );
	}

	/*! Удаляет сущность из ОП. */
	virtual void
	erase(
		const oess_1::ent_id_t & id )
	{
		m_slice_in_ram.erase( id );
	}

	/*! Обновляет сущность в ОП. */
	virtual void
	update(
		const oess_1::ent_id_t & id,
		const Item & value )
	{
		m_slice_in_ram[ id ] = value;
	}

	/*! Осуществляется загрузка объекта из ОП. */
	virtual void
	get(
		const oess_1::ent_id_t & id,
		auto_ptr_3::soft_obj_ptr_t< Item > & o )
	{
		typename slice_in_ram_t::iterator it = m_slice_in_ram.find( id );
		if( m_slice_in_ram.end() != it )
			o.reset( auto_ptr_3::nondestroyable_obj_t< Item >(
				it->second ) );
		else
			OESS_THROW_LOGIC( oess_1::db::cln::err::c_unknown_ent_id,
				"can't find entity " << id << " in RAM cache" )
	}

	/*! Очистка ресурсов - память, выделенная под внутренний буфер,
	очищается. */
	virtual void
	clear()
	{
		m_slice_in_ram.clear();
	}

	/*! Осуществляет перечисление всех объектов раздела.
		\attention Если достигнут конец списка сущностей,
		возвращается эквивалентное NULL значение.
	 */
	virtual oess_1::ent_id_t
	next(
		const oess_1::ent_id_t & id )
	{
		if( id )
			{
				typename slice_in_ram_t::iterator it = m_slice_in_ram.find( id );
				if( m_slice_in_ram.end() != it )
					{
						++it;
						if( m_slice_in_ram.end() != it )
							return it->first;
					}
			}
		else
			if( m_slice_in_ram.size() )
				return m_slice_in_ram.begin()->first;

		return oess_1::ent_id_t();
	}
};

/*!
	\since v.1.0.9
	\brief Образ загруженного в ОП раздела БД.

	По умолчанию объекты раздела в ОП не храняться.
*/
template<
	class Item,
	class Obj_storage = no_slice_ram_storage_t< Item > >
class slice_image_t :
    public slice_image_iface_t< Item >
{
	//! Псевдоним для базового типа.
	typedef slice_image_iface_t< Item >	base_t;

private :
	//! Хранилище объектов.
	/*! Хранилище является динамически созданным
		объектом, чтобы можно было обращаться к
		неконстантным методам хранилища из
		константных методов образа раздела. */
	slice_image_storage_t< Item > *	m_obj_storage;

public:
	/*! Инициализирующий конструктор. Битовые флаги,
		которые могут быть переданы в параметре flags
		описаны в классе slice_image_base_t.

		Раздел автоматически создается в БД.

		Если указан флаг slice_image_base_t::auto_load,
		то содержимое раздела сразу загружается в ОП.
	*/
	slice_image_t(
		//! БД с которой осуществляется работа.
		oess_1::db::cln::db_t & db,
		//! Имя раздела, с чьим образом нужно работать.
		const std::string & slice_name,
		unsigned int flags = 0 )
	throw( std::exception )
	:
		base_t( db, slice_name, flags )
	,	m_obj_storage( new Obj_storage( db, slice_name ) )
	{
		if( base_t::auto_load & flags )
			reload();
	}
	virtual ~slice_image_t()
	{}

	/*! Перезагружает содержимое раздела. */
	void
	reload()
	{
		clear();

		// Организуем цикл по всем объектам раздела, загружаем
		// их в ОП, помещаем в активные индексы.
		db_t & d = base_t::query_db();
		const std::string & s = base_t::slice_name();

		oess_1::ent_id_t id;
		while( id = d.ent_find_next( s, id ) )
		{
			Item o;
			d.ent_load( id, o );
			m_obj_storage->insert( id, o );
			base_t::lo_refresh_indexes( id, o );
		}
	}

	/*! Выгружает содержимое раздела. */
	void
	clear()
	{
		m_obj_storage->clear();
		base_t::lo_clear();
	}

	/*! Добавляет объект в раздел и, автоматически,
		во все активные индексы. */
	virtual oess_1::ent_id_t
	insert( const Item & o )
	{
		oess_1::ent_id_t id = base_t::lo_insert( o );
		m_obj_storage->insert( id, o );

		return id;
	}

	/*! Изымает объект из раздела и, автоматически,
		из всех активных индексов. */
	virtual void
	erase( const oess_1::ent_id_t & id )
	{
		base_t::lo_erase( id );
		m_obj_storage->erase( id );
	}

	/*! Обновляет содержимое объекта в ОП, в разделе
		и во всех активных индексах. */
	virtual void
	update(
		const ent_id_t & id,
		const Item & o )
	{
		base_t::lo_update( id, o );
		m_obj_storage->update( id, o );
	}

	virtual void
	get(
		const ent_id_t & id,
		auto_ptr_3::soft_obj_ptr_t< Item > & o ) const
	{
		m_obj_storage->get( id, o );
	}

	/*! Добавить активный индекс. */
	virtual void
	attach(
		slice_index_base_t & index,
		/*! Если выставлен флаг
			slice_image_base_t::auto_destroy_index, то объект
			индекса будет автоматически уничтожен в
			деструкторе посредством delete. */
		unsigned int flags = 0 )
	{
		base_t::attach( index, flags );

		// Проходим по всем объектам хранилища и
		// помещаем их значения в индекс.
		auto_ptr_3::soft_obj_ptr_t< Item > o;
		oess_1::ent_id_t id;
		while( id = m_obj_storage->next( id ) )
		{
			m_obj_storage->get( id, o );
			index.on_insert( id, *o );
		}
	}
};

} /* namespace cln */

} /* namespace db */

} /* namespace oess_1 */

#endif
