// Copyright (c) 2017, 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.

/// An object that uses [noSuchMethod] to dynamically handle invocations
/// (property getters, property setters, and method invocations).
abstract class ProxyObject {}

/// A function reference that, when invoked, will forward the invocation back
/// to a [ProxyObject].
///
/// This is used when a caller accesses a method on a [ProxyObject] via the
/// method's getter. In these cases, the caller will receive a [MethodProxy]
/// that allows delayed invocation of the method.
class MethodProxy {
  /// The object on which the method was retrieved.
  ///
  /// This will be the target object when this method proxy is invoked.
  final ProxyObject _proxyObject;

  /// The name of the method in question.
  final Symbol _methodName;

  /// Creates a new [MethodProxy] that, when invoked, will invoke the method
  /// identified by [methodName] on the specified target [object].
  MethodProxy(ProxyObject object, Symbol methodName)
      : _proxyObject = object,
        _methodName = methodName;

  @override
  dynamic noSuchMethod(Invocation invocation) {
    if (invocation.isMethod && invocation.memberName == #call) {
      // The method is being invoked. Capture the arguments, and invoke the
      // method on the proxy object. We have to synthesize an invocation, since
      // our current `invocation` object represents the invocation of `call()`.
      return _proxyObject.noSuchMethod(new _MethodInvocationProxy(
        _methodName,
        invocation.positionalArguments,
        invocation.namedArguments,
      ));
    }
    return super.noSuchMethod(invocation);
  }
}

// TODO(tvolkert): remove (https://github.com/dart-lang/sdk/issues/28706)
class _MethodInvocationProxy extends Invocation {
  _MethodInvocationProxy(
    this.memberName,
    this.positionalArguments,
    this.namedArguments,
  );

  @override
  final Symbol memberName;

  @override
  final List<dynamic> positionalArguments;

  @override
  final Map<Symbol, dynamic> namedArguments;

  @override
  final bool isMethod = true;

  @override
  final bool isGetter = false;

  @override
  final bool isSetter = false;
}
