/*

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

#include <ace/Guard_T.h>

#include <auto_ptr_3/h/obj_ptr.hpp>

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

#include <oess_1/defs/log/h/out.hpp>

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

#include <oess_1/db/impl/db_content/h/db_content.hpp>

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

#include <oess_1/db/site/impl/h/std_localhost.hpp>

namespace oess_1 {

namespace db {

namespace site {

//
// create_std_localhost
//

OESS_1__DB__FUNC(localhost_t *)
create_std_localhost()
{
	return new impl::std_localhost_t();
}

namespace impl {

//
// ref_db_content_t
//
SMART_REF_3_IMPL_REF_FULL(
	oess_1::db::impl::db_content::db_content_t,
	ref_db_content_t
)

//
// db_info_t
//

db_info_t::db_info_t()
:	m_is_read_only( false )
,	m_is_auto_repair_enabled( false )
{
}

db_info_t::db_info_t(
	const std::string & physic_name,
	const oess_1::db::storage::config_t & config,
	bool is_read_only,
	bool is_auto_repair_enabled )
:	m_physic_name( physic_name )
,	m_config( config )
,	m_is_read_only( is_read_only )
,	m_is_auto_repair_enabled( is_auto_repair_enabled )
{
}

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

db_info_t::~db_info_t()
{
}

db_info_t &
db_info_t::operator=( const db_info_t & o )
{
	if( &o != this )
	{
		m_physic_name = o.m_physic_name;
		m_config = o.m_config;
		m_is_read_only = o.m_is_read_only;
		m_is_auto_repair_enabled = o.m_is_auto_repair_enabled;
	}

	return *this;
}

//
// std_localhost_t
//

std_localhost_t::std_localhost_t()
{
}

std_localhost_t::~std_localhost_t()
{
}

void
std_localhost_t::db_create(
	const std::string & physic_name,
	oess_1::uint_t creation_mode )
{
	db_create( physic_name, oess_1::db::storage::config_t(),
			creation_mode );
}

void
std_localhost_t::db_create(
	const std::string & physic_name,
	const oess_1::db::storage::config_t & config,
	oess_1::uint_t creation_mode )
{
	bool is_exists = oess_1::db::impl::db_content::
		db_content_t::db_is_exists( physic_name );
	if( is_exists )
		// Существующая БД не может быть перезаписана,
		// если нам это запретили.
		if( no_overwrite & creation_mode )
			return;

	// Во всех остальных случаях БД нужно создавать заново.
	oess_1::db::impl::db_content::db_content_t::db_create(
		physic_name, config );
}

void
std_localhost_t::db_describe(
	const std::string & logic_name,
	const std::string & physic_name,
	oess_1::uint_t cache_size,
	bool is_read_only,
	bool is_auto_repair_enabled )
{
	db_describe( logic_name, physic_name, oess_1::db::storage::config_t(),
			is_read_only, is_auto_repair_enabled );
}

void
std_localhost_t::db_describe(
	const std::string & logic_name,
	const std::string & physic_name,
	const oess_1::db::storage::config_t & config,
	bool is_read_only,
	bool is_auto_repair_enabled )
{
	ACE_Guard< ACE_Thread_Mutex > lock( m_lock );

	if( m_db_info_map.find( logic_name ) != m_db_info_map.end() )
		OESS_THROW_LOGIC(
			oess_1::db::site::err::c_db_already_described,
			"logic_name: " << logic_name )

	m_db_info_map[ logic_name ] = db_info_t(
			physic_name, config,
			is_read_only, is_auto_repair_enabled );
}

void
std_localhost_t::db_flush_all()
{
// Этот метод уже ничего не делает, т.к. каждая БД теперь
//флушируется автоматически.
}

bool
std_localhost_t::db_set_auto_flush(
	const std::string & logic_name,
	bool is_on )
{
	return true;
// Этот метод уже ничего не делает, т.к. каждая БД теперь
//флушируется автоматически.
}

response_t *
std_localhost_t::process(
	const request_t & req )
{
//FIX ME: пока, для простоты, используем один замок
//для всех БД.
ACE_Guard< ACE_Thread_Mutex >	lock( m_lock );

	switch( req.query_req_id() )
	{
		case attach_cmd_t::e_id :
			return on_attach_cmd( dynamic_cast<
				const attach_cmd_t & >( req ) );

		case detach_cmd_t::e_id :
			return on_detach_cmd( dynamic_cast<
				const detach_cmd_t & >( req ) );

		case ent_create_cmd_t::e_id :
			return on_ent_create_cmd( dynamic_cast<
				const ent_create_cmd_t & >( req ) );

		case ent_destroy_cmd_t::e_id :
			return on_ent_destroy_cmd( dynamic_cast<
				const ent_destroy_cmd_t & >( req ) );

		case ent_destroy_all_cmd_t::e_id :
			return on_ent_destroy_all_cmd( dynamic_cast<
				const ent_destroy_all_cmd_t & >( req ) );

		case ent_load_cmd_t::e_id :
			return on_ent_load_cmd( dynamic_cast<
				const ent_load_cmd_t & >( req ) );

		case ent_update_cmd_t::e_id :
			return on_ent_update_cmd( dynamic_cast<
				const ent_update_cmd_t & >( req ) );

		case ent_find_next_cmd_t::e_id :
			return on_ent_find_next_cmd( dynamic_cast<
				const ent_find_next_cmd_t & >( req ) );

		case slice_create_cmd_t::e_id :
			return on_slice_create_cmd( dynamic_cast<
				const slice_create_cmd_t & >( req ) );

		case slice_destroy_cmd_t::e_id :
			return on_slice_destroy_cmd( dynamic_cast<
				const slice_destroy_cmd_t & >( req ) );

		case slice_is_exists_cmd_t::e_id :
			return on_slice_is_exists_cmd( dynamic_cast<
				const slice_is_exists_cmd_t & >( req ) );

		case slice_names_cmd_t::e_id :
			return on_slice_names_cmd( dynamic_cast<
				const slice_names_cmd_t & >( req ) );

		case transaction_cmd_t::e_id :
			return on_transaction_cmd( dynamic_cast<
				const transaction_cmd_t & >( req ) );
	}

	return 0;
}

response_t *
std_localhost_t::on_attach_cmd(
	const attach_cmd_t & req )
{
	try {
		// БД с таким логическим именем должна быть опеределена.
		const db_info_t & db_info = find_db_info(
			req.query_db_logic_name() );

		// Попробуем открыть БД, если она еще не открыта.
		oess_1::db::impl::db_content::db_content_t & db_content =
			open_db_if_needed( req.query_db_logic_name(), db_info );

		// Попробуем подключить клиента к БД.
		db_content.client_attach( req.query_client_id() );

		// Можно отсылать ответ.
		return new attach_cmd_result_t();
	}
	catch( const oess_1::ex_t & x ) {
		return new attach_cmd_result_t(	result_t(
			x.query_err_code() ) );
	}
}

response_t *
std_localhost_t::on_detach_cmd(
	const detach_cmd_t & req )
{
	try {
		// БД должна быть открыта.
		oess_1::db::impl::db_content::db_content_t & db_content =
			find_opened_db( req.query_db_logic_name() );

		// Попробуем отключить клиента от БД.
		db_content.client_detach( req.query_client_id() );

		// Если клиентов больше нет, БД можно закрывать
		if( !db_content.client_count() )
			close_db( req.query_db_logic_name() );

		// Можно отсылать ответ.
		return new detach_cmd_result_t();
	}
	catch( const oess_1::ex_t & x ) {
		return new detach_cmd_result_t(	result_t(
			x.query_err_code() ) );
	}
}

response_t *
std_localhost_t::on_ent_create_cmd(
	const ent_create_cmd_t & req )
{
	try {
		// БД должна быть открыта.
		oess_1::db::impl::db_content::db_content_t & db_content =
			find_opened_db( req.query_db_logic_name() );

		// Попробуем создать сущность.
		oess_1::io::ibstring_t ent_value(
			req.query_ent_value() );
		oess_1::ent_id_t new_ent_id =
			db_content.ent_create( req.query_client_id(),
				req.query_slice_name(),
				ent_value, ent_value.capacity() );

		// Можно отсылать ответ.
		return new ent_create_cmd_result_t( new_ent_id );
	}
	catch( const oess_1::ex_t & x ) {
		return new ent_create_cmd_result_t(	result_t(
			x.query_err_code() ) );
	}
}

response_t *
std_localhost_t::on_ent_destroy_cmd(
	const ent_destroy_cmd_t & req )
{
	try {
		// БД должна быть открыта.
		oess_1::db::impl::db_content::db_content_t & db_content =
			find_opened_db( req.query_db_logic_name() );

		// Попробуем удалить сущность.
		db_content.ent_destroy( req.query_client_id(),
			req.query_ent_id() );

		// Можно отсылать ответ.
		return new ent_destroy_cmd_result_t();
	}
	catch( const oess_1::ex_t & x ) {
		return new ent_destroy_cmd_result_t( result_t(
			x.query_err_code() ) );
	}
}

response_t *
std_localhost_t::on_ent_destroy_all_cmd(
	const ent_destroy_all_cmd_t & req )
{
	try {
		// БД должна быть открыта.
		oess_1::db::impl::db_content::db_content_t & db_content =
			find_opened_db( req.query_db_logic_name() );

		// Попробуем удалить сущность.
		db_content.ent_destroy_all( req.query_client_id(),
			req.query_slice_name() );

		// Можно отсылать ответ.
		return new ent_destroy_all_cmd_result_t();
	}
	catch( const oess_1::ex_t & x ) {
		return new ent_destroy_all_cmd_result_t( result_t(
			x.query_err_code() ) );
	}
}

response_t *
std_localhost_t::on_ent_load_cmd(
	const ent_load_cmd_t & req )
{
	try {
		// БД должна быть открыта.
		oess_1::db::impl::db_content::db_content_t & db_content =
			find_opened_db( req.query_db_logic_name() );

		// Используем auto_ptr_t для освобождения объекта resp,
		// при возникновении исключений.
		ent_load_cmd_result_t * resp = new ent_load_cmd_result_t();
		auto_ptr_3::obj_ptr_t< ent_load_cmd_result_t > resp_ptr( resp );

		// Подготавливаем средства для загрузки значения
		// сущности из хранилища.
		oess_1::io::obstring_t ent_stream(
			resp->query_ent_value() );
		db_content.ent_load(
			req.query_client_id(),
			req.query_ent_id(), ent_stream );

		// Поскольку исключений не произошло, значит, можно
		// отсылать ответ.
		resp_ptr.clear();
		return resp;
	}
	catch( const oess_1::ex_t & x ) {
		return new ent_load_cmd_result_t( result_t(
			x.query_err_code() ) );
	}
}

response_t *
std_localhost_t::on_ent_update_cmd(
	const ent_update_cmd_t & req )
{
	try {
		// БД должна быть открыта.
		oess_1::db::impl::db_content::db_content_t & db_content =
			find_opened_db( req.query_db_logic_name() );

		// Попробуем создать сущность.
		oess_1::io::ibstring_t ent_value(
			req.query_ent_value() );
		db_content.ent_update( req.query_client_id(),
			req.query_ent_id(), ent_value, ent_value.capacity() );

		// Можно отсылать ответ.
		return new ent_update_cmd_result_t();
	}
	catch( const oess_1::ex_t & x ) {
		return new ent_update_cmd_result_t( result_t(
			x.query_err_code() ) );
	}
}

response_t *
std_localhost_t::on_ent_find_next_cmd(
	const ent_find_next_cmd_t & req )
{
	try {
		// БД должна быть открыта.
		oess_1::db::impl::db_content::db_content_t & db_content =
			find_opened_db( req.query_db_logic_name() );

		// Попробуем найти сущность.
		oess_1::ent_id_t next_ent_id =
			db_content.ent_find_next( req.query_client_id(),
				req.query_slice_name(),
				req.query_ent_id() );

		// Можно отсылать ответ.
		return new ent_find_next_cmd_result_t(
			next_ent_id );
	}
	catch( const oess_1::ex_t & x ) {
		return new ent_find_next_cmd_result_t( result_t(
			x.query_err_code() ) );
	}
}

response_t *
std_localhost_t::on_slice_create_cmd(
	const slice_create_cmd_t & req )
{
	try
	{
		// БД должна быть открыта.
		oess_1::db::impl::db_content::db_content_t & db_content =
			find_opened_db( req.query_db_logic_name() );

		// Попробуем создать раздел.
		db_content.slice_create(
			req.query_client_id(),
			req.query_slice_name() );

		// Можно отсылать ответ.
		return new slice_create_cmd_result_t();
	}
	catch( const oess_1::ex_t & x )
	{
		return new slice_create_cmd_result_t( result_t(
			x.query_err_code() ) );
	}
}

response_t *
std_localhost_t::on_slice_destroy_cmd(
	const slice_destroy_cmd_t & req )
{
	try
	{
		// БД должна быть открыта.
		oess_1::db::impl::db_content::db_content_t & db_content =
			find_opened_db( req.query_db_logic_name() );

		// Попробуем создать раздел.
		db_content.slice_destroy(
			req.query_client_id(),
			req.query_slice_name() );

		// Можно отсылать ответ.
		return new slice_destroy_cmd_result_t();
	}
	catch( const oess_1::ex_t & x )
	{
		return new slice_destroy_cmd_result_t( result_t(
			x.query_err_code() ) );
	}
}

response_t *
std_localhost_t::on_slice_is_exists_cmd(
	const slice_is_exists_cmd_t & req )
{
	try
	{
		// БД должна быть открыта.
		oess_1::db::impl::db_content::db_content_t & db_content =
			find_opened_db( req.query_db_logic_name() );

		// Попробуем проверить существование разделa.
		bool r = db_content.slice_is_exists(
			req.query_client_id(),
			req.query_slice_name() );

		// Можно отсылать ответ.
		return new slice_is_exists_cmd_result_t( r );
	}
	catch( const oess_1::ex_t & x )
	{
		return new slice_is_exists_cmd_result_t( result_t(
			x.query_err_code() ) );
	}
}

response_t *
std_localhost_t::on_slice_names_cmd(
	const slice_names_cmd_t & req )
{
	try
	{
		// БД должна быть открыта.
		oess_1::db::impl::db_content::db_content_t & db_content =
			find_opened_db( req.query_db_logic_name() );

		// Попробуем получить имена существующих разделов.
		return new slice_names_cmd_result_t(
			db_content.slice_names(
				req.query_client_id() ) );
	}
	catch( const oess_1::ex_t & x )
	{
		return new slice_names_cmd_result_t( result_t(
			x.query_err_code() ) );
	}
}

response_t *
std_localhost_t::on_transaction_cmd(
	const transaction_cmd_t & req )
{
	try
	{
		// БД должна быть открыта.
		oess_1::db::impl::db_content::db_content_t & db_content =
			find_opened_db( req.query_db_logic_name() );

		// Выполняем заказанное действие.
		const std::string & client_id = req.query_client_id();
		switch( req.query_op_id() )
		{
			case transaction_cmd_t::begin :
				db_content.transaction_begin( client_id );
			break;

			case transaction_cmd_t::commit :
				db_content.transaction_commit( client_id );
			break;

			case transaction_cmd_t::rollback :
				db_content.transaction_rollback( client_id );
			break;
		}

		// Можно отсылать ответ.
		return new transaction_cmd_result_t();
	}
	catch( const oess_1::ex_t & x )
	{
		return new transaction_cmd_result_t( result_t(
			x.query_err_code() ) );
	}
}

const db_info_t &
std_localhost_t::find_db_info(
	const std::string & db_logic_name ) const
{
	db_info_map_t::const_iterator it = m_db_info_map.find(
		db_logic_name );
	if( it == m_db_info_map.end() )
		OESS_THROW_LOGIC(
			oess_1::db::site::err::c_db_not_described,
			"logic_name: " << db_logic_name )

	return (*it).second;
}

oess_1::db::impl::db_content::db_content_t &
std_localhost_t::open_db_if_needed(
	const std::string & db_logic_name,
	const db_info_t & db_info )
{
	ref_db_content_t ref_db;

	ref_db_content_map_t::iterator it = m_db_content_map.find(
		db_logic_name );
	if( it == m_db_content_map.end() ) {
		// БД нужно открывать...
		ref_db = ref_db_content_t( oess_1::db::impl::
			db_content::create_std_db_content().release() );

		ref_db->open( db_info.m_physic_name,
			db_info.m_config,
			db_info.m_is_read_only,
			db_info.m_is_auto_repair_enabled );

		// Если исключений не возникло, значит БД открыта и
		// ее нужно сохранить в списке открытых БД.
		m_db_content_map.insert(
			ref_db_content_map_t::value_type(
				db_logic_name, ref_db ) );
	}
	else
		ref_db = (*it).second;

	return *ref_db;
}

oess_1::db::impl::db_content::db_content_t &
std_localhost_t::find_opened_db(
	const std::string & db_logic_name )
{
	ref_db_content_map_t::iterator it = m_db_content_map.find(
		db_logic_name );
	if( it == m_db_content_map.end() )
		OESS_THROW_LOGIC(
			oess_1::db::site::err::c_db_not_open,
			"logic_name: " << db_logic_name )

	return (*((*it).second));
}

void
std_localhost_t::close_db(
	const std::string & db_logic_name )
{
	m_db_content_map.erase( db_logic_name );
}

} /* namespace impl */

} /* namespace site */

} /* namespace db */

} /* namespace oess_1 */
