#!/usr/bin/env perl
use strict;
use warnings;
use IO::Socket;
use Data::Dumper;
use File::Basename;
use lib File::Basename::dirname(__FILE__)."/../../../lib";
use lib File::Basename::dirname(__FILE__).'/../..';

use Test::More tests => 828;

use UR::Namespace::Command::Old::DiffRewrite;

my $c = "UR::Namespace::Command::Old::DiffRewrite";

# The messaging methods print to the filehandle $Command::stderr, which defaults
# to STDERR.  Redefine it so the messages are printed to a filehandle we
# can read from, $stderr_twin, but regular perl diagnostic messages still go
# to the real STDERR
my $stderr_twin;
$UR::ModuleBase::stderr = undef;
socketpair($UR::ModuleBase::stderr,$stderr_twin, AF_UNIX, SOCK_STREAM, PF_UNSPEC);
$UR::ModuleBase::stderr->autoflush(1);
$stderr_twin->blocking(0);

my $buffer;
for my $type (qw/error warning status/) {
    my $accessor = $type . "_message";

    my $uc_type = uc($type);
    my $msg_prefix = ($type eq "status" ? "" : "$uc_type: ");

    my $msg_source_sub = $accessor . '_source';

    for my $do_queue ([],[0],[1]) {
        for my $do_dump ([],[0],[1]) {

            my $dump_flag = "dump_" . $type . "_messages";
            $c->$dump_flag(@$do_dump);

            my $queue_flag = "queue_" . $type . "_messages";
            $c->$queue_flag(@$do_queue);

            my $list_accessor = $accessor . "s";

            is($c->$accessor(),         undef ,         "$type starts unset");
            $buffer = $stderr_twin->getline;
            is($buffer, undef, "no message");

            my $cb_register = $type . "_messages_callback";
            my $cb_msg_count = 0;
            my @cb_args;
            my $callback_sub = sub { @cb_args = @_; $cb_msg_count++;};
            ok($c->$cb_register($callback_sub), "can set callback");
            is($c->$cb_register(), $callback_sub, 'can get callback');

            my $message_line = __LINE__ + 1;    # The messaging sub will be called on the next line
            is($c->$accessor("error1"), "error1",       "$type setting works");
            $buffer = $stderr_twin->getline;
            is($buffer, ($c->$dump_flag ? "${msg_prefix}error1\n" : undef), ($c->$dump_flag ?  "got message 1" : "no dump") );

            my %source_info = $c->$msg_source_sub();
            is_deeply(\%source_info,
                      { $accessor => 'error1',
                        $type.'_package' => 'main',
                        $type.'_file' => __FILE__,
                        $type.'_line' => $message_line,
                        $type.'_subroutine' => undef },   # not called from within a sub
                      "$msg_source_sub returns correct info");

            is($cb_msg_count, 1, "$type callback fired");
            is_deeply(
                \@cb_args,    [$c, "error1"],       "$type callback got correct args"
            );

            is($c->$accessor(),         "error1",       "$type returns");
            $buffer = $stderr_twin->getline;
            is($buffer, undef, "no dump");

            is($c->$accessor("error2"), "error2",       "$type resetting works");
            $buffer = $stderr_twin->getline;
            is($buffer, ($c->$dump_flag ? "${msg_prefix}error2\n" : undef), ($c->$dump_flag ?  "got message 2" : "no dump") );

            is($cb_msg_count, 2, "$type callback fired");

            is($c->$accessor(),         "error2",       "$type returns");
            is_deeply(
                \@cb_args,    [$c, "error2"],       "$type callback got correct args"
            );

            is_deeply(
                [$c->$list_accessor],
                ($c->$queue_flag ? ["error1","error2"] : []),
                ($c->$queue_flag ? "$type list is correct" : "$type list is correctly empty")
            );

            is($c->$accessor(undef),    undef ,         "undef message sent to $type");

            is($cb_msg_count, 3, "$type callback fired");

            $buffer = $stderr_twin->getline;
            is($buffer, undef, 'Setting undef message results in no output');

            is($c->$accessor(),         undef ,         "$type still has the previous message");
            is_deeply(
                \@cb_args,    [$c, undef],       "$type callback got correct args"
            );

            is_deeply(
                [$c->$list_accessor],
                ($c->$queue_flag ? ["error1","error2"] : []),
                ($c->$queue_flag ? "$type list is correct" : "$type list is correctly empty")
            );

            my $listref_accessor = $list_accessor . "_arrayref";
            my $listref = $c->$listref_accessor();
            is_deeply(
    	        $listref,
                ($c->$queue_flag ? ['error1','error2'] : []),
                "$type listref is correct"
            );

            $c->$cb_register(sub { $_[1] .= "foo"});
            $c->$accessor("altered");
            $buffer = $stderr_twin->getline();
            is($buffer, ($c->$dump_flag ? "${msg_prefix}alteredfoo\n" : undef), ($c->$dump_flag ?  "got altered message" : "no dump") );
            is_deeply(
                [$c->$list_accessor],
                ($c->$queue_flag ? ["error1","error2","alteredfoo"] : []),
                ($c->$queue_flag ? "$type list is correct" : "$type list is correctly empty")
            );

            $c->$cb_register(undef);  # Unset the callback

            is($c->$accessor(undef),    undef ,         "undef message sent to $type message");
            is($cb_msg_count, 3, "$type callback correctly didn't get fired");
            $buffer = $stderr_twin->getline();
            is($buffer, undef, 'Setting undef message results in no output');
            is_deeply(
                [$c->$list_accessor],
                ($c->$queue_flag ? ["error1","error2","alteredfoo"] : []),
                ($c->$queue_flag ? "$type list is correct" : "$type list is correctly empty")
            );

            if ($c->$queue_flag) {
                $listref->[2] = "something else";
                is_deeply(
                    [$c->$list_accessor],
                    ["error1","error2","something else"],
                    "$type list is correct after changing via the listref"
                );


                @$listref = ();
                is_deeply(
                    [$c->$list_accessor],   [],    "$type list cleared out as expected"
                );
            }
        }

    }
}

1;
