#ifndef FSFW_CONTAINER_ARRAYLIST_H_
#define FSFW_CONTAINER_ARRAYLIST_H_

#include "../returnvalues/HasReturnvaluesIF.h"
#include "../serialize/SerializeAdapter.h"
#include "../serialize/SerializeIF.h"

/**
 * @brief 	A List that stores its values in an array.
 * @details
 * The underlying storage is an array that can be allocated by the class
 * itself or supplied via ctor.
 *
 * @ingroup container
 */
template<typename T, typename count_t = uint8_t>
class ArrayList {
	template<typename U, typename count> friend class SerialArrayListAdapter;
public:
	static const uint8_t INTERFACE_ID = CLASS_ID::ARRAY_LIST;
	static const ReturnValue_t FULL = MAKE_RETURN_CODE(0x01);

	/**
	 * This is the allocating constructor.
	 * It allocates an array of the specified size.
	 * @param maxSize
	 */
	ArrayList(count_t maxSize) :
		size(0), maxSize_(maxSize), allocated(true) {
		entries = new T[maxSize];
	}

	/**
	 * This is the non-allocating constructor
	 *
	 * It expects a pointer to an array of a certain size and initializes
	 * itself to it.
	 *
	 * @param storage the array to use as backend
	 * @param maxSize size of storage
	 * @param size size of data already present in storage
	 */
	ArrayList(T *storage, count_t maxSize, count_t size = 0) :
		size(size), entries(storage), maxSize_(maxSize), allocated(false) {
	}

	/**
	 * Copying is forbiden by declaring copy ctor and copy assignment deleted
	 * It is too ambigous in this case.
	 * (Allocate a new backend? Use the same? What to do in an modifying call?)
	 */
	ArrayList(const ArrayList& other) = delete;
	const ArrayList& operator=(const ArrayList& other) = delete;

	/**
	 * Number of Elements stored in this List
	 */
	count_t size;


	/**
	 * Destructor, if the allocating constructor was used, it deletes the array.
	 */
	virtual ~ArrayList() {
		if (allocated) {
			delete[] entries;
		}
	}

	/**
	 * An Iterator to go trough an ArrayList
	 *
	 * It stores a pointer to an element and increments the
	 * pointer when incremented itself.
	 */
	class Iterator {
	public:
		/**
		 * Empty ctor, points to NULL
		 */
		Iterator(): value(0) {}

		/**
		 * Initializes the Iterator to point to an element
		 *
		 * @param initialize
		 */
		Iterator(T *initialize) {
			value = initialize;
		}

		/**
		 * The current element the iterator points to
		 */
		T *value;

		Iterator& operator++() {
			value++;
			return *this;
		}

		Iterator operator++(int) {
			Iterator tmp(*this);
			operator++();
			return tmp;
		}

		Iterator& operator--() {
			value--;
			return *this;
		}

		Iterator operator--(int) {
			Iterator tmp(*this);
			operator--();
			return tmp;
		}

		T& operator*() {
			return *value;
		}

		const T& operator*() const {
			return *value;
		}

		T *operator->() {
			return value;
		}

		const T *operator->() const {
			return value;
		}
	};

	friend bool operator==(const ArrayList::Iterator& lhs,
			const ArrayList::Iterator& rhs) {
		return (lhs.value == rhs.value);
	}

	friend bool operator!=(const ArrayList::Iterator& lhs,
			const ArrayList::Iterator& rhs) {
		return not (lhs.value == rhs.value);
	}

	/**
	 * Iterator pointing to the first stored elmement
	 *
	 * @return Iterator to the first element
	 */
	Iterator begin() const {
		return Iterator(&entries[0]);
	}

	/**
	 * returns an Iterator pointing to the element after the last stored entry
	 *
	 * @return Iterator to the element after the last entry
	 */
	Iterator end() const {
		return Iterator(&entries[size]);
	}

	T & operator[](count_t i) const {
		return entries[i];
	}

	/**
	 * The first element
	 *
	 * @return pointer to the first stored element
	 */
	T *front() {
		return entries;
	}

	/**
	 * The last element
	 *
	 * does not return a valid pointer if called on an empty list.
	 *
	 * @return pointer to the last stored element
	 */
	T *back() {
		return &entries[size - 1];
		//Alternative solution
		//return const_cast<T*>(static_cast<const T*>(*this).back());
	}

	const T* back() const{
		return &entries[size-1];
	}

	/**
	 * The maximum number of elements this List can contain
	 *
	 * @return maximum number of elements
	 */
	size_t maxSize() const {
		return this->maxSize_;
	}

	/**
	 * Insert a new element into the list.
	 *
	 * The new element is inserted after the last stored element.
	 *
	 * @param entry
	 * @return
	 *          -@c FULL if the List is full
	 *          -@c RETURN_OK else
	 */
	ReturnValue_t insert(T entry) {
		if (size >= maxSize_) {
			return FULL;
		}
		entries[size] = entry;
		++size;
		return HasReturnvaluesIF::RETURN_OK;
	}

	/**
	 * clear the List
	 *
	 * This does not actually clear all entries, it only sets the size to 0.
	 */
	void clear() {
		size = 0;
	}

	count_t remaining() {
		return (maxSize_ - size);
	}

protected:
	/**
	 * pointer to the array in which the entries are stored
	 */
	T *entries;
	/**
	 * remembering the maximum size
	 */
	size_t maxSize_;

	/**
	 * true if the array was allocated and needs to be deleted in the destructor.
	 */
	bool allocated;
};



#endif /* FSFW_CONTAINER_ARRAYLIST_H_ */