///////////////////////////////////////////////////////////////////////////////
/// @file deFunctors.hpp
///
/// @brief templated functor classes
///
/// @author Assassin
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Dec 2002
/// @author Assassin
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#ifndef DEFUNCTORS_HPP
#define DEFUNCTORS_HPP

#include "deGlobalTypes.hpp"

typedef class IdeWorldObject IdeWorldObject;

template <typename T>
void deSwap(T & a, T& b)
{
	T t = a;
	a = b;
	b = t;
}

template <class T>
class deDelPtrsFunctor
{
public:
	inline void operator() (T* &ptr) const
	{
		delete ptr;
		ptr = NULL;
	}
};

template <class T>
class deReleasePtrsFunctor
{
public:
	inline void operator() (T* &ptr) const
	{
		if (ptr)
			ptr->Release();
		ptr = NULL;
	}
};

template <class T>
class deBoundsCheckedArray
{
	T* m_array;
	int m_length;
public:
	deBoundsCheckedArray() {m_array = 0; m_length = 0; }
	~deBoundsCheckedArray() {}
	void operator=(T* refarray) { m_array = refarray;}
	void SetLength(int size) {m_length = size;}
	int  GetLength() {return m_length;}
	inline T& operator[](int index)
	{
		DE_ASSERT(m_array && index >= 0 && index < m_length);
		return m_array[index];
	}
};

// when using this class, you still have to release the pointer when you're all done
// it just helps when you are passing pointers around between functions
template <class T=IdeWorldObject>
class deWorldObjectSmartPtr
{
	T* m_ptr;
public:
	explicit deWorldObjectSmartPtr() { m_ptr = 0; }
	explicit deWorldObjectSmartPtr(const deWorldObjectSmartPtr<T>&ref)
		{ m_ptr = ref.m_ptr; if (m_ptr) m_ptr->Claim(); }
	~deWorldObjectSmartPtr() { if (m_ptr) m_ptr->Release(); }
	T* operator=(T* new_ptr)
	{
		if (new_ptr) new_ptr->Claim();
		if (m_ptr) m_ptr->Release();
		m_ptr = new_ptr;
		return m_ptr;
	}
	T* operator ->() { return m_ptr; }
	T** operator &() { return &m_ptr; }
	operator T* () { return m_ptr; }
};

class HashEdge // for use in a deTHashFunctor object
{
	DWORD m_Value;
public:
	inline HashEdge(u16 index1, u16 index2)
	{
		if (index1 < index2)
			m_Value = index1 | (index2<<16);
		else
			m_Value = index2 | (index1<<16);
	}
	inline HashEdge(const HashEdge & ref) : m_Value(ref.m_Value) { }
	inline deBoolean operator==(const HashEdge & rhs)
	{
		return (m_Value == rhs.m_Value);
	}
	inline DWORD Hash() const
	{
		return m_Value;
	}
	inline u16 index1() const
	{
		return (u16)((m_Value >> 16) & 0x0000ffff);
	}
	inline u16 index2() const
	{
		return (u16)(m_Value & 0x0000ffff);
	}
};

class HashEdge32 // for use in a deTHashFunctor object
{
	long m_Index1;
	long m_Index2;
public:
	inline HashEdge32(long index1, long index2)
	{
		if (index1 < index2)
		{
			m_Index1 = index1;
			m_Index2 = index2;
		}
		else
		{
			m_Index1 = index2;
			m_Index2 = index1;
		}
	}
	inline HashEdge32(const HashEdge32 & ref) : m_Index1(ref.m_Index1), m_Index2(ref.m_Index2) { }
	inline deBoolean operator==(const HashEdge32 & rhs)
	{
		return (m_Index1 == rhs.m_Index1 && m_Index2 == rhs.m_Index2);
	}
	inline DWORD Hash() const
	{
		return ((m_Index1 << 16) | (m_Index1 >> 16)) ^ m_Index2;
	}
	inline long index1() const
	{
		return m_Index1;
	}
	inline long index2() const
	{
		return m_Index2;
	}
};


template <typename Y, class Eval, typename T>
class deLazyEval
{
private:
	T m_ID;
	Y* m_Eval;
public:
	deLazyEval(T id) : m_ID(id), m_Eval(0) {}
	~deLazyEval(){}	

	operator Y* ()
	{	if (!m_Eval)
			m_Eval = Eval(m_ID);
		return m_Eval;
	}
};

/// iterates over an iterator range, executing func on each element unconditionally
template <typename IT, class F>
void deIter_App(IT first, const IT& last, F & func)
{
	for (; first != last; ++first)
		func(*first);
}
/// iterates over an iterator range, executing func on each element, until func(e) evaluates to false
/// @return deTRUE if all func(e) returned true for all elements, deFALSE otherwise
template <typename IT, class F>
deBoolean deIter_All(IT first, const IT& last, F & func)
{
	for (; first != last; ++first)
	{
		if (!func(*first))
			return deFALSE;
	}
	return deTRUE;
}
/// iterates over an iterator range, until it finds the matching value, and erases it from container
template <typename IT, class T, class C>
void deFindErase(IT first, const IT& last, const T& val, C & container)
{
	for (; first != last; ++first)
	{
		if (*first == val)
		{
			container.erase(first);
			return;
		}
	}
}
/// iterates over an iterator range, until it finds the matching value, and returns that iterator,
/// or returns last if it was not found
template <typename IT, class T>
IT deFind(IT first, const IT& last, const T& val)
{
	for (; first != last; ++first)
	{
		if (*first == val)
			return first;
	}
	return last;
}

/// calculates the mean and variance of a set of data, one element at a time
template <typename T>
class mean_variance
{
	// s^2 = ( sum (x_i - x_1)^2 - ( sum (x_i - x_1) )^2 / n ) / (n-1)
private:
	T mMean, mVar, mSum1, mSum2;
	const T mFirst;
	void operator=(const mean_variance&) { throw; }
public:
	mean_variance(const T & first) : mFirst(first), mMean(0), mVar(0), mSum1(0), mSum2(0) {}
	void inline operator()(const T& elem)
	{
		T temp = elem - mFirst;
		mSum1 += temp*temp;
		mSum2 += temp;
	}
	T mean(long num_elems)
	{
		T temp = mSum2 / num_elems;
		temp += mFirst;
		return temp;
	}
	T var(long num_elems)
	{
		T temp = (mSum2*mSum2)/num_elems;
		temp = (mSum1 - temp)/(num_elems - 1);
		return temp;
	}
};

#endif

