/*

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 oess_1/tlv/h/scalar.hpp
	\brief Класс %oess_1::tlv::scalar_tlv_t.
*/

#if !defined( _OESS_1__TLV__SCALAR_HPP_ )
#define _OESS_1__TLV__SCALAR_HPP_

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

#include <string>

#include <auto_ptr_3/h/vect_ptr.hpp>

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

#include <oess_1/io/h/bin_data_size.hpp>
#include <oess_1/io/h/subbinstream.hpp>

#include <oess_1/tlv/h/base.hpp>
#include <oess_1/tlv/h/default_processors.hpp>
#include <oess_1/tlv/h/errno.hpp>

namespace oess_1
{

namespace tlv
{

//
// scalar_tlv_t
//

//! TLV для сериализации одиночных объектов примитивных типов.
/*!
	Предназначен для сохранения только тех типов, для
	которых определено действие oess_1::io::bin_data_size_t.
*/
template< class Tag_type,
	class Scalar,
	class Tag_processor = default_tag_processor_t< Tag_type >,
	class Length_processor = default_length_processor_t< Tag_type > >
class	scalar_tlv_t :
	public tlv_base_t
{
	public :
		//! Псевдоним для параметра Scalar.
		typedef Scalar value_type_t;

		//! Синоним для scalar_tlv_t< Tag_type, Scalar,
		//! Tag_processor, Length_processor >.
		typedef scalar_tlv_t< Tag_type, Scalar,
				Tag_processor, Length_processor >
			self_t;

	private :
		//! Значение поля Tag.
		Tag_processor	m_tag;

		//! Обработчик поля Length.
		Length_processor	m_length;

		//! Значение поля Value.
		Scalar	m_value;

	public :
		//! Присваивает пустое значение полю Value.
		scalar_tlv_t( Tag_type id )
			:
				m_tag( id ),
				m_value()
		{
		}

		//! Назначение значения полю Value.
		scalar_tlv_t(
			Tag_type id,
			const Scalar & value )
			:
				m_tag( id ),
				m_value( value )
		{
		}

		//! Конструктор копирования.
		scalar_tlv_t(
			const self_t & o )
			:
				m_tag( o.m_tag ),
				m_length( o.m_length ),
				m_value( o.m_value )
		{
		}

		virtual ~scalar_tlv_t()
		{
		}

		//! Оператор копирования.
		self_t &
		operator=( const self_t & o )
		{
			if( &o != this )
			{
				m_tag = o.m_tag;
				m_length = o.m_length;
				m_value = o.m_value;
			}
			return *this;
		}

		//! Получить текущее значение поля Value.
		const Scalar &
		query_value() const
		{
			return m_value;
		}

		//! Установить значение поля Value.
		void
		set_value(
			//! Новое значение для поля Value.
			const Scalar & value )
		{
			m_value = value;
		}

		//! Получить значение поля Tag.
		virtual default_tag_type_t
		tlv_tag() const
		{
			return m_tag.query_tag();
		}

		//! Количество байт, которые требуются
		//! для представления всего TLV (с учетом Tag, Length,
		//! Value).
		virtual size_t
		tlv_size() const
		{
			size_t length = oess_1::io::bin_data_size_t<
				Scalar >::image_size;
			length += m_length.tlv_length_size( length );
			length += m_tag.tlv_tag_size();

			return length;
		}

		//! Извлечение полей Length и Value из входного потока.
		/*!
			Поле Length не может содержать нулевого значения.

			\throw oess_1::physic_ex_t, если поле Length содержит
			нулевое значение.

			\throw std::exception При возникновении других ошибок.
		*/
		virtual void
		tlv_unpack(
			//! Поток, с текущей позиции которого будет
			//! осуществляться чтение полей Length и Value.
			oess_1::io::ibinstream_t & s,
			//! В данном классе игнорируется
			default_tag_type_t )
		{
			size_t length;
			m_length.tlv_unpack_length( s,  length );
			if( length )
			{
				oess_1::io::isubbinstream_t substream( s, length );
				substream >> m_value;
			}
			else
			{
				OESS_THROW_PHYSIC( err::c_zero_length,
					"awaiting length: " << oess_1::io::bin_data_size_t<
						Scalar >::image_size )
			}
		}

		//! Запись TLV в выходной поток.
		/*!
			Записываются все поля (Tag, Length, Value).
		*/
		virtual void
		tlv_pack(
			//! Поток, с текущей позиции которого будет
			//! осуществляться запись TLV.
			oess_1::io::obinstream_t & s ) const
		{
			size_t length =
				oess_1::io::bin_data_size_t< Scalar >::image_size;

			m_tag.tlv_pack_tag( s );
			m_length.tlv_pack_length( s, length );
			s << m_value;
		}
};

} /* namespace tlv */

} /* namespace oess_1 */

#endif

