#!/usr/bin/perl

use strict;
use lib "blib/lib";

use Regexp::Common qw /RE_comment_ALL/;
use t::Common qw /run_new_tests ww/;

use warnings;

BEGIN {$^W = 0 if $[ < 5.006;}



# 1. tokens for single line comments.
# 2. start/end tokens for multi-line comments.
# 3. list of languages this applies to.
my @data = do {
    no warnings;
    (
        [[qw {//}]                  =>
         [[qw {/* */}]]             =>
         [qw {C++ C# Cg ECMAScript FPL Java JavaScript}],
        ],
        [[qw {#}]                   =>
         [[qw {/* */}]]             =>
         [qw {Nickle}],
        ],
        [[qw {//}]                  =>
         [[qw !{ }!], [qw !(* *)!]] =>
         [[qw /Pascal Delphi/], [qw /Pascal Free/], [qw /Pascal GPC/]],
        ],
        [[qw {!}]                   =>
         [[qw {/* */}]]             =>
         [qw {PEARL}]
        ],
        [[qw {# //}]                =>
         [[qw {/* */}]]             =>
         [qw {PHP}]
        ],
        [[qw {--}]                  =>
         [[qw {/* */}]]             =>
         [qw {PL/SQL}]
        ]
    );
};

# Grab the single line tokens.
my @s_tokens = do {my %h; grep {!$h {$_} ++} map {@{$$_ [0]}} @data};

# Grab the multiline line tokens.
my @mo_tokens = do {my %h; grep {!$h {$_} ++}
                           map {map {$$_ [0]} @{$$_ [1]}} @data};
my @mc_tokens = do {my %h; grep {!$h {$_} ++}
                           map {map {$$_ [1]} @{$$_ [1]}} @data};

my @comments = ("", "This is a comment", "This is a\nmultiline comment",
                "\n", (map {" $_ "} @s_tokens, @mo_tokens, @mc_tokens));
my @no_eol   = grep {!/\n/} @comments;

# Targets, and test suites.
my %targets;
my @tests;
my @bad;

# Tests for the single line comments (including failures).
foreach my $token (@s_tokens) {
    my $key  = "single_$token";
    my $fkey = "single_fail_$token";

    $targets {$key} = {
        list   => \@no_eol,
        query  => sub {$token . $_ [0] . "\n"},
    };

    my @s_bad;
    # No trailing newline.
    push @s_bad => map {"$token$_"} @no_eol;
    # Double newline.
    push @s_bad => map {"$token$_\n\n"} @no_eol;
    # Double comment.
    push @s_bad => map {"$token$_\n" x 2} @no_eol;
    # Leading garbage.
    push @s_bad => map {ww (1, 10) . "$token$_\n"} @no_eol;
    # Trailing garbage.
    push @s_bad => map {"$token$_\n" . ww (1, 10)} @no_eol;

    $targets {$fkey} = {
        list   => \@s_bad,
    };
}

# No leading token.
$targets {single_fail} = {
    list => [map {"$_\n"} @no_eol],
};

# Tests for the multi line comments (including failures).
for (my $i = 0; $i < @mc_tokens; $i ++) {
    my $start = $mo_tokens [$i];
    my $end   = $mc_tokens [$i];

    my $key   = "multi_${start}_$end";
    my $key2  = "multi2_${start}_$end";
    my $fkey  = "multi_fail_${start}_$end";

    my @list  = grep {!/\Q$end/} @comments;

    $targets {$key} = {
        list    => \@list,
        query   => sub {$start . $_ [0] . $end},
    };
    # Doubling the start token should be ok.
    $targets {$key2} = {
        list    => \@list,
        query   => sub {$start . $start . $_ [0] . $end},
    };

    my @m_bad;
    # No starting token.
    push @m_bad => map {"$_$end"} @comments;
    # No ending token.
                         # Hack for old versions of Perl. The regexes will
                         # work there, but it just takes too long to test them.
    push @m_bad => map {"$start$_"} $] < 5.006 ? @no_eol : @comments;
    # Double the comment.
    push @m_bad => map {"$start$_$end" x 2} @comments;
    # Leading garbage.
    push @m_bad => map {ww (1, 5) . "$start$_$end"} @comments;
    # Trailing garbage.
    push @m_bad => map {"$start$_$end" . ww (1, 5)} @comments;

    $targets {$fkey} = {
        list    => \@m_bad,
    };
}

# No tokens at all.
$targets {fail} = {
    list => \@comments,
};

foreach my $data (@data) {
    my ($singles, $doubles, $langs) = @$data;
    my %s_seen;
    my %m_seen;
    $s_seen {$_}              = 1 for @$singles;
    $m_seen {join "_" => @$_} = 1 for @$doubles;

    my   @passes   =   map {"single_$_"} @$singles;
    push @passes   =>  map {join _ => "multi",  @$_} @$doubles;
    push @passes   =>  map {join _ => "multi2", @$_} @$doubles;
    my   @failures =   map {"single_$_"} grep {!$s_seen {$_}} @s_tokens;
    push @failures =>  map {"single_fail_$_"} @$singles;
    push @failures => "single_fail";
    # Multiline comments using *other* delimiters.
    push @failures =>  map  {join _ => "multi", $_}
                       grep {!$m_seen {$_}}
                       map  {join _ => $mo_tokens [$_], $mc_tokens [$_]}
                             0 .. $#mo_tokens;
    push @failures =>  map  {join _ => "multi_fail", @$_} @$doubles;
    push @failures => "fail";

    foreach my $lang (@$langs) {
        my $name = ref $lang ? join "/" => @$lang : $lang;
        my $sub  = ref $lang ? join "_" => "RE_comment", @$lang
                             : "RE_comment_$lang";
        $sub =~ s/\W/X/g;
        my $re   = ref $lang ? $RE {comment} {$$lang [0]} {$$lang [1]}
                             : $RE {comment} {$lang};

        no strict 'refs';
        push @tests => {
            name    => $name,
            re      => $re,
            sub     => \&{$sub},
            pass    => \@passes,
            fail    => \@failures,
        };
    }
}

run_new_tests tests        => \@tests,
              targets      => \%targets,
              version_from => 'Regexp::Common::comment',


__END__
