
// Written in the D programming language.

/**
 * Templates with which to manipulate type tuples (also known as type lists).
 *
 * Some operations on type tuples are built in to the language,
 * such as TL[$(I n)] which gets the $(I n)th type from the
 * type tuple. TL[$(I lwr) .. $(I upr)] returns a new type
 * list that is a slice of the old one.
 *
 * References:
 *      Based on ideas in Table 3.1 from
 *      $(LINK2 http://www.amazon.com/exec/obidos/ASIN/0201704315/ref=ase_classicempire/102-2957199-2585768,
 *              Modern C++ Design),
 *       Andrei Alexandrescu (Addison-Wesley Professional, 2001)
 * Macros:
 *      WIKI = Phobos/StdTypeTuple
 * Copyright:
 *      Public Domain
 */

/* Author:
 *      Walter Bright, Digital Mars, www.digitalmars.com
 */

module std.typetuple;

/**
 * Creates a typetuple out of a sequence of zero or more types.
 * Example:
 * ---
 * import std.typetuple;
 * alias TypeTuple!(int, double) TL;
 *
 * int foo(TL td)  // same as int foo(int, double);
 * {
 *    return td[0] + cast(int)td[1];
 * }
 * ---
 *
 * Example:
 * ---
 * TypeTuple!(TL, char)
 * // is equivalent to:
 * TypeTuple!(int, double, char)
 * ---
 */
template TypeTuple(TList...)
{
    alias TList TypeTuple;
}

/**
 * Returns the index of the first occurrence of type T in the
 * sequence of zero or more types TList.
 * If not found, -1 is returned.
 * Example:
 * ---
 * import std.typetuple;
 * import std.stdio;
 *
 * void foo()
 * {
 *    writefln("The index of long is ",
 *          IndexOf!(long, TypeTuple!(int, long, double)));
 *    // prints: The index of long is 1
 * }
 * ---
 */
template IndexOf(T, TList...)
{
    static if (TList.length == 0)
        const int IndexOf = -1;
    else static if (is(T == TList[0]))
        const int IndexOf = 0;
    else
        const int IndexOf =
                (IndexOf!(T, TList[1 .. length]) == -1)
                        ? -1
                        : 1 + IndexOf!(T, TList[1 .. length]);
}

/**
 * Returns a typetuple created from TList with the first occurrence,
 * if any, of T removed.
 * Example:
 * ---
 * Erase!(long, int, long, double, char)
 * // is the same as:
 * TypeTuple!(int, double, char)
 * ---
 */
template Erase(T, TList...)
{
    static if (TList.length == 0)
        alias TList Erase;
    else static if (is(T == TList[0]))
        alias TList[1 .. length] Erase;
    else
        alias TypeTuple!(TList[0], Erase!(T, TList[1 .. length])) Erase;
}

/**
 * Returns a typetuple created from TList with the all occurrences,
 * if any, of T removed.
 * Example:
 * ---
 * alias TypeTuple!(int, long, long, int) TL;
 *
 * EraseAll!(long, TL)
 * // is the same as:
 * TypeTuple!(int, int)
 * ---
 */
template EraseAll(T, TList...)
{
    static if (TList.length == 0)
        alias TList EraseAll;
    else static if (is(T == TList[0]))
        alias EraseAll!(T, TList[1 .. length]) EraseAll;
    else
        alias TypeTuple!(TList[0], EraseAll!(T, TList[1 .. length])) EraseAll;
}

/**
 * Returns a typetuple created from TList with the all duplicate
 * types removed.
 * Example:
 * ---
 * alias TypeTuple!(int, long, long, int, float) TL;
 *
 * NoDuplicates!(TL)
 * // is the same as:
 * TypeTuple!(int, long, float)
 * ---
 */
template NoDuplicates(TList...)
{
    static if (TList.length == 0)
        alias TList NoDuplicates;
    else
        alias TypeTuple!(TList[0], NoDuplicates!(EraseAll!(TList[0], TList[1 .. length]))) NoDuplicates;
}

/**
 * Returns a typetuple created from TList with the first occurrence
 * of type T, if found, replaced with type U.
 * Example:
 * ---
 * alias TypeTuple!(int, long, long, int, float) TL;
 *
 * Replace!(long, char, TL)
 * // is the same as:
 * TypeTuple!(int, char, long, int, float)
 * ---
 */
template Replace(T, U, TList...)
{
    static if (TList.length == 0)
        alias TList Replace;
    else static if (is(T == TList[0]))
        alias TypeTuple!(U, TList[1 .. length]) Replace;
    else
        alias TypeTuple!(TList[0], Replace!(T, U, TList[1 .. length])) Replace;
}

/**
 * Returns a typetuple created from TList with all occurrences
 * of type T, if found, replaced with type U.
 * Example:
 * ---
 * alias TypeTuple!(int, long, long, int, float) TL;
 *
 * ReplaceAll!(long, char, TL)
 * // is the same as:
 * TypeTuple!(int, char, char, int, float)
 * ---
 */
template ReplaceAll(T, U, TList...)
{
    static if (TList.length == 0)
        alias TList ReplaceAll;
    else static if (is(T == TList[0]))
        alias TypeTuple!(U, ReplaceAll!(T, U, TList[1 .. length])) ReplaceAll;
    else
        alias TypeTuple!(TList[0], ReplaceAll!(T, U, TList[1 .. length])) ReplaceAll;
}

/**
 * Returns a typetuple created from TList with the order reversed.
 * Example:
 * ---
 * alias TypeTuple!(int, long, long, int, float) TL;
 *
 * Reverse!(TL)
 * // is the same as:
 * TypeTuple!(float, int, long, long, int)
 * ---
 */
template Reverse(TList...)
{
    static if (TList.length == 0)
        alias TList Reverse;
    else
        alias TypeTuple!(Reverse!(TList[1 .. length]), TList[0]) Reverse;
}

/**
 * Returns the type from TList that is the most derived from type T.
 * If none are found, T is returned.
 * Example:
 * ---
 * class A { }
 * class B : A { }
 * class C : B { }
 * alias TypeTuple!(A, C, B) TL;
 *
 * MostDerived!(Object, TL) x;  // x is declared as type C
 * ---
 */
template MostDerived(T, TList...)
{
    static if (TList.length == 0)
        alias T MostDerived;
    else static if (is(TList[0] : T))
        alias MostDerived!(TList[0], TList[1 .. length]) MostDerived;
    else
        alias MostDerived!(T, TList[1 .. length]) MostDerived;
}

/**
 * Returns the typetuple TList with the types sorted so that the most
 * derived types come first.
 * Example:
 * ---
 * class A { }
 * class B : A { }
 * class C : B { }
 * alias TypeTuple!(A, C, B) TL;
 *
 * DerivedToFront!(TL)
 * // is the same as:
 * TypeTuple!(C, B, A)
 * ---
 */
template DerivedToFront(TList...)
{
    static if (TList.length == 0)
        alias TList DerivedToFront;
    else
        alias TypeTuple!(MostDerived!(TList[0], TList[1 .. length]),
                        DerivedToFront!(ReplaceAll!(MostDerived!(TList[0], TList[1 .. length]),
                                                    TList[0],
                                                    TList[1 .. length]))) DerivedToFront;
}
