
template Tuple(T...)
{
    alias T Tuple;
}

// All conditions are alias parameters, and must contain a template
//
//     template IsConditionMet(T)
//
// with two fields: const bool Met, and const char[] Reason (but the second is only required if the first is false).
// These fields can be forwarded fields with implicit template properties.
//
// For the purposes of generating conforming types, only standard conditions are allowed.

template Concept(Conditions...)
{
    const char[] ConditionType = "Concept";

    alias FindAll!("Method", Conditions) MethodConditions;
    alias FindAll!("Concept", Conditions) ConceptConditions;

    alias Conditions AllConditions;
    template IsConditionMet(T)
    {
        alias CheckAllConditions!(T, Conditions) IsConditionMet;
    }
    template CheckConditions(T)
    {
        static assert(IsConditionMet!(T).Met, IsConditionMet!(T).Reason);
    }

    const char[] MemberList = GenerateMembers!(MethodConditions);

    interface impl // : GenerateInheritanceList!(ConceptConditions) // This should work when bug #586 is fixed
    {
        mixin(MemberList);
    }
}

template Method(RetType, char[] name, ParamTypes...)
{
    const char[] ConditionType = "Method";

    const char[] Signature = RetType.stringof ~ " " ~ name ~ ParamTypes.stringof;

    template IsConditionMet(T)
    {
        char[] dummy; // This is here as a workaround for bug #1113

        mixin(`const bool isMet = is(typeof(T.` ~ name ~ `(` ~ ConvertToText!(ParamTypes) ~ `)) : RetType);`);
        static if (isMet)
            const bool Met = true;
        else
        {
            const bool Met = false;
            const char[] Reason = "Type '" ~ T.stringof ~ "' has no method with signature '" ~ Signature ~ "'.";
        }
    }
}

template GenerateInheritanceList(Concepts...)
{
    static if (Concepts.length == 0)
        alias Tuple!() GenerateInheritanceList;
    else
    {
        alias Concepts[0] MyConcept;
        alias MyConcept.impl TheImpl;
        alias Tuple!(TheImpl, GenerateInheritanceList!(Concepts[1..$])) GenerateInheritanceList;
    }
}


template GenerateMembers(Methods...)
{
    static if (Methods.length == 0)
        const char[] GenerateMembers = "";
    else
        const char[] GenerateMembers = Methods[0].Signature ~ ";\n" ~ GenerateMembers!(Methods[1..$]);
}

template FindAll(char[] type, Conditions...)
{
    static if (Conditions.length == 0)
        alias Tuple!() FindAll;
    else static if (Conditions[0].ConditionType == type)
        alias Tuple!(Conditions[0], FindAll!(type, Conditions[1..$])) FindAll;
    else
        alias FindAll!(type, Conditions[1..$]) FindAll;
}

template ConvertToText(ParamTypes...)
{
    static if (ParamTypes.length == 0)
        const char[] ConvertToText = "";
    else static if (ParamTypes.length == 1)
        const char[] ConvertToText = ParamTypes[0].stringof ~ ".init";
    else
        const char[] ConvertToText = ParamTypes[0].stringof ~ ".init, " ~ ConvertToText!(ParamTypes[1..$]);
}

template CheckAllConditions(T, Conditions...)
{
    static if (Conditions.length == 0)
        alias CheckInfo!(true, "") CheckAllConditions;
    else static if (Conditions[0].IsConditionMet!(T).Met)
        alias CheckAllConditions!(T, Conditions[1..$]) CheckAllConditions;
    else
        alias CheckInfo!(false, Conditions[0].IsConditionMet!(T).Reason) CheckAllConditions;
}

// A compile-time 'struct'
template CheckInfo(bool met, char[] reason)
{
    const bool Met = met;
    const char[] Reason = reason;
}



///////////////////////////////// TESTING //////////////////////
alias Concept!(   // Defines a concept which requires two member methods:
    Method!(char[], "testMethod", int),    // char[] testMethod(int)
    Method!(char[], "anotherMethod", bool) // char[] anotherMethod(bool)
              ) TestConcept;

alias Concept!(   // Defines a concept which
    TestConcept,  //     inherits another concept
    Method!(char[], "andAnotherMethod", double)  // requires the member method char[] andAnotherMethod(double)
              ) InheritedConcept;

// Examples which can be 
interface MyInterface
{
    char[] testMethod(int val);
}

interface MySecondInterface : MyInterface
{
    char[] anotherMethod(bool);
}

interface MyThirdInterface : MySecondInterface
{
    char[] andAnotherMethod(double);
}


interface MyFourthInterface : MyInterface
{
    char[] andAnotherMethod(double);
}

void main()
{
//    alias TestConcept.CheckConditions!(MyInterface) TheCheck;             // Fails, and caught
//    alias InheritedConcept.CheckConditions!(MyThirdInterface) TheCheck2;  // Works
//    alias InheritedConcept.CheckConditions!(MySecondInterface) TheCheck3; // Fails, and caught
//    alias InheritedConcept.CheckConditions!(MyFourthInterface) TheCheck4; // Fails, and caught
}

void ExampleFunc(T)(T t)
{
    alias TestConcept.CheckConditions!(T) TheCheck;
    char[] foo = t.testMethod(1);
//    dchar[] foo2 = t.anotherMethod(true); // Mistake -- but caught by __ExampleFunc_test
}

alias ExampleFunc!(TestConcept.impl) __ExampleFunc_test;

