/*	Copyright (C) 2003-2008 Free Electron Organization
	Any use of this software requires a license.  If a valid license
	was not distributed with this file, visit freeelectron.org. */

#ifndef __data_WeakRecordSB_h__
#define __data_WeakRecordSB_h__

namespace fe
{

/**	@brief Non-persistant reference to an instance of a Layout

	@ingroup data
*/
class FE_DL_EXPORT WeakRecordSB
{
	friend class RecordArraySB;
	public:
					WeakRecordSB(I32 ignored=0);
//					WeakRecordSB(void *datablock);
					WeakRecordSB(const RecordSB &other);
					WeakRecordSB(const WeakRecordSB &other);
virtual				~WeakRecordSB(void);

					operator RecordSB(void);

		WeakRecordSB	&operator=(const WeakRecordSB &other);

		bool		operator==(const WeakRecordSB &other) const;
		bool		operator!=(const WeakRecordSB &other) const;

					/** @brief Return true if the record points to the
						original valid state block

						A record without the ":SN" field can never
						be confirmed as valid.
						You may be able access it if you are sure it
						still exists by other means. */
		bool		isValid(void) const;
					/** Set the state block. */
		void		set(void *datablock);
					/** Return the state block. */
		UWORD		idr(void) const;
					/** Return the Layout. */
		const hp<LayoutSB>	&layout(void) const;


					/** @brief throw if the record doesn't have
						a serial number */
		void		demandSerialNumber(void) const;

		template <class T>
		T			&accessAttribute(UWORD aLocator) const;

		void		*rawAttribute(UWORD aLocator) const;

	private:
		void		*data(void) const;
					/** Return a raw pointer to the Layout.
						Mainly intended for Accessor for speed. */
		LayoutSB	*rawLayout(void) const;

	private:

		void		cacheSerialNumber(void);
		I32			readSerialNumber(void) const;

		hp<LayoutSB>	m_hpLayout;
		void*		m_pStateBlock;
		I32			m_serialNumber;
		U32			m_serialOffset;
};

class WeakRecordSBInfo : public BaseType::Info
{
	public:
virtual	String	print(void *instance);
virtual	IWORD	output(std::ostream &ostrm, void *instance, t_serialMode mode);
virtual	void	input(std::istream &istrm, void *instance, t_serialMode mode);
virtual	IWORD	iosize(void);
virtual	bool	getConstruct(void);
virtual	void	construct(void *instance);
virtual	void	destruct(void *instance);
};

inline WeakRecordSB::WeakRecordSB(I32):
	m_pStateBlock(NULL),
	m_serialNumber(-1)
{
}

inline WeakRecordSB::WeakRecordSB(const RecordSB& other)
{
	if(other.isValid())
	{
		m_hpLayout = other.layout();
		m_pStateBlock = other.data();

		cacheSerialNumber();
	}
	else
	{
		m_pStateBlock=NULL;
		m_serialNumber= -1;
	}
}

inline WeakRecordSB::WeakRecordSB(const WeakRecordSB &other)
{
	if(other.isValid())
	{
		m_hpLayout = other.m_hpLayout;
		m_pStateBlock = other.m_pStateBlock;
		m_serialOffset = other.m_serialOffset;
		m_serialNumber = other.m_serialNumber;
	}
	else
	{
		m_pStateBlock=NULL;
		m_serialNumber= -1;
	}
}

/*
inline WeakRecordSB::WeakRecordSB(void *datablock)
{
	m_pStateBlock = datablock;
	m_hpLayout = FE_SB_TO_HDR(m_pStateBlock)->m_pStore->getLayout();
}
*/

inline WeakRecordSB::~WeakRecordSB(void)
{
}

inline WeakRecordSB::operator RecordSB(void)
{
	RecordSB record;
	if(isValid())
	{
		record.set(m_pStateBlock);
	}
	return record;
}

inline I32 WeakRecordSB::readSerialNumber(void) const
{
	return (m_serialOffset==LayoutSB::offsetNone)?
		-1: *((I32 *)((char *)(m_pStateBlock) + m_serialOffset));
}

inline LayoutSB *WeakRecordSB::rawLayout(void) const
{
	return m_hpLayout.raw();
}

inline WeakRecordSB &WeakRecordSB::operator=(const WeakRecordSB &other)
{
	if(this != &other)
	{
		m_hpLayout = other.m_hpLayout;
		m_pStateBlock = other.m_pStateBlock;
		m_serialOffset = other.m_serialOffset;
		m_serialNumber = other.m_serialNumber;
	}
	return *this;
}

inline bool WeakRecordSB::operator==(const WeakRecordSB &other) const
{
	return (m_pStateBlock == other.m_pStateBlock);
}

inline bool WeakRecordSB::operator!=(const WeakRecordSB &other) const
{
	return (m_pStateBlock != other.m_pStateBlock);
}

inline bool WeakRecordSB::isValid(void) const
{
	if(m_pStateBlock)
	{
		if(m_serialNumber<0 || m_serialNumber==readSerialNumber())
		{
			return TRUE;
		}

		const_cast<WeakRecordSB*>(this)->m_pStateBlock=NULL;
		const_cast<WeakRecordSB*>(this)->m_serialNumber= -1;
	}

	return FALSE;
}

inline void WeakRecordSB::set(void *datablock)
{
	m_pStateBlock = datablock;
	m_hpLayout = FE_SB_TO_HDR(m_pStateBlock)->m_pStore->getLayout();

	RecordSB record;
	record.set(datablock);
	cacheSerialNumber();

	//* reset if there is no serial number
#if TRUE
	if(isValid() && m_serialNumber<0)
	{
		feLog("WeakRecordSB::set layout \"%s\""
				" doesn't support \":SN\" attribute -> refusing assignment\n",
				m_hpLayout->name().c_str());
		m_hpLayout=NULL;
		m_pStateBlock=NULL;
	}
#endif
}

template <class T>
inline T &WeakRecordSB::accessAttribute(UWORD aLocator) const
{
#if FE_CODEGEN<=FE_DEBUG
	return *((T *)((char *)(data()) + layout()->offsetTable()[aLocator]));
#else
	return *((T *)((char *)(data()) + rawLayout()->offsetTable()[aLocator]));
#endif
}

inline void *WeakRecordSB::rawAttribute(UWORD aLocator) const
{
#if FE_CODEGEN<=FE_DEBUG
	return (void *)((char *)(data()) + layout()->offsetTable()[aLocator]);
#else
	return (void *)((char *)(data()) + rawLayout()->offsetTable()[aLocator]);
#endif
}

inline void *WeakRecordSB::data(void) const
{
	return m_pStateBlock;
}

inline UWORD WeakRecordSB::idr(void) const
{
	return reinterpret_cast<UWORD>(m_pStateBlock);
}

inline const hp<LayoutSB> &WeakRecordSB::layout(void) const
{
	return m_hpLayout;
}

inline void WeakRecordSB::cacheSerialNumber(void)
{
	U32 index = m_hpLayout->serialIndex();
	m_serialOffset = m_hpLayout->offsetTable()[index];
	m_serialNumber = readSerialNumber();

#if FE_CODEGEN <= FE_DEBUG
	demandSerialNumber();
#endif
}


} /* namespace */

#endif /* __data_WeakRecordSB_h__ */

