// Semantics Consulting's Tyr Library
// http://www.semantics.org
//
// Copyright (c) 2003 by Stephen C. Dewhurst
//
// Permission to use, copy, modify, distribute, and sell this software
// for any purpose is hereby granted without fee, provided that the above
// copyright notice appears in all copies and that both that copyright
// notice and this permission notice appear in supporting documentation.
// The author makes no representations about the suitability of this
// software for any purpose.  It is provided "as is" without express
// or implied warranty.

//
//	Typelists and utilities.  Mostly taken from Andrei Alexandrescu's
//	Modern C++ Design, so I'll include the following for good measure:
////////////////////////////////////////////////////////////////////////////////
// The Loki Library
// Copyright (c) 2001 by Andrei Alexandrescu
// This code accompanies the book:
// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design 
//     Patterns Applied". Copyright (c) 2001. Addison-Wesley.
// Permission to use, copy, modify, distribute and sell this software for any 
//     purpose is hereby granted without fee, provided that the above copyright 
//     notice appear in all copies and that both that copyright notice and this 
//     permission notice appear in supporting documentation.
// The author or Addison-Welsey Longman make no representations about the 
//     suitability of this software for any purpose. It is provided "as is" 
//     without express or implied warranty.
////////////////////////////////////////////////////////////////////////////////

#ifndef TYPELIST_H
#define TYPELIST_H

#include "utils.h"

namespace Tyr {

//
//	type lists
//
//	These basic typelist functions are almost all taken from
//	Alexandrescu's Modern C++ Design, and their implementations
//	are almost identical to those.
//
template <class H, class T>
struct typelist {
	typedef H head;
	typedef T tail;
};

class null_typelist {};

template <typename T>
struct IsTypelist {
	enum { r = false };
};

template <typename H, class T>
struct IsTypelist< typelist<H,T> > {
	enum { r = true };
};

template <>
struct IsTypelist<null_typelist> {
	enum { r = true };
};

template <class TL>
struct printTL {
	static void print() {
		std::cout << typeid(typename TL::head).name() << ", ";
		printTL<typename TL::tail>::print();
	}
};
template <>
struct printTL<null_typelist> {
	static void print() {
		std::cout << "[end]" << std::endl;
	}
};

// Length
template <class TList>
struct Length;

template <class T, class U>
struct Length< typelist<T,U> > {
	enum { r = 1 + Length<U>::r };
};

template <>
struct Length<null_typelist> {
	enum { r = 0 };
};

// TypeAt
template <class TList, int i>
struct TypeAt;

template <class H, class T>
struct TypeAt<typelist<H,T>,0> {
	typedef H R;
};

template <class H, class T, int i>
struct TypeAt<typelist<H,T>,i> {
	typedef typename TypeAt<T,i-1>::R R;
};

// IndexOf
template <class TList, class T>
struct IndexOf;

template <class T>
struct IndexOf<null_typelist,T> {
	enum { r = -1 };
};

template <class Tail, class T>
struct IndexOf<typelist<T,Tail>,T> {
	enum {r = 0 };
};

template <class Head, class Tail, class T>
class IndexOf<typelist<Head,Tail>,T> {
	enum { temp = IndexOf<Tail,T>::r };
  public:
	enum { r = temp == -1 ? -1 : 1 + temp };
};

// Append
template <class TList, class T>
struct Append;

template <>
struct Append<null_typelist,null_typelist> {
	typedef null_typelist R;
};

template <class T>
struct Append<null_typelist,T> {
	typedef typelist<T,null_typelist> R;
};

template <class Head, class Tail>
struct Append < null_typelist,typelist<Head,Tail> > {
	typedef typelist<Head,Tail> R;
};

template <class Head, class Tail, class T>
struct Append<typelist<Head,Tail>,T> {
	typedef typelist<Head,typename Append<Tail,T>::R> R;
};

//	Erase
template <class TList,class T>
struct Erase;

template <class T>
struct Erase<null_typelist,T> {
	typedef null_typelist R;
};

template <class T,class Tail>
struct Erase<typelist<T,Tail>,T> {
	typedef Tail R;
};

template <class Head,class Tail, class T>
struct Erase<typelist<Head,Tail>,T> {
	typedef typelist<Head, typename Erase<Tail,T>::R> R;
};

//	Erase All
template <class TList, class T>
struct EraseAll;

template <class T>
struct EraseAll<null_typelist,T> {
	typedef null_typelist R;
};

template <class T, class Tail>
struct EraseAll<typelist<T,Tail>,T> {
	typedef typename EraseAll<Tail,T>::R R;
};

template <class Head, class Tail, class T>
struct EraseAll<typelist<Head,Tail>,T> {
	typedef typelist<Head,typename EraseAll<Tail,T>::R> R;
};

//	Erase Duplicates
template <class TList>
struct NoDuplicates;

template <>
struct NoDuplicates<null_typelist> {
	typedef null_typelist R;
};

template <class Head, class Tail>
class NoDuplicates<typelist<Head,Tail> > {
	typedef typename NoDuplicates<Tail>::R L1;
	typedef typename Erase<L1,Head>::R L2;
  public:
	typedef typelist<Head,L2> R;
};

#define MakeTypelist1(T1) Tyr::typelist<T1, Tyr::null_typelist>
#define MakeTypelist2(T1,T2) Tyr::typelist<T1, MakeTypelist1(T2) >
#define MakeTypelist3(T1,T2,T3) Tyr::typelist<T1, MakeTypelist2(T2,T3) >
#define MakeTypelist4(T1,T2,T3,T4) Tyr::typelist<T1, MakeTypelist3(T2,T3,T4) >
#define MakeTypelist5(T1,T2,T3,T4,T5) Tyr::typelist<T1, MakeTypelist4(T2,T3,T4,T5) >
#define MakeTypelist6(T1,T2,T3,T4,T5,T6) Tyr::typelist<T1, MakeTypelist5(T2,T3,T4,T5,T6) >
#define MakeTypelist7(T1,T2,T3,T4,T5,T6,T7) Tyr::typelist<T1, MakeTypelist6(T2,T3,T4,T5,T6,T7) >
#define MakeTypelist8(T1,T2,T3,T4,T5,T6,T7,T8) Tyr::typelist<T1, MakeTypelist7(T2,T3,T4,T5,T6,T7,T8) >
#define MakeTypelist9(T1,T2,T3,T4,T5,T6,T7,T8,T9) Tyr::typelist<T1, MakeTypelist8(T2,T3,T4,T5,T6,T7,T8,T9) >

} // namespace Tyr

#endif
