package Test::Stream::Event;
use strict;
use warnings;

use Scalar::Util qw/blessed/;
use Test::Stream::Carp qw/confess/;

use Test::Stream::ArrayBase(
    accessors => [qw/context created in_subtest/],
    no_import => 1,
);

sub import {
    my $class = shift;

    # Import should only when event is imported, subclasses do not use this
    # import.
    return if $class ne __PACKAGE__;

    my $caller = caller;
    my (%args) = @_;

    my $ctx_meth = delete $args{ctx_method};

    require Test::Stream::Context;
    require Test::Stream;

    # %args may override base
    Test::Stream::ArrayBase->apply_to($caller, base => $class, %args);
    Test::Stream::Context->register_event($caller, $ctx_meth);
    Test::Stream::Exporter::export_to(
        'Test::Stream',
        $caller,
        qw/OUT_STD OUT_ERR OUT_TODO/,
    );
}

sub init {
    confess("No context provided!") unless $_[0]->[CONTEXT];
}

sub encoding { $_[0]->[CONTEXT]->encoding }

sub extra_details {}

sub summary {
    my $self = shift;
    my $type = blessed $self;
    $type =~ s/^.*:://g;

    my $ctx = $self->context;

    my ($package, $file, $line) = $ctx->call;
    my ($tool_pkg, $tool_name)  = @{$ctx->provider};
    $tool_name =~ s/^\Q$tool_pkg\E:://;

    return (
        type => lc($type),

        $self->extra_details(),

        package => $package || undef,
        file    => $file,
        line    => $line,

        tool_package => $tool_pkg,
        tool_name    => $tool_name,

        encoding => $ctx->encoding || undef,
        in_todo  => $ctx->in_todo  || 0,
        todo     => $ctx->todo     || '',
        pid      => $ctx->pid      || 0,
        skip     => $ctx->skip     || '',
    );
}

1;

__END__

=head1 NAME

Test::Stream::Event - Base class for events

=head1 DESCRIPTION

Base class for all event objects that get passed through
L<Test::Stream>.

=head1 SYNOPSYS

    package Test::Stream::Event::MyEvent;
    use strict;
    use warnings;

    # This will make our class an event subclass, add the specified accessors,
    # inject a helper method into the context objects, and add constants for
    # all our fields, and fields we inherit.
    use Test::Stream::Event(
        accessors  => [qw/foo bar baz/],
        ctx_method => 'my_event',
    );

    # Chance to initialize some defaults
    sub init {
        my $self = shift;
        # no other args in @_

        $self->SUPER::init();

        $self->set_foo('xxx') unless defined $self->foo;

        # Events are arrayrefs, all accessors have a constant defined with
        # their index.
        $self->[BAR] ||= "";

        ...
    }

    # If your event produces TAP output it must define this method
    sub to_tap {
        my $self = shift;
        return (
            # Constants are defined at import, all are optional, and may appear
            # any number of times.
            [OUT_STD, $self->foo],
            [OUT_ERR, $self->bar],
            [OUT_STD, $self->baz],
        );
    }

    # This is your hook to add details to the summary fields.
    sub extra_details {
        my $self = shift;

        my @super_details = $self->SUPER::extra_details();

        return (
            @super_details,

            foo => $self->foo || undef,
            bar => $self->bar || '',
            ...
        );
    }

    1;

=head1 IMPORTING

=head2 ARGUMENTS

In addition to the arguments listed here, you may pass in any arguments
accepted by L<Test::Stream::ArrayBase>.

=over 4

=item ctx_method => $NAME

This specifies the name of the helper meth that will be injected into
L<Test::Stream::Context> to help generate your events. If this is not specified
it will use the lowercased last section of your package name.

=item base => $BASE_CLASS

This lets you specify an event class to subclass. B<THIS MUST BE AN EVENT
CLASS>. If you do not specify anything here then C<Test::Stream::Event> will be
used.

=item accessors => \@FIELDS

This lets you define any fields you wish to be present in your class. This is
the only way to define storage for your event. Each field specified will get a
read-only accessor with the same name as the field, as well as a setter
C<set_FIELD()>. You will also get a constant that returns the index of the
field in the classes arrayref. The constant is the name of the field in all
upper-case.

=back

=head2 SUBCLASSING

C<Test::Stream::Event> is added to your @INC for you, unless you specify an
alternative base class, which must itself subclass C<Test::Stream::Event>.

Events B<CAN NOT> use multiple inheritance in most cases. This is mainly
because events are arrayrefs and not hashrefs. Each subclass must add fields as
new indexes after the last index of the parent class.

=head2 CONTEXT HELPER

All events need some initial fields for construction. These fields include a
context, and some other state from construction time. The context object will
get helper methods for all events that fill in these fields for you. It is not
advised to ever construct an event object yourself, you should I<always> use
the context helper method.

=head1 EVENTS ARE ARRAY REFERENCES

Events are an arrayref. Events use L<Test::Stream::ArrayBase> under the hood to
generate accessors, constants, and field indexes. The key thing to take away
from this is that you cannot add attributes on the fly, you B<MUST> use
L<Test::Stream::Event> and/or L<Test::Stream::ArrayBase> to add fields.

If you need a place to store extar generic, and possibly unpredictable, data,
you should add a field and assign a hashref to it, then use that hashref to
store your mixed data.

=head1 METHODS

=over 4

=item $ctx = $e->context

Get a snapshot of the context as it was when this event was generated

=item $call = $e->created

Get the C<caller()> details from when the objects was created. This is usually
the call to the tool that generated the event such as C<Test::More::ok()>.

=item $bool = $e->in_subtest

Check if the event was generated within a subtest.

=item $encoding = $e->encoding

Get the encoding that was in effect when the event was generated

=item @details = $e->extra_details

Get an ordered key/value pair list of summary fields for the event. Override
this to add additional fields.

=item @summary = $e->summary

Get an ordered key/value pair list of summary fields for the event, including
parent class fields. In general you should not override this as it has a useful
(thought not depended upon) order.

=back

=head1 SUMMARY FIELDS

These are the fields that will be present when calling
C<< my %sum = $e->summary >>. Please note that the fields are returned as an
order key+pair list, they can be directly assigned to a hash if desired, or
they can be assigned to an array to preserver the order. The order is as it
appears below, B<NOT> alphabetical.

=over 4

=item type

The name of the event type, typically this is the lowercase form of the last
part of the class name.

=item package

The package that generated this event.

=item file

The file in which the event was generated, and to which errors should be attributed.

=item line

The line number on which the event was generated, and to which errors should be
attributed.

=item tool_package

The package that provided the tool that generated the event (example:
Test::More)

=item tool_name

The name of the sub that produced the event (examples: C<ok()>, C<is()>).

=item encoding

The encoding that should be used when printing the TAP output from this event.

=item in_todo

True if the event was generated while TODO was in effect.

=item todo

The todo message if the event was generated with TODO in effect.

=item pid

The PID in which the event was generated.

=item skip

The skip message if the event was generated via skip.

=back

=encoding utf8

=head1 SOURCE

The source code repository for Test::More can be found at
F<http://github.com/Test-More/test-more/>.

=head1 MAINTAINER

=over 4

=item Chad Granum E<lt>exodist@cpan.orgE<gt>

=back

=head1 AUTHORS

The following people have all contributed to the Test-More dist (sorted using
VIM's sort function).

=over 4

=item Chad Granum E<lt>exodist@cpan.orgE<gt>

=item Fergal Daly E<lt>fergal@esatclear.ie>E<gt>

=item Mark Fowler E<lt>mark@twoshortplanks.comE<gt>

=item Michael G Schwern E<lt>schwern@pobox.comE<gt>

=item 唐鳳

=back

=head1 COPYRIGHT

There has been a lot of code migration between modules,
here are all the original copyrights together:

=over 4

=item Test::Stream

=item Test::Stream::Tester

Copyright 2014 Chad Granum E<lt>exodist7@gmail.comE<gt>.

This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

See F<http://www.perl.com/perl/misc/Artistic.html>

=item Test::Simple

=item Test::More

=item Test::Builder

Originally authored by Michael G Schwern E<lt>schwern@pobox.comE<gt> with much
inspiration from Joshua Pritikin's Test module and lots of help from Barrie
Slaymaker, Tony Bowden, blackstar.co.uk, chromatic, Fergal Daly and the perl-qa
gang.

Idea by Tony Bowden and Paul Johnson, code by Michael G Schwern
E<lt>schwern@pobox.comE<gt>, wardrobe by Calvin Klein.

Copyright 2001-2008 by Michael G Schwern E<lt>schwern@pobox.comE<gt>.

This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

See F<http://www.perl.com/perl/misc/Artistic.html>

=item Test::use::ok

To the extent possible under law, 唐鳳 has waived all copyright and related
or neighboring rights to L<Test-use-ok>.

This work is published from Taiwan.

L<http://creativecommons.org/publicdomain/zero/1.0>

=item Test::Tester

This module is copyright 2005 Fergal Daly <fergal@esatclear.ie>, some parts
are based on other people's work.

Under the same license as Perl itself

See http://www.perl.com/perl/misc/Artistic.html

=item Test::Builder::Tester

Copyright Mark Fowler E<lt>mark@twoshortplanks.comE<gt> 2002, 2004.

This program is free software; you can redistribute it
and/or modify it under the same terms as Perl itself.

=back
