role Perl6::Metamodel::InvocationProtocol {
#?if !moar
    has int $!has_invocation_attr;
    has $!invocation_attr_class;
    has str $!invocation_attr_name;

    has int $!has_invocation_handler;
    has $!invocation_handler;

    my $default_invoke_handler;
    method set_default_invoke_handler($h) {
        $default_invoke_handler := $h;
    }

    method set_invocation_attr($obj, $class, str $name) {
        $!has_invocation_attr   := 1;
        $!invocation_attr_class := $class;
        $!invocation_attr_name  := $name;
    }

    method set_invocation_handler($obj, $handler) {
        $!has_invocation_handler := 1;
        $!invocation_handler     := $handler;
    }

    method has_invocation_attr($obj) { $!has_invocation_attr }
    method invocation_attr_class($obj) { $!invocation_attr_class }
    method invocation_attr_name($obj) { $!invocation_attr_name }

    method has_invocation_handler($obj) { $!has_invocation_handler }
    method invocation_handler($obj) { $!invocation_handler }

    method compose_invocation($obj) {
        # Check if we have a invoke, and if so install
        # the default invocation forwarder. Otherwise, see if we or
        # a parent has an invocation attr.
        if $obj.HOW.archetypes.composable {
            # We special case roles by using only default handler
            nqp::setinvokespec($obj, nqp::null(), nqp::null_s(),
                $default_invoke_handler);
        }
        else {
            my $pcmeth := self.find_method($obj, 'CALL-ME', :no_fallback(1));
            if nqp::defined($pcmeth) {
                nqp::die('Default invocation handler is not invokable')
                    unless nqp::isinvokable($default_invoke_handler);
                nqp::setinvokespec($obj, nqp::null(), nqp::null_s(),
                    $default_invoke_handler);
            }
            else {
                for self.mro($obj) -> $class {
                    if nqp::can($class.HOW, 'has_invocation_attr') {
                        if $class.HOW.has_invocation_attr($class) {
                            nqp::setinvokespec($obj,
                                $class.HOW.invocation_attr_class($class),
                                $class.HOW.invocation_attr_name($class),
                                nqp::null());
                            last;
                        }
                    }
                    if nqp::can($class.HOW, 'has_invocation_handler') {
                        if $class.HOW.has_invocation_handler($class) {
                            nqp::setinvokespec($obj,
                                nqp::null(), nqp::null_s(),
                                $class.HOW.invocation_handler($class));
                            last;
                        }
                    }
                }
            }
        }
    }
#?endif
}

# vim: expandtab sw=4
