/*

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.

*/

/*!
	\file
	\brief Штатная реализация интерфейса %entity_stream_t.
*/

#include <oess_1/io/h/mem_buf.hpp>

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

#include <oess_1/db/storage/impl/h/std_entity_stream.hpp>

namespace oess_1 {

namespace db {

namespace storage {

namespace impl {

//
// stream_desc_t
//

stream_desc_t::stream_desc_t()
	:	m_first( invalid_chain_id )
	,	m_last( invalid_chain_id )
	{}

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

stream_desc_t::~stream_desc_t()
	{}

stream_desc_t &
stream_desc_t::operator=(
	const stream_desc_t & o )
	{
		if( &o != this )
			{
				base_type_t::operator=( o );

				m_first = o.m_first;
				m_last = o.m_last;
			}

		return *this;
	}

chain_id_t
stream_desc_t::first() const
	{
		return m_first;
	}

void
stream_desc_t::set_first(
	const chain_id_t & chain )
	{
		m_first = chain;
	}

chain_id_t
stream_desc_t::last() const
	{
		return m_last;
	}

void
stream_desc_t::set_last(
	const chain_id_t & chain )
	{
		m_last = chain;
	}

//
// stream_item_t
//

stream_item_t::stream_item_t()
	:	m_description_chain( invalid_chain_id )
	,	m_prev( invalid_chain_id )
	,	m_next( invalid_chain_id )
	,	m_length( 0 )
	,	m_entity_chain( invalid_chain_id )
	{}

stream_item_t::stream_item_t(
	const chain_id_t & description_chain,
	const chain_id_t & prev,
	oess_1::uint_t & length,
	const chain_id_t & entity_chain )
	:	m_description_chain( description_chain )
	,	m_prev( prev )
	,	m_next( invalid_chain_id )
	,	m_length( length )
	,	m_entity_chain( entity_chain )
	{}

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

stream_item_t::~stream_item_t()
	{}

stream_item_t &
stream_item_t::operator=(
	const stream_item_t & o )
	{
		if( &o != this )
			{
				base_type_t::operator=( o );

				m_description_chain = o.m_description_chain;
				m_prev = o.m_prev;
				m_next = o.m_next;
				m_length = o.m_length;
				m_entity_chain = o.m_entity_chain;
			}

		return *this;
	}

chain_id_t
stream_item_t::description_chain() const
	{
		return m_description_chain;
	}

void
stream_item_t::set_description_chain(
	const chain_id_t & chain )
	{
		m_description_chain = chain;
	}

chain_id_t
stream_item_t::prev() const
	{
		return m_prev;
	}

void
stream_item_t::set_prev(
	const chain_id_t & chain )
	{
		m_prev = chain;
	}

chain_id_t
stream_item_t::next() const
	{
		return m_next;
	}

void
stream_item_t::set_next(
	const chain_id_t & chain )
	{
		m_next = chain;
	}

void
stream_item_t::set_entity_params(
	oess_1::uint_t length,
	const chain_id_t & chain )
	{
		m_length = length;
		m_entity_chain = chain;
	}

oess_1::uint_t
stream_item_t::length() const
	{
		return m_length;
	}

const chain_id_t &
stream_item_t::entity_chain() const
	{
		return m_entity_chain;
	}

#include "std_entity_stream.ddl.cpp"

/*
** std_entity_stream_t
*/

std_entity_stream_t::std_entity_stream_t(
	chain_storage_t & storage,
	oess_1::uint_t id,
	const chain_id_t & description_chain )
	:	m_storage( storage )
	,	m_id( id )
	,	m_description_chain( description_chain )
	{
		// Нужно загрузить описание потока из хранилища.
		load_from_storage( storage, description_chain, m_desc );
	}

std_entity_stream_t::~std_entity_stream_t()
	{}

oess_1::uint_t
std_entity_stream_t::id() const
	{
		return m_id;
	}

chain_id_t
std_entity_stream_t::entity_create(
	oess_1::io::istream_t & ent_value,
	oess_1::uint_t ent_size )
	{
		// Cформируем новый конец списка.
		stream_item_t tail( m_description_chain, m_desc.last(), ent_size,
				invalid_chain_id );
		chain_id_t tail_chain = create_in_storage( m_storage, tail );

		// Нужно сохранить значение сущности, чтобы получить
		// идентификатор цепочки со значением.
		chain_id_t entity_chain = m_storage.chain_create(
				ent_value, ent_size );

		// Обновляем параметры в заголовке.
		tail.set_entity_params( ent_size, entity_chain );
		update_in_storage( m_storage, tail_chain, tail );

		// Прописываем новый элемент в список.
		if( invalid_chain_id != m_desc.last() )
			{
				stream_item_t prev_last;
				load_from_storage( m_storage, m_desc.last(), prev_last,
						stream_item_t::length_limit );
				prev_last.set_next( tail_chain );
				update_in_storage( m_storage, m_desc.last(), prev_last );
			}

		// Заменяем описание потока.
		m_desc.set_last( tail_chain );
		if( invalid_chain_id == m_desc.first() )
			m_desc.set_first( tail_chain );

		update_in_storage( m_storage, m_description_chain, m_desc );

		return tail_chain;
	}

void
std_entity_stream_t::entity_load(
	const chain_id_t & ent_id,
	oess_1::io::ostream_t & ent_value ) const
	{
		stream_item_t item;
		load_from_storage( m_storage, ent_id, item,
				stream_item_t::length_limit );

		m_storage.chain_load( item.entity_chain(), ent_value, item.length() );
	}

void
std_entity_stream_t::entity_update(
	const chain_id_t & ent_id,
	oess_1::io::istream_t & ent_value,
	oess_1::uint_t ent_size )
	{
		// Нужно обновить описание объекта в потоке.
		stream_item_t item;
		load_from_storage( m_storage, ent_id, item,
				stream_item_t::length_limit );
		// Обновляем описание только, если размер объекта изменился.
		if( ent_size != item.length() )
			{
				item.set_entity_params( ent_size, item.entity_chain() );
				update_in_storage( m_storage, ent_id, item );
			}

		// Осталось только изменить значение объекта.
		m_storage.chain_update( item.entity_chain(), ent_value, ent_size );
	}

void
std_entity_stream_t::entity_destroy(
	const chain_id_t & ent_id )
	{
		// Нужно изъять описание объекта из потока.
		stream_item_t item;
		load_from_storage( m_storage, ent_id, item,
				stream_item_t::length_limit );
		m_storage.chain_destroy( item.entity_chain() );
		m_storage.chain_destroy( ent_id );

		bool is_desc_changed = false;

		// Корректируем левую часть двухсвязного списка.
		if( invalid_chain_id != item.prev() )
			{
				stream_item_t left;
				load_from_storage( m_storage, item.prev(), left,
						stream_item_t::length_limit );
				left.set_next( item.next() );
				update_in_storage( m_storage, item.prev(), left );
			}
		else
			{
				m_desc.set_first( item.next() );
				is_desc_changed = true;
			}

		// Корректируем правую часть двухсвязного списка.
		if( invalid_chain_id != item.next() )
			{
				stream_item_t right;
				load_from_storage( m_storage, item.next(), right,
						stream_item_t::length_limit );
				right.set_prev( item.prev() );
				update_in_storage( m_storage, item.next(), right );
			}
		else
			{
				m_desc.set_last( item.prev() );
				is_desc_changed = true;
			}
		
		if( is_desc_changed )
			update_in_storage( m_storage, m_description_chain, m_desc );
	}

void
std_entity_stream_t::entity_destroy_all()
	{
		stream_item_t item;
		chain_id_t cur = m_desc.first();

		while( invalid_chain_id != cur )
			{
				load_from_storage( m_storage, cur, item,
						stream_item_t::length_limit );
				m_storage.chain_destroy( item.entity_chain() );
				m_storage.chain_destroy( cur );

				cur = item.next();
			}

		m_desc.set_first( invalid_chain_id );
		m_desc.set_last( invalid_chain_id );
		update_in_storage( m_storage, m_description_chain, m_desc );
	}

chain_id_t
std_entity_stream_t::entity_find_next(
	const chain_id_t & ent_id ) const
	{
		chain_id_t result;
		if( invalid_chain_id == ent_id )
			result = m_desc.first();
		else
			{
				stream_item_t item;
				load_from_storage( m_storage, ent_id, item,
						stream_item_t::length_limit );
				result = item.next();
			}

		return result;
	}

} /* namespace impl */

//
// create_std_entity_stream
//

OESS_1__DB__FUNC(entity_stream_t *)
create_std_entity_stream(
	chain_storage_t & storage,
	oess_1::uint_t id,
	const chain_id_t & description_chain )
	{
		return new impl::std_entity_stream_t(
				storage, id, description_chain );
	}

//
// create_entity_stream_description
//

OESS_1__DB__FUNC(chain_id_t)
create_entity_stream_description(
	chain_storage_t & storage )
	{
		impl::stream_desc_t empty;
		return create_in_storage( storage, empty );
	}

//
// destroy_entity_stream_description
//

OESS_1__DB__FUNC(void)
destroy_entity_stream_description(
	chain_storage_t & storage,
	const chain_id_t & description_chain )
	{
		storage.chain_destroy( description_chain );
	}

} /* namespace storage */

} /* namespace db */

} /* namespace oess_1 */

