/*

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.

*/

/*!
	\since v.1.4.0
	\file
	\brief Интерфейс объекта, отвечающего за доступ к страницам хранилища.
*/

#include <vector>

//FIX ME: для отладки!
#include <map>
#include <fstream>

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

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

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

namespace oess_1 {

namespace db {

namespace storage {

namespace impl {

//
// page_accessor_t
//

page_accessor_t::~page_accessor_t()
	{}

//
// std_page_accessor_t
//

/*!
	\since v.1.4.0
	\brief Штатная реализация интерфейса %page_accessor_t.
*/
class	std_page_accessor_t
	:	public page_accessor_t
	{
	public :
		//! Инициализирующий конструктор.
		std_page_accessor_t(
			//! Конфигурация для хранилища.
			const config_t & config,
			//! Основной файл БД.
			oess_1::file::file_t & main_file,
			//! Логическая длина основного файла. Будет изменяться по мере
			//! создания новых страниц.
			oess_1::uint_t & main_file_logic_length,
			//! Кэш хранилища.
			cache_t & cache,
			//! Хранилище грязных страниц.
			dirty_page_storage_t & dirty_pages,
			//! true, если допускается только чтение страниц хранилища.
			bool is_read_only );
		virtual ~std_page_accessor_t();

	//! \name Реализация унаследованных методов.
	//! \{
		virtual bool
		is_exists( oess_1::uint_t page );

		virtual const oess_1::char_t *
		read_access( oess_1::uint_t page );

		virtual oess_1::char_t *
		write_access( oess_1::uint_t page );
	//! \}

	private :
		//! Конфигурация для хранилища.
		const config_t & m_config;
		//! Основной файл БД.
		oess_1::file::file_t & m_main_file;
		//! Логическая длина основного файла. Будет изменяться по мере
		//! создания новых страниц.
		oess_1::uint_t & m_main_file_logic_length;
		//! Кэш хранилища.
		cache_t & m_cache;
		//! Хранилище грязных страниц.
		dirty_page_storage_t & m_dirty_pages;
		//! true; если допускается только чтение страниц хранилища.
		bool m_is_read_only;

		//! Вычислить смещение страницы по ее номеру.
		oess_1::uint_t
		page_offset( oess_1::uint_t page ) const
			{
				return page * m_config.page_size();
			}

		//! Извлечь страницу из хранилища.
		oess_1::char_t *
		extract_page_value(
			oess_1::uint_t index,
			bool is_read_only_access = true );
	};

std_page_accessor_t::std_page_accessor_t(
	const config_t & config,
	oess_1::file::file_t & main_file,
	oess_1::uint_t & main_file_logic_length,
	cache_t & cache,
	dirty_page_storage_t & dirty_pages,
	bool is_read_only )
	:	m_config( config )
	,	m_main_file( main_file )
	,	m_main_file_logic_length( main_file_logic_length )
	,	m_cache( cache )
	,	m_dirty_pages( dirty_pages )
	,	m_is_read_only( is_read_only )
	{}

std_page_accessor_t::~std_page_accessor_t()
	{}

bool
std_page_accessor_t::is_exists( oess_1::uint_t page )
	{
		return page_offset( page ) < m_main_file_logic_length;
	}

const oess_1::char_t *
std_page_accessor_t::read_access( oess_1::uint_t page )
	{
		if( !is_exists( page ) )
			OESS_THROW_LOGIC(
					oess_1::db::storage::err::c_invalid_page_index,
					page << ": page not exists in storage; "
					"page offset: " << page_offset( page )
					<< ", file length: " << m_main_file_logic_length )

		const oess_1::char_t * value = m_cache.read_access( page );
		if( !value )
			// Страницы нет в кэше.
			// Если страница присутсвует в хранилище,
			// то нужно прочитать ее в кэш.
			value = extract_page_value( page );
   
		return value;
	}

oess_1::char_t *
std_page_accessor_t::write_access( oess_1::uint_t page )
	{
		if( m_is_read_only )
			OESS_THROW_LOGIC(
					oess_1::db::storage::err::c_read_only_mode,
					"write-access to page denied in read-only mode" )

		oess_1::uint_t new_logic_length = ( page + 1 ) * m_config.page_size();
		if( new_logic_length > m_main_file_logic_length )
			m_main_file_logic_length = new_logic_length;

		oess_1::char_t * value = m_cache.write_access( page );
		if( !value )
			// Страницы нет в кэше.
			// Если страница присутсвует в хранилище,
			// то нужно прочитать ее в кэш.
			value = extract_page_value( page, false );
   
		return value;
	}

oess_1::char_t *
std_page_accessor_t::extract_page_value(
	oess_1::uint_t index, bool is_read_only_access )
	{
		// Попробуем найти страницу среди вытолкнутых
		// в файл грязных страниц.
		ref_loaded_page_t value;
		if( m_dirty_pages.find( index, value ) )
			return m_cache.push( value, m_dirty_pages, is_read_only_access );
		else
			{
				// Страницу нужно загрузить или создать.
				value = ref_loaded_page_t( new loaded_page_t( index,
						m_config.page_size() ) );

				if( page_offset( index ) < m_main_file.length() )
					{
						// Страница находится в хранилище.
						m_main_file.seek( page_offset( index ) );
						m_main_file.read( value->current(), value->page_size() );
						
						// У страницы есть предудущее значение.
						value->change_previous_by_current();
					}

				return m_cache.push( value, m_dirty_pages, is_read_only_access );
			}
	}

//
// create_std_page_accessor
//

std::auto_ptr< page_accessor_t >
create_std_page_accessor(
	const config_t & config,
	oess_1::file::file_t & main_file,
	oess_1::uint_t & main_file_logic_length,
	cache_t & cache,
	dirty_page_storage_t & dirty_pages,
	bool is_read_only )
	{
		return std::auto_ptr< page_accessor_t >(
				new std_page_accessor_t( config, main_file,
						main_file_logic_length, cache,
						dirty_pages, is_read_only ) );
	}

} /* namespace impl */

} /* namespace storage */

} /* namespace db */

} /* namespace oess_1 */

