/*
* Copyright (c) 2001-2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  EAP and WLAN authentication protocols.
*
*/

/*
* %version: %
*/

#if !defined(_EAP_ARRAY_H_)
#define _EAP_ARRAY_H_

#include "eap_am_memory.h"
#include "eap_tools.h"
#include "eap_am_tools.h"
#include "eap_am_export.h"

#if defined(_WIN32) && !defined(__GNUC__)
	#pragma warning( disable : 4251 ) // needs to have dll-interface to be used by clients
#endif

#define EAP_ARRAY_SANITY_CHECK(this) \
	{ \
		EAP_ASSERT(((this)->m_head == 0 && (this)->m_tail == 0 && (this)->m_object_count == 0) \
				   || ((this)->m_head != 0 && (this)->m_tail != 0 && (this)->m_object_count != 0)); \
		\
		EAP_ASSERT(((this)->m_object_count == 0 && (this)->m_head == 0) \
				   || ((this)->m_object_count == 1 && (this)->m_head != 0 \
					   && (this)->m_head == (this)->m_tail && (this)->m_head->get_next_atom() == 0) \
				   || ((this)->m_object_count > 1 && (this)->m_head != 0 \
					   && (this)->m_head != (this)->m_tail && (this)->m_head->get_next_atom() != 0)); \
		\
		EAP_ASSERT(((this)->m_object_count == 0 && (this)->m_tail == 0) \
				   || ((this)->m_object_count != 0 && (this)->m_tail != 0 && (this)->m_tail->get_next_atom() == 0)); \
	}

//----------------------------------------------------------------------------------------

/**
 * The eap_array_atom_c is a template class for single object stored to eap_array_c array.
 * The array objects are stored as a linked list.
 * @param Type template parameter is the actual type which is stored.
 */
template <class Type>
class EAP_EXPORT eap_array_atom_c
{
private:

	/// This is pointer to the tools class. 
	abs_eap_am_tools_c * m_am_tools;

	/// This is the pointer to the actual object
	Type *m_data;

	/// Linked list pointer to the next atom
	eap_array_atom_c<Type> *m_next_atom;

	/// This flag indicates whether eap_array deletes the stored object when it is destroyed.
	bool m_free_atom;

public:
	
	/**
	 * The destructor deletes the object in this atom if necessary.	 
	 */	
	virtual ~eap_array_atom_c()
	{
		m_am_tools = 0;

		if (m_data != 0 
			&& m_free_atom == true)
		{
			delete m_data;
			m_data = 0;
		}
	}
	
	/**
	 * The constructor sets the values for the member variables
	 */	
	eap_array_atom_c(
		abs_eap_am_tools_c * const tools,
		Type * const p_data,
		const bool free_atom)
		: m_am_tools(tools)
		, m_data(p_data)	
		, m_next_atom(0)
		, m_free_atom(free_atom)
	{
	}

	//
	Type * const get_data()
	{
		return m_data;
	}

	//
	void set_data(Type * const p_data)
	{
		m_data = p_data;
	}

	eap_array_atom_c * const get_next_atom() const
	{
		return m_next_atom;
	}
	
	void set_next_atom(eap_array_atom_c<Type> * const next)
	{
		m_next_atom = next;
	}
};

//----------------------------------------------------------------------------------------

/**
  * The eap_array_c template class includes an array for type of Type objects.
  * The objects can be added, retrieved and their count can be queried.
  * @param Type template parameter is the actual type which is stored.
  */
template <class Type>
class EAP_EXPORT eap_array_c
{
private:
	//--------------------------------------------------

	/// This is pointer to the tools class. 
	abs_eap_am_tools_c * m_am_tools;

	/// Head pointer for the object atom linked list. 
	eap_array_atom_c<Type> *m_head;

	/// Tail pointer for the object atom linked list. 
	eap_array_atom_c<Type> *m_tail;

	/// This is the number of objects
	u32_t m_object_count;

	//--------------------------------------------------
protected:
	//--------------------------------------------------

	//--------------------------------------------------
public:
	//--------------------------------------------------

	/**
	 * The constructor initializes attributes using the passed parameter.
	 * @param tools is pointer to the tools class. @see abs_eap_am_tools_c.
	 */
	eap_array_c(
		abs_eap_am_tools_c * const tools)
		: m_am_tools(tools)
		, m_head(0)
		, m_tail(0)
		, m_object_count(0)
	{		
	}

	/**
	 * Destructor deletes all atoms.
	 */		
	virtual ~eap_array_c()
	{
		(void) reset();
		m_am_tools = 0;
	}

	// NOTE: This operator is NOT supported. This is unplemented prototype to cause linker error if assigment operator is used.
	eap_array_c<Type> &operator = (const eap_array_c<Type> &right_type_value);


	/**
	 * reset() deletes all atoms.
	 */		
	eap_status_e reset()
	{
		EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

		EAP_ARRAY_SANITY_CHECK(this);

		if (m_head == 0)
		{
			// The array is empty
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return eap_status_ok;
		}

		eap_array_atom_c<Type> *current;
		eap_array_atom_c<Type> *last;

		// Go through the linked list and delete all
		current = m_head;

		while (current != 0)
		{
			last = current;
			current = last->get_next_atom();
			delete last;
		}

		m_object_count = 0UL;
		m_head = 0;
		m_tail = 0;

		EAP_ARRAY_SANITY_CHECK(this);

		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return eap_status_ok;
	}

	/**
	 * The function add_object_to_begin() adds one new object to the begin of the array.
	 * @param object is pointer to the added object
	 * @param m_free_object is flag that indicates whether the object should be deleted by eap_array_c
	 */		
	eap_status_e add_object_to_begin(
		Type * const object,
		const bool m_free_object)
	{
		EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

		eap_array_atom_c<Type> *atom;

		if (object == 0)
		{
			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
		}

		EAP_ARRAY_SANITY_CHECK(this);

		atom = new eap_array_atom_c<Type>(m_am_tools, object, m_free_object);
		if (atom == 0
			|| object == 0)
		{
			if (m_free_object == true)
			{
				delete object;
			}

			EAP_ARRAY_SANITY_CHECK(this);

			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
		}

		// Check if this is the first item
		if (m_head == 0)
		{
			// Yes
			m_head = atom;
			m_tail = atom;
		}
		else 
		{
			// No. Add it to the begin.
			atom->set_next_atom(m_head);
			m_head = atom;
		}

		m_object_count++;

		EAP_ARRAY_SANITY_CHECK(this);

		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return eap_status_ok;
	}

	/**
	 * The function add_object() adds one new object to the array.
	 * @param object is pointer to the added object
	 * @param m_free_object is flag that indicates whether the object should be deleted by eap_array_c
	 */		
	eap_status_e add_object(
		Type * const object,
		const bool m_free_object)
	{
		EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

		eap_array_atom_c<Type> *atom;

		if (object == 0)
		{
			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
		}

		EAP_ARRAY_SANITY_CHECK(this);

		atom = new eap_array_atom_c<Type>(m_am_tools, object, m_free_object);
		if (atom == 0)
		{
			if (m_free_object == true)
			{
				delete object;
			}

			EAP_ARRAY_SANITY_CHECK(this);

			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
		}

		// Check if this is the first item
		if (m_head == 0)
		{
			// Yes
			m_head = atom;
			m_tail = atom;
		}
		else 
		{
			// No. Add it to the end.
			m_tail->set_next_atom(atom);
			m_tail = atom;
		}

		m_object_count++;

		EAP_ARRAY_SANITY_CHECK(this);

		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return eap_status_ok;
	}

	/**
	 * The get_object() function returns object based on the index given.
	 * @param index Indicates which object pointer is returned
	 * @return Object pointer
	 */			
	Type * const get_object(const u32_t index) const
	{
		EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
		EAP_ASSERT_ALWAYS(index < get_object_count());

		EAP_ARRAY_SANITY_CHECK(this);

		eap_array_atom_c<Type> *atom;
		Type *object = 0;
		atom = m_head;
		u32_t i = 0;

		// find the object needed by stepping through the array.
		while (atom != 0)
		{	
			// Is this the correct one?
			if (index == i)
			{
				// Yes.
				object = atom->get_data();
				break;
			}

			// Nope. Get the next one
			i++;
			atom = atom->get_next_atom();
		}

		EAP_ARRAY_SANITY_CHECK(this);

		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return object;
	}

	/**
	 * The remove_object() function removes object based on the index given.
	 * @param index Indicates which object pointer is returned
	 * @return Object pointer
	 */			
	eap_status_e remove_object(const u32_t index)
	{
		EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
		EAP_ASSERT_ALWAYS(index < get_object_count());

		EAP_ARRAY_SANITY_CHECK(this);

		eap_array_atom_c<Type> *atom;
		eap_array_atom_c<Type> *prev_atom = 0;
		atom = m_head;
		u32_t i = 0;
		eap_status_e status = eap_status_illegal_index;

		// find the object needed by stepping through the array.
		while (atom != 0)
		{	
			// Is this the correct one?
			if (index == i)
			{
				// Yes.
				if (index == 0
					&& prev_atom == 0)
				{
					// this is the first on the array.
					m_head = atom->get_next_atom();
					if (m_head == 0)
					{
						m_tail = 0;
					}
				}
				else
				{
					prev_atom->set_next_atom(atom->get_next_atom());
				}

				if (m_tail == atom)
				{
					// This is the last on the array.
					m_tail = prev_atom;
				}

				delete atom;
				--m_object_count;

				EAP_ARRAY_SANITY_CHECK(this);

				status = eap_status_ok;
				break;
			}

			// Nope. Get the next one
			i++;
			prev_atom = atom;
			atom = atom->get_next_atom();

		} // while()

		EAP_ARRAY_SANITY_CHECK(this);

		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	/**
	 * The get_object_count() function returns the number of objects in the array.
	 * @return number of objects
	 */			
	const u32_t get_object_count() const
	{
		EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return m_object_count;
	}

	/**
	 * Gets last object.
	 * @return Object pointer or null if array is empty.
	 */			
	Type * const get_last_object() const
	{
		EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

		EAP_ARRAY_SANITY_CHECK(this);

		// find the object needed by stepping through the array.
		if (get_object_count() > 0UL
			&& m_tail != 0)
		{	
			// Yes.
			Type * const object = m_tail->get_data();
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return object;
		}
		else
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return 0;
		}
	}

	//--------------------------------------------------
}; // class eap_array_c

//----------------------------------------------------------------------------------------

#endif //#if !defined(_EAP_ARRAY_H_)




// End.
