/*	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_RecordArrayAV_h__
#define __data_RecordArrayAV_h__

namespace fe
{

#ifdef FE_RA_INDEX
struct hash_av
{ UWORD operator()(const UWORD &u) const
	{ return u; } };

struct eq_av
{ bool operator()(UWORD u1, UWORD u2) const
	{ return u1 == u2; } };
typedef HashMap<UWORD, IWORD, hash_av, eq_av>		t_av_map;
#endif

/**	@brief Homogeneous collection of Records

	@ingroup data

	An array of Records, or more precisely an array of state block pointers
	all pointing to state blocks of the same layout.  Therefore only a single
	reference to the layout itself is stored.  Therefore this record array
	object consumes less memory than an array of record objects.
	*/
class FE_DL_EXPORT RecordArrayAV : public Counted
{
	public:
		RecordArrayAV(void);
		RecordArrayAV(sp<LayoutAV> spLayout);
		RecordArrayAV(sp<LayoutAV> spLayout, UWORD aCount);
		RecordArrayAV(const RecordArrayAV &other);
virtual	~RecordArrayAV(void);

		RecordArrayAV	&operator=(const RecordArrayAV &other);
		UWORD			idr(IWORD index);
		/**	Return the length of the array. */
		IWORD			length(void);
		/** Return the Layout. */
		sp<Layout>		layout(void);
		/** Return a raw pointer to the Layout.  Mainly intended for Accessor
			for speed. */
		Layout			*rawLayout(void) const;
		/**	Add the given record to the array.	If the record array already
			has records and the Layout is different than for @em record
			the record will not be added and false will be returned.
			On success true is returned and if @em index is not NULL the index
			of the added record is return in @em index . */
		bool			add(const RecordAV &record, IWORD *index = NULL);
		bool			add(sp<RecordArrayAV> spRA, IWORD start, IWORD size);

		/** create and add a record of an already set Layout.  This avoids
			the overhead of checking for duplicates. */
		bool			addCreate(void);
		/** Remove the given record from the array.  On success return
			true and if @em index is provided return the index of the removed
			record.  If the record is not in the array return false. */
		bool			remove(const RecordAV &record, IWORD *index = NULL);
		/** Return the record at the given index. */
		RecordAV		getRecord(IWORD index);
		/// Return a non-persistant record for the given index
		WeakRecordAV	getWeakRecord(IWORD index);
		/**	Clear the record array. */
		void			clear(void);
		/**	Return true if @em record is in the record array.  Otherwise return
			false. */
		bool			find(const RecordAV &record);

		void			set(WeakRecordAV &record, IWORD index);
		void			set(RecordAV &record, IWORD index);

		void			setLayout(const sp<LayoutAV>& rspLayout);

						/// @brief Remove any invalid weak references
		void			prune(void);

						/** @internal

							@brief Choose weak referencing

							This should only be set by RecordGroup at
							construction. */
		void			setWeak(BWORD weak);

						/// @brief Return TRUE if using weak referencing
		BWORD			isWeak(void) const		{ return m_weak; }

						/** Remove record at given index.  The array is kept
							contiguous by moving the last element into the
							the given index after removal.	Therefore, in
							an iteration that involves deletion it is best
							to iterate backwards through the array. */
		void			remove(IWORD index);

		template <class T>
		T				&accessAttribute(UWORD aLocator, UWORD aIndex) const;
	private:
		/**	Return the arrayindex at the given index. */
		UWORD			arrayindex(IWORD index) const;
		template <class T>
		T				&accessAttributeUnsafe(UWORD aLocator, UWORD aIndex) const;
	private:
		void			grow(void);
		void			copy(const RecordArrayAV &other);

		I32				readSerialNumber(IWORD index) const;

		void			trackBlock(U32 index);
		void			untrackBlock(U32 index);

		void			acquireAV(IWORD index);
		void			releaseAV(IWORD index);

	private:
		sp<LayoutAV>	m_spLayout;
		std::vector<UWORD>	m_pArrayIndex;
		std::vector<IWORD>	m_pSN;
		BWORD			m_weak;
		UWORD			m_serialLocator;
#ifdef FE_RA_INDEX
		t_av_map		m_av_map;
#endif

#if FE_COUNTED_TRACK
	public:
const	String&	name(void) const
				{	return m_spLayout.isValid()?
						m_spLayout->name(): Counted::name(); }
const	String	verboseName(void) const
				{	return "RecordArray " +
							String(m_weak? "(weak) ": "") + name(); }
#endif
};

template <class T>
inline T &RecordArrayAV::accessAttribute(UWORD aLocator, UWORD aIndex) const
{
	//return *(T *)(m_spLayout->m_attributeVector[m_spLayout->m_locatorTable[aLocator]]->raw_at(arrayindex(aIndex)));
	return *(T *)(m_spLayout->m_attributeVector[m_spLayout->m_locatorTable[aLocator]]->raw_at(m_pArrayIndex[aIndex]));
}

template <class T>
inline T &RecordArrayAV::accessAttributeUnsafe(UWORD aLocator, UWORD aIndex) const
{
	return *(T *)(m_spLayout->m_attributeVector[m_spLayout->m_locatorTable[aLocator]]->raw_at(m_pArrayIndex[aIndex]));
}

class PtrRecordArrayAVInfo : public BaseType::Info
{
	public:
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 void RecordArrayAV::setWeak(BWORD weak)
{
	m_weak = weak;
#if FE_COUNTED_TRACK
	//* propagate name change
	sp<RecordArrayAV> spRA(this);
#endif
}

inline void RecordArrayAV::set(WeakRecordAV &record, IWORD index)
{
	record.set(m_pArrayIndex[index], m_spLayout);
}

inline void RecordArrayAV::set(RecordAV &record, IWORD index)
{
	record.set(m_pArrayIndex[index], m_spLayout);
}

inline IWORD RecordArrayAV::length(void)
{
	return m_pArrayIndex.size();
}

// TODO: AJW: if we choose just AV, go back to returing const reference of LayoutAV
inline sp<Layout> RecordArrayAV::layout(void)
{
	return m_spLayout;
}

inline RecordAV RecordArrayAV::getRecord(IWORD index)
{
	RecordAV record;
	UWORD ai=arrayindex(index);
	if(ai != LayoutAV::arrayindexNone)
	{
		record.set(ai, m_spLayout);
	}
	return record;
}

inline WeakRecordAV RecordArrayAV::getWeakRecord(IWORD index)
{
	WeakRecordAV record;
	UWORD ai=arrayindex(index);
	if(ai != LayoutAV::arrayindexNone)
	{
		record.set(ai, m_spLayout);
	}
	return record;
}

inline void RecordArrayAV::acquireAV(IWORD index)
{
	if(!m_weak)
	{
		m_spLayout->acquireArrayIndex(arrayindex(index));
	}
	trackBlock(index);
}

inline void RecordArrayAV::releaseAV(IWORD index)
{
	untrackBlock(index);
	if(!m_weak)
	{
		m_spLayout->releaseArrayIndex(arrayindex(index));
	}
}

inline I32 RecordArrayAV::readSerialNumber(IWORD index) const
{
	if(m_serialLocator!=LayoutAV::locatorNone)
	{
		return accessAttributeUnsafe<int>(m_serialLocator, index);
	}
	else
	{
		return -1;
	}
}


inline UWORD RecordArrayAV::idr(IWORD index)
{
	return m_spLayout->idr(arrayindex(index));
}

inline void RecordArrayAV::untrackBlock(U32 index)
{
#if FE_COUNTED_TRACK
	untrackReference(this, this);
#endif
}

inline void RecordArrayAV::trackBlock(U32 index)
{
#if FE_COUNTED_TRACK
	trackReference(this,this,"RecordArray");
#endif
}


inline UWORD RecordArrayAV::arrayindex(IWORD index) const
{
	if(m_weak && m_pSN[index]>=0 && m_pSN[index]!=readSerialNumber(index))
	{
		feLog("RecordArray::data weak element %d is not valid (%d vs %d)\n",
				index,m_pSN[index],readSerialNumber(index));
#if FE_COUNTED_TRACK
		feLog("  name \"%s\"\n",name().c_str());
#endif

		*(const_cast<UWORD *>(&(m_pArrayIndex[index])))=LayoutAV::arrayindexNone;
		return LayoutAV::arrayindexNone;
	}
	return m_pArrayIndex[index];
}


};

#endif /* __data_RecordArrayAV_h__ */

