
import core.stdc.stdio;

/*******************************************************/

class Outer
{
    int o;
    static int os = 3;

    static class StaticInner
    {
        int si;

        int foo()
        {
            return si + os;
        }
    }

    class Inner
    {
        int i;

        int foo(int m)
        {
            return i * o + m;
        }

        this(int k)
        {
            i = k + 7;
            os = 6;
        }
    }

    Inner bar()
    {
        return new Inner(17);
    }
}

void test1()
{
    Outer p = new Outer();

    assert(p.o == 0);
    assert(p.os == 3);
    p.o = 9;

    Outer.StaticInner s = new Outer.StaticInner;
    s.si = 10;
    assert(s.foo() == 13);

    Outer.Inner q = p.bar();
    assert(q.i == 24);
    assert(q.foo(8) == 24*9+8);
}

/*******************************************************/

class C1
{
    int c1;

    this()
    {
        c1 = 2;
    }

    class C2
    {
        class C3
        {
            int c3;

            this(int n)
            {
                c3 = n + c1 + c2;
            }
        }

        int c2;

        C3 foo()
        {
            return new C3(8);
        }

        this(int k)
        {
            c2 = k + 7;
        }
    }

    C2 bar()
    {
        return new C2(17);
    }
}

void test2()
{
   C1 p = new C1();
   assert(p.c1 == 2);

   C1.C2 q = p.bar();
   assert(q.c2 == 24);

   C1.C2.C3 r = q.foo();
   assert(r.c3 == 2+24+8);
}

/*******************************************************/

class C3
{
    int c1;

    this()
    {
        c1 = 2;
    }

    class B { }

    class C2 : B
    {
        int c2;

        this(int k)
        {
            c2 = c1 + k + 7;
        }
    }

    C2 bar()
    {
        return new C2(17);
    }
}

void test3()
{
   C3 q = new C3();
   assert(q.c1 == 2);
   C3.C2 p = q.bar();
   assert(p.c2 == 26);
}


/*******************************************************/

void test4()
{
    int m = 3;

    class C
    {
        void foo()
        {
            assert(m == 3);
        }
    }

    void bar()
    {
        assert(m == 3);
    }

    bar();
    C c = new C;
    c.foo();
}

/*******************************************************/

void test5()
{
    int m = 3;

    void bar()
    {
        assert(m == 3);
    }

    class C
    {
        void foo()
        {
            assert(m == 3);
            bar();
        }
    }

    bar();
    C c = new C;
    c.foo();
}


/*******************************************************/

void test6()
{
    int m = 3;

    void bar()
    {
        assert(m == 3);
    }

    void abc()
    {
        class C
        {
            void foo()
            {
                assert(m == 3);
                bar();
            }
        }

        bar();
        C c = new C;
        c.foo();
    }

    abc();
}

/*******************************************************/

void test7()
{
    int m = 3;

    void bar()
    {
        assert(m == 3);
    }

    void ghi()
    {
        void abc()
        {
            class C
            {
                void foo()
                {
                    assert(m == 3);
                    bar();
                }
            }

            bar();
            C c = new C;
            c.foo();
        }

        abc();
    }

    ghi();
}

/*******************************************************/

void test8()
{
    int m = 3;

    void bar()
    {
        assert(m == 3);
    }

    void ghi()
    {
        void abc()
        {
            class C
            {
                void foo()
                {
                    void fooey()
                    {
                        assert(m == 3);
                        bar();
                    }

                    fooey();
                }
            }

            bar();
            C c = new C;
            c.foo();
        }

        abc();
    }

    ghi();
}

/*******************************************************/

class C9
{
    int m;

    void ccc()
    {
        assert(m == 3);
    }

    this()
    {
        m = 3;
    }

    class D
    {
        int n;

        this() { n = 4; }

        void abc()
        {
            assert(m == 3);
            ccc();
        }
    }

    D def()
    {
        return new D;
    }
}


void test9()
{
    C9 c = new C9;
    assert(c.m == 3);
    c.ccc();

    C9.D d = c.def();
    assert(d.n == 4);
    d.abc();
}

/*******************************************************/

void test10()
{
    int m = 3;

    class C
    {
        void foo()
        {
            assert(m == 3);
        }
    }

    void abc()
    {
        C c = new C;
        c.foo();
    }

    abc();
}

/*******************************************************/

void test11()
{
    int m = 3;

    class C
    {
        void foo()
        {
            assert(m == 3);
        }
    }

    void abc()
    {
        void def()
        {
            C c = new C;
            c.foo();
        }

        def();
    }

    abc();
}

/*******************************************************/

void test12()
{
    int m = 3;

    class C
    {
        void foo()
        {
            assert(m == 3);
        }

        void abc()
        {
            assert(m == 3);
            C c = new C;
            c.foo();
        }
    }

    C d = new C;
    d.abc();
}

/*******************************************************/

void test13()
{
    int m = 3;

    class C
    {
        void foo()
        {
            assert(m == 3);
        }

        void abc()
        {
            void def()
            {
                assert(m == 3);
                C c = new C;
                c.foo();
            }

            def();
        }
    }

    C d = new C;
    d.abc();
}

/*******************************************************/

class C14
{
    void foo()
    {
        assert(0);
    }
}

void test14()
{
    int m = 3;

    C14 c = new class C14
        {
            override void foo()
            {
                printf("in inner class\n");
                assert(m == 3);
            }
        };

    c.foo();
}

/*******************************************************/

class C15
{
    void foo()
    {
        assert(0);
    }
}

void test15()
{
    int m = 3;

    C15 c = new class(1,2) C15
        {
            this(int i, int j)
            {
                printf("in inner class ctor\n");
                assert(i == 1);
                assert(j == 2);
            }

            override void foo()
            {
                printf("in inner class\n");
                assert(m == 3);
            }
        };

    c.foo();
}

/*******************************************************/

class C16
{
    int w = 3;

    void iterator()
    {
        class Foo
        {
            void current()
            {
                assert(w == 3);
            }
        }

        Foo f = new Foo();
        f.current();
    }
}

void test16()
{
    C16 s = new C16();

    s.iterator();
}

/*******************************************************/

class Owner
{

    this()
    {
        n = new Nested(this);
    }

    class Nested
    {
        this(Owner owner)
        {
            m = owner;
        }

        Owner foo()
        {
            return m;
        }

        Owner m;
    }

    Nested n;
}

class OwnerDerived : Owner
{
}

void test17()
{
    OwnerDerived o = new OwnerDerived();

    assert(o.n.foo() is o);
}

/*******************************************************/

class Foo18
{
    class Bar
    {
        void doSayHello()
        {
            printf("Betty\n");
            sayHello();
        }
    }
    Bar bar;

    void sayHello()
    {
        printf("Hello\n");
    }
}

class Foo182 : Foo18
{
    this()
    {
        bar = new Bar();
    }
}

void test18()
{
    Foo182 foo = new Foo182();
    printf("This should print Hello:\n");

    foo.bar.doSayHello();
}

/*******************************************************/

class Foo19
{
    class Bar
    {
        void doSayHello()
        {
            printf("Betty\n");
            sayHello();
        }
    }
    Bar bar;

    void sayHello()
    {
        printf("Hello\n");
    }

    this()
    {
        bar = new Bar();
    }
}

class Foo192 : Foo19
{
}

void test19()
{
    Foo192 foo = new Foo192();
    printf("This should print Hello:\n");

    foo.bar.doSayHello();
}

/*******************************************************/

class Outer20
{
    int a;

    class Inner
    {
        int foo()
        {
            return a;
        }
    }
}

void test20()
{
    Outer20 o = new Outer20;
    o.a = 3;
    Outer20.Inner oi = o.new Inner;
    assert(oi.foo() == 3);
}

/*******************************************************/

class Foo21{}

static if (is(typeof(new class Foo21{}))) {}

void test21()
{
}

/*******************************************************/

class Outer22
{
    class Inner
    {
        Outer22 foo()
        {
            return this.outer;
        }
    }

    void bar()
    {
        Inner i = new Inner;
        assert(this == i.foo());
    }
}

void test22()
{
    Outer22 o = new Outer22;
    o.bar();
}

/*******************************************************/

class Adapter23
{
    void func() { }
}

class Foo23
{
    class AnonAdapter : Adapter23
    {
    }

    void bar()
    {
        Adapter23 a = cast( Adapter23 )( new AnonAdapter() );
    }
}

void test23()
{
    Foo23 f = new Foo23();
    f.bar();
    Adapter23 a = cast( Adapter23 )( f.new AnonAdapter() );
    auto aa = f.new AnonAdapter();
    Adapter23 ab = cast( Adapter23 )(aa);

}

/*******************************************************/

class I24
{
    public abstract void callI();
}

C24 c24;

class C24
{
    private int index;
    void foo()
    {
        printf( "ok, this = %p\n", this);
        assert(this == c24);
    }
    I24 bar()
    {
        auto i = new class() I24
        {
            override public void callI()
            {
                foo();
            }
        };
        printf("bar.this = %p\n", this);
        printf("  i.this = %p\n", (cast(void**)i)[2]);
        assert(*cast(void**)&c24 == (cast(void**)i)[2]);
        return i;
    }
}

void test24()
{
    c24 = new C24;
    printf("c = %p\n", c24);
    auto i = c24.bar();
    i.callI();
}

/*******************************************************/

struct S7426
{
    static struct Inner
    {
        int x;
        alias typeof(Inner.tupleof) T;
    }
}

/*******************************************************/
// https://issues.dlang.org/show_bug.cgi?id=14046

class A14046
{
    class NestedA { }
}

class B14046 : A14046
{
    int field;

    class NestedB : NestedA
    {
        void foo()
        {
            this.outer.field = 1;  // ok <- disallowed

            //(cast(B14046)this.outer).field = 1;  // workaround
        }
    }
}

void test14046()
{
    auto b = new B14046();
    auto nb = b.new NestedB();
    assert(b.field == 0);
    nb.foo();
    assert(b.field == 1);
}

/*******************************************************/
// https://issues.dlang.org/show_bug.cgi?id=15839

class AnimatedProgress15839(bool makeClosure)
{
    static AnimatedProgress15839 saveThis;

    interface Runnable {}

    static class GC
    {
        this(AnimatedProgress15839 ap)
        {
            assert(ap is saveThis);
        }
    }

    void start()
    {
        assert(this is saveThis);

        static if (makeClosure) int a;

        auto r = new class Runnable
        {
            void run()
            {
                static assert(is(typeof(this.outer) == AnimatedProgress15839));
                assert(this.outer is saveThis);

                GC gc = new GC(this.outer);

                static if (makeClosure) int b = a;
            }
        };
        r.run();
    }
}

void test15839()
{
    auto ap1 = new AnimatedProgress15839!false();
    ap1.saveThis = ap1;
    ap1.start();

    auto ap2 = new AnimatedProgress15839!true();
    ap2.saveThis = ap2;
    ap2.start();
}

/*******************************************************/

int main()
{

    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7();
    test8();
    test9();
    test10();

    test11();
    test12();
    test13();

    test14();
    test15();
    test16();
    test17();
    test18();
    test19();
    test20();

    test21();
    test22();
    test23();
    test24();

    test14046();
    test15839();

    printf("Success\n");
    return 0;
}
