/** \file
 * \brief Declaration and implementation of HashArray class.
 *
 * \author Carsten Gutwenger
 *
 * \par License:
 * This file is part of the Open Graph Drawing Framework (OGDF).
 *
 * \par
 * Copyright (C)<br>
 * See README.md in the OGDF root directory for details.
 *
 * \par
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * Version 2 or 3 as published by the Free Software Foundation;
 * see the file LICENSE.txt included in the packaging of this file
 * for details.
 *
 * \par
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * \par
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, see
 * http://www.gnu.org/copyleft/gpl.html
 */

#pragma once

#include <ogdf/basic/Hashing.h>

namespace ogdf {


//! Indexed arrays using hashing for element access.
/**
 * @ingroup containers
 *
 * @tparam I is the index type.
 * @tparam E is the element type.
 * @tparam H is the hash function type. Optional; its default uses the class DefHashFunc.
 *
 * A hashing array can be used like a usual array but has a general
 * index type.
 *
 * The hashing array is only defined for a subset <I>I<SUB>def</SUB></I> of the
 * index set (set of all elements of the index type). At construction, this set
 * is empty. Whenever an index is assigned an element, this index is added
 * to <I>I<SUB>def</SUB></I>. There are also method for testing if an index
 * is defined (is in <I>I<SUB>def</SUB></I>).
 *
 * <H3>Example</H3>
 * The following code snippet demonstrates how to use a hashing array. First,
 * the example inserts elements into a hashing array simulating a tiny
 * German&ndash;English dictionary, then it prints some elements via array
 * access, and finally it iterates over all defined indices and prints the
 * dictionary entries. We use a the const reference \a Hc, since we want to
 * avoid that array access for undefined indices creates these elements.
 *
 * \code
 *   HashArray<string,string> H("[undefined]");
 *   const HashArray<string,string> &Hc = H;
 *
 *   H["Hund"]  = "dog";
 *   H["Katze"] = "cat";
 *   H["Maus"]  = "mouse";
 *
 *   std::cout << "Katze:   " << Hc["Katze"]   << std::endl;
 *   std::cout << "Hamster: " << Hc["Hamster"] << std::endl;
 *
 *   std::cout << "\nAll elements:" << std::endl;
 *   HashConstIterator<string,string> it;
 *   for(it = Hc.begin(); it.valid(); ++it)
 *     std::cout << it.key() << " -> " << it.info() << std::endl;
 * \endcode
 *
 * The produced output is as follows:
 * \code
 * Katze:   cat
 * Hamster: [undefined]
 *
 * All elements:
 * Hund -> dog
 * Maus -> mouse
 * Katze -> cat
 * \endcode
 */
template<class I, class E, class H = DefHashFunc<I>>
class HashArray : private Hashing<I, E, H> {
	E m_defaultValue; //! The default value for elements.

public:
	//! The type of const-iterators for hash arrays.
	using const_iterator = HashConstIterator<I, E, H>;

	//! Creates a hashing array; the default value is the default value of the element type.
	HashArray() : Hashing<I, E, H>() { }

	//! Creates a hashing array with default value \p defaultValue.
	explicit HashArray(const E& defaultValue, const H& hashFunc = H())
		: Hashing<I, E, H>(256, hashFunc), m_defaultValue(defaultValue) { }

	//! Copy constructor.
	HashArray(const HashArray<I, E, H>& A)
		: Hashing<I, E, H>(A), m_defaultValue(A.m_defaultValue) { }

	//! Returns an iterator to the first element in the list of all elements.
	HashConstIterator<I, E, H> begin() const { return Hashing<I, E, H>::begin(); }

	//! Returns the number of defined indices (= number of elements in hash table).
	int size() const { return Hashing<I, E, H>::size(); }

	//! Returns if any indices are defined (= if the hash table is empty)
	int empty() const { return Hashing<I, E, H>::empty(); }

	//! Returns the element with index \p i.
	const E& operator[](const I& i) const {
		HashElement<I, E>* pElement = Hashing<I, E, H>::lookup(i);
		if (pElement) {
			return pElement->info();
		} else {
			return m_defaultValue;
		}
	}

	//! Returns a reference to the element with index \p i.
	E& operator[](const I& i) {
		HashElement<I, E>* pElement = Hashing<I, E, H>::lookup(i);
		if (!pElement) {
			pElement = Hashing<I, E, H>::fastInsert(i, m_defaultValue);
		}
		return pElement->info();
	}

	//! Returns true iff index \p i is defined.
	bool isDefined(const I& i) const { return Hashing<I, E, H>::member(i); }

	//! Undefines index \p i.
	void undefine(const I& i) { Hashing<I, E, H>::del(i); }

	//! Assignment operator.
	HashArray<I, E, H>& operator=(const HashArray<I, E, H>& A) {
		m_defaultValue = A.m_defaultValue;
		Hashing<I, E, H>::operator=(A);
		return *this;
	}

	//! Undefines all indices.
	void clear() { Hashing<I, E, H>::clear(); }
};

}
