package Java::Swing::Generate::Listener;
use strict; use warnings;

use Exporter;
our @ISA    = ('Exporter');
our @EXPORT = qw(generate_listener generate_module);

# This package makes Java programs which implement listeners.  Call generate
# with a hash like this:
{
    'listener' => 'TreeWillExpandListener',
    'methods' => [
        {
            'name' => 'treeWillExpand',
            'type' => 'javax.swing.event.TreeExpansionEvent',
            'throws' => 'javax.swing.tree.ExpandVetoException'
        },
        {
            'name' => 'treeWillCollapse',
            'type' => 'javax.swing.event.TreeExpansionEvent',
            'throws' => 'javax.swing.tree.ExpandVetoException'
        }
    ],
    'full_name' => 'javax.swing.event.TreeWillExpandListener'
}
# Alternatively, you can generate a batch of listeners using decomp_listeners
# and all_listeners in this directory.

sub generate_listener {
    my $hash = shift;
    my $retval = generate_imports($hash);
    $retval   .= generate_header($hash);
    foreach my $event (@{$hash->{events}}) {
        $retval .= generate_method($hash, $event);
    }
    $retval .= generate_footer();

    return $retval;
}

sub generate_footer {
    return "}\n";
}

sub generate_imports {
    my $hash = shift;
    my %extras;

    # The first section here was abandoned after I recalled that importing
    # is not necessary when fully qualified names will be used.

    # This will not handle event methods which throw multiple exception types.
    # foreach my $event (@{$hash->{events}}) {
    #     $extras{$event->{type}}++   if defined ($event->{type});
    #     $extras{$event->{throws}}++ if defined ($event->{throws});
    # }
    # my $extra_imports = "";
    # foreach my $extra (keys %extras) {
    #     $extra_imports .= "import $extra;\n";
    # }

    return << "EOIMPORT";
import $hash->{package}.$hash->{name};
import org.perl.inline.java.*;

EOIMPORT
}

sub generate_header {
    my $hash = shift;
    my $name = $hash->{name};

    return << "EODECL";
public class      Perl${name}
       extends    InlineJavaPerlCaller
       implements ${name}
{
    String sender;
    String callbacks;

    public Perl${name}(String sender, String callbacks)
        throws InlineJavaException
    {
        this.sender    = sender;
        this.callbacks = callbacks;
    }

EODECL
}

sub generate_method {
    my $hash          = shift;
    my $event         = shift;
    my $type          = $event->{type};
    my $sub_name      = $hash->{'sub'};
    my $listener      = $hash->{name};
    my $method_name   = $event->{name};
    my $throws_clause = $event->{throws} ? "throws $event->{throws}" : "";

    return << "EOMETHOD";
    public void $method_name($type event) $throws_clause {
        try {
            CallPerl(
                "Java::Swing::$listener",
                "$sub_name",
                new Object[] {sender, callbacks, "$method_name", event}
            );
        }
        catch (InlineJavaPerlException ijpe) {
            System.err.println("couldn't call perl ijpe: " + ijpe);
        }
        catch (InlineJavaException ije) {
            System.err.println("couldn't call perl ije: " + ije);
        }
    }
EOMETHOD
}

sub generate_module {
    my $hash      = shift;
    my $perl_pack = "Java::Swing::$hash->{name}";
    my $listener  = "Perl$hash->{name}";
    my $perl_pack_plus_listener
                  = "$perl_pack\::$listener";
    my $sub_name  = $hash->{'sub'};

    my $retval = << "EOModule";
package $perl_pack;
use strict; use warnings;

use Inline Java      => 'STUDY',
           AUTOSTUDY => 1,
           STUDY     => ['$listener'];

my \%callbacks;
my \%listeners;

sub connect {
    my \$invocant       = shift;  # not used
    my \$sender         = shift;
    my \$callbacks      = shift;
    my \$object_name    = ref \$sender;
    my \$callbacks_name = ref \$callbacks;
    my \$listener       =
        $perl_pack_plus_listener->new(
            \$object_name,
            \$callbacks_name
        );

    \$callbacks{\$object_name}{\$callbacks_name} = \$callbacks;
    \$listeners{\$object_name}{\$callbacks_name} = \$listener;

    no strict;
    \$sender->add$hash->{name}(\$listener);
}

sub disconnect {
    my \$invocant       = shift;  # not used
    my \$sender         = shift;
    my \$callbacks      = shift;
    my \$object_name    = ref \$sender;
    my \$callbacks_name = ref \$callbacks;

    undef \$callbacks{\$object_name}{\$callbacks_name};
    \$sender->remove$hash->{name}(
        \$listeners{\$object_name}{\$callbacks_name}
    );
}

sub $sub_name {
    my \$sender_name      = shift;
    my \$callbacks_name   = shift;
    my \$triggered_method = shift;
    my \$event            = shift;

    my \$methods = \$callbacks{\$sender_name}{\$callbacks_name}
        or die "No registered $sub_name callback for \$sender_name\\n";
    my \$method = \$methods->{\$triggered_method} || sub {};

    no strict;
    \$method->(\$sender, \$event);
}

1;
EOModule
}

1;
