import std.stdio;
import nonnull;

class A
{
    public string output() { return "A"; }
}

class B : A
{
    public override string output() { return "B"; }
}

void NullsNotAllowedA(NN!A a)
{}

void NullsAllowedA(A a)
{}

void NullsNotAllowedB(NN!B b)
{}

void NullsAllowedB(B b)
{}

void main()
{
    A notNullA = new A();
    A isNullA = null;
    B notNullB = new B();
    B isNullB = null;
    NN!A nnToNA = toNN(new A());
    NN!B nnToNB = toNN(new B());
    
    /*
     * Initialise
     * Unfortunately for the below I couldn't figure out how to get
     * traits(compiles) to work
     */
    NN!A nnA0;  // Annoying! I am guessing when a struct is initialised
                // it does not call the invariant then? If it did then we
                // would atleast catch this at debug runtime ... right?
    //NN!A nnA1 = notNullA;   // Will not compile ... good
    //NN!A nnA2 = isNullA;   // Will not compile ... good
    //NN!A nnA3 = new A();   // Will not compile ... annoying
    //NN!A nnA4 = notNullB;   // Will not compile ... good
    //NN!A nnA5 = isNullB;   // Will not compile ... good
    //NN!A nnA6 = new B();   // Will not compile ... annoying
    //NN!A nnA7 = null;   // Will not compile ... good
    //NN!A nnA8 = toNN(null);   // Will not compile ... good
    NN!A nnA9 = toNN(notNullA);   // good
    ///NN!A nnA10 = toNN(isNullA);   // Good, Defers null check to runtime
    NN!A nnA11 = toNN(new A());   // Good, but annoying
    //NN!A nnA12 = toNN(notNullB);   // Will not compile ... annoying
    NN!A nnA13 = toNN!A(notNullB);   // Good.
    //NN!A nnA14 = toNN(isNullB);   // Will not compile ... annoying
    ///NN!A nnA15 = toNN!A(isNullB);   // Good, Defers null check to runtime
    //NN!A nnA16 = toNN(new B());   // Will not compile ... annoying
    NN!A nnA17 = toNN!A(new B());   // Good, but annoying
    A a1 = nnToNA;   // Good
    A a2 = nnToNB;   // Good
    //B b1 = nnToNA;   // Will not compile ... good
    B b1 = nnToNB;   // Good

    /*
     * Assignment
     */
    nnToNA = nnToNB;    // Good
    static assert(!__traits(compiles, nnToNB = nnToNA));    // Will not compile ... good
    static assert(!__traits(compiles, nnToNA = notNullA));   // Will not compile ... good
    static assert(!__traits(compiles, nnToNA = isNullA));   // Will not compile ... good
    static assert(!__traits(compiles, nnToNA = new A()));   // Will not compile ... annoying
    static assert(!__traits(compiles, nnToNA = notNullB));   // Will not compile ... good
    static assert(!__traits(compiles, nnToNA = isNullB));   // Will not compile ... good
    static assert(!__traits(compiles, nnToNA = new B()));   // Will not compile ... annoying
    static assert(!__traits(compiles, nnToNA = null));   // Will not compile ... good
    static assert(!__traits(compiles, nnToNA = toNN(null)));   // Will not compile ... good
    nnToNA = toNN(notNullA);   // Good, Defers null check to runtime
    static assert(__traits(compiles, nnToNA = toNN(isNullA)));   // Good, Defers null check to runtime
    nnToNA = toNN(new A());   // Good, but annoying
    nnToNA = toNN(notNullB);   // Good. Defers null check to runtime
    static assert(__traits(compiles, nnToNA = toNN(isNullB)));   // Good, Defers null check to runtime
    nnToNA = toNN(new B());   // Good, but annoying
    A nntoNA1 = nnToNA;

    /*
     * Usage
     */
    NN!A uA = toNN(new A());
    write("1: ");   writefln(uA.output);    // A
    NN!B uB = toNN(new B());
    write("2: ");     writefln(uB.output);    // B
    uA = uB;
    write("3: "); writefln(uA.output);    // A

    /*
     * Function Calls
     *
     * Unfortunately it seems opAssign doesn't kick in here
     * so with toNN you need to provide the type and
     * when assigning nonNull to normal references you need
     * to use the value property
     */
    static assert(!__traits(compiles, NullsNotAllowedA(notNullA)));   // Will not compile ... good
    static assert(!__traits(compiles, NullsNotAllowedA(isNullA)));   // Will not compile ... good
    static assert(!__traits(compiles, NullsNotAllowedA(new A())));   // Will not compile ... annoying
    static assert(!__traits(compiles, NullsNotAllowedA(notNullB)));   // Will not compile ... good
    static assert(!__traits(compiles, NullsNotAllowedA(isNullB)));   // Will not compile ... good
    static assert(!__traits(compiles, NullsNotAllowedA(new B())));   // Will not compile ... annoying
    static assert(!__traits(compiles, NullsNotAllowedA(null)));   // Will not compile ... good
    static assert(!__traits(compiles, NullsNotAllowedA(toNN(null))));   // Will not compile ... good
    NullsNotAllowedA(toNN(notNullA));   // Good
    static assert(__traits(compiles, NullsNotAllowedA(toNN(isNullA))));   // Good, Defers null check to runtime
    NullsNotAllowedA(toNN(new A()));   // Good, but annoying
    static assert(!__traits(compiles, NullsNotAllowedA(toNN(notNullB))));   // Will not compile ... annoying
    NullsNotAllowedA(toNN!A(notNullB));   // Good.
    static assert(!__traits(compiles, NullsNotAllowedA(toNN(isNullB))));   // Will not compile ... annoying
    static assert(__traits(compiles, NullsNotAllowedA(toNN!A(isNullB))));   // Good, Defers null check to runtime
    static assert(!__traits(compiles, NullsNotAllowedA(toNN(new B()))));   // Will not compile ... annoying
    NullsNotAllowedA(toNN!A(new B()));   // Good, but annoying
    NullsNotAllowedA(nnToNA);   // Good
    static assert(!__traits(compiles, NullsNotAllowedA(nnToNB)));   // Wont compile .... bad
    NullsNotAllowedA(toNN!A(nnToNB.value));   // Good, but horrible just horrible
    static assert(!__traits(compiles, NullsNotAllowedB(notNullA)));   // Will not compile ... good
    static assert(!__traits(compiles, NullsNotAllowedB(isNullA)));   // Will not compile ... good
    static assert(!__traits(compiles, NullsNotAllowedB(new A())));   // Will not compile ... annoying
    static assert(!__traits(compiles, NullsNotAllowedB(notNullB)));   // Will not compile ... good
    static assert(!__traits(compiles, NullsNotAllowedB(isNullB)));   // Will not compile ... good
    static assert(!__traits(compiles, NullsNotAllowedB(new B())));   // Will not compile ... annoying
    static assert(!__traits(compiles, NullsNotAllowedB(null)));   // Will not compile ... good
    static assert(!__traits(compiles, NullsNotAllowedB(toNN(notNullA))));   // Will not compile ... good
    static assert(!__traits(compiles, NullsNotAllowedB(toNN(isNullA))));   // Will not compile ... good
    static assert(!__traits(compiles, NullsNotAllowedB(toNN(new A()))));   // Will not compile ... good
    NullsNotAllowedB(toNN(notNullB));   // Good
    static assert(__traits(compiles, NullsNotAllowedB(toNN(isNullB))));   // Good, Defers null check to runtime
    NullsNotAllowedB(toNN(new B()));   // Good, but annoying

    NullsAllowedA(nnToNA.value);   // Good, but annoying
    NullsAllowedA(nnToNB.value);   // Good, but annoying
    static assert(!__traits(compiles, NullsAllowedB(nnToNA.value)));   // Will not compile ... good
    NullsAllowedB(nnToNB.value);   // Good, but annoying
}