// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Bind a method to a variable that can be invoked as a function

class A {
  int a;

  static var func;

  A(this.a) { }

  static foo() { return 4; }

  bar() { return a; }

  int baz() { return a; }

  getThis() { return this.bar; }

  getNoThis() { return bar; }

  methodArgs(arg) { return arg + a; }

  selfReference () { return selfReference; }

  invokeBaz() { return (baz)(); }

  invokeBar(var obj) { return (obj.bar)(); }

  invokeThisBar() { return (this.bar)(); }

  implicitStaticRef() { return foo; }
}

class B {
  static foo() { return -1; }
}

class C {
  C() { }
  var f;
}

topLevel99() {
  return 99;
}

var topFunc;

class D extends A {
  D(a): super(a) { }
  getSuper() { return super.bar; }
}

class MethodBindingTest {
  static test() {

    // Create closure from global
    Expect.equals(99, topLevel99());
    Function f99 = topLevel99;
    Expect.equals(99, f99());

    // Invoke closure through a global
    topFunc = f99;
    Expect.equals(99, topFunc());   

    // Create closure from static method
    Function f4 = A.foo;
    Expect.equals(4, f4());

    // Create closure from instance method
    var o5 = new A(5);
    Function f5 = o5.bar;
    Expect.equals(5, f5());

    // Assign closure to field and invoke it
    var c = new C();
    c.f = () => "success";
    Expect.equals("success", c.f());

    // referencing instance method with explicit 'this' qualiier
    var o6 = new A(6);
    var f6 = o6.getThis();
    Expect.equals(6, f6());

    // referencing an instance method with no qualifier
    var o7 = new A(7);
    var f7 = o7.getNoThis();
    Expect.equals(7, f7());

    // bind a method that takes arguments
    var o8 = new A(8);
    Function f8 = o8.methodArgs;
    Expect.equals(9, f8(1));

    // Self referential method
    var o9 = new A(9);
    Function f9 = o9.selfReference;

    // invoking a known method as if it were a bound closure...
    var o10 = new A(10);
    Expect.equals(10, o10.invokeBaz());

    // invoking a known method as if it were a bound closure...
    var o11 = new A(11);
    Expect.equals(10, o11.invokeBar(o10));

    // invoking a known method as if it were a bound closure...
    var o12 = new A(12);
    Expect.equals(12, o12.invokeThisBar());

    // bind to a static variable with no explicit class qualifier
    var o13 = new A(13);
    Function f13 = o13.implicitStaticRef();
    Expect.equals(4, f13());

    var o14 = new D(14);
    Function f14 = o14.getSuper();
    Expect.equals(14, f14());

    // Assign static field to a function and invoke it.
    A.func = A.foo;
    Expect.equals(4, A.func());

  }

  static testMain() {
    test();
  }
}

main() {
  MethodBindingTest.testMain();
}
