#!/usr/bin/perl

# [[[ HEADER ]]]
use strict;
use warnings;
our $VERSION = 0.001_010;

# [[[ CRITICS ]]]
## no critic qw(ProhibitUselessNoCritic ProhibitMagicNumbers RequireCheckedSyscalls)  # USER DEFAULT 1: allow numeric values & print operator
## no critic qw(ProhibitStringyEval)  # SYSTEM DEFAULT 1: allow eval()
## no critic qw(RequireInterpolationOfMetachars)  # USER DEFAULT 2: allow single-quoted control characters & sigils

# [[[ INCLUDES ]]]
use Test::More tests => 16;
use Test::Exception;
use Carp;
use English qw(-no_match_vars);

# [[[ OPERATIONS ]]]

#$SIG{__WARN__}=sub {cluck $_[0]};  # DEBUGGING

BEGIN {
    if ( $ENV{RPERL_VERBOSE} ) {
        Test::More::diag('[[[ Beginning Inline::C Pre-Test Loading ]]]');
    }
}
if ( $ENV{RPERL_VERBOSE} ) {
    Test::More::diag('[[[ Beginning Selection Of Tests From The Inline::C Cookbook ]]]');
}

# Inline::C examples from:  http://search.cpan.org/~sisyphus/Inline-0.53/C/C-Cookbook.pod
# Note: I excluded the Inline::C examples which required reading files, loading 3rd-party libraries, and other weirdness not suitable for testing.

# greet()
#lives_ok( sub { use Inline C => q{charge* greet(){return("Hello, World");}}; }, q{Inline::C, define greet() lives} );  # bad: invokes Inline during syntax check and crashes all tests
lives_and(
    sub {
        my $EVAL_RETVAL = eval 'use Inline C=>q{char* greet(){return("Hello, World");}};';

        #            '$SIG{__WARN__}=sub {cluck $_[0]};  use Inline C=>q{char* greet(){return("Hello, World");}};';
        if ( $EVAL_ERROR ne q{} ) {
            croak("Error in eval, have \$EVAL_ERROR =\n\nBEGIN EVAL ERROR\n\n$EVAL_ERROR\n\nEND EVAL ERROR\n\ncroaking");
        }
        is( $EVAL_RETVAL, undef, q{Inline::C, define greet() returns correct value} );
    },
    q{Inline::C, define greet() lives}
);
lives_and(
    sub {
        my $EVAL_RETVAL = eval 'greet();';
        if ( $EVAL_ERROR ne q{} ) {
            croak("Error in eval, have \$EVAL_ERROR =\n\nBEGIN EVAL ERROR\n\n$EVAL_ERROR\n\nEND EVAL ERROR\n\ncroaking");
        }
        is( $EVAL_RETVAL, 'Hello, World', q{Inline::C, call greet() returns correct value} );
    },
    q{Inline::C, call greet() lives}
);

# greet_bind()
lives_and(
    sub {
        my $EVAL_RETVAL = eval 'use Inline; Inline->bind(C=>q{char* greet_bind(){return("Hello again, world");}})';
        if ( $EVAL_ERROR ne q{} ) {
            croak("Error in eval, have \$EVAL_ERROR =\n\nBEGIN EVAL ERROR\n\n$EVAL_ERROR\n\nEND EVAL ERROR\n\ncroaking");
        }
        like(
            $EVAL_RETVAL, '/HASH\(0x\w*\)/',    ## RPERL SYSTEM allow like() regex
            q{Inline::C, define greet_bind() returns correct value}
        );
    },
    q{Inline::C, define greet_bind() lives}
);
lives_and(
    sub {
        my $EVAL_RETVAL = eval 'greet_bind();';
        if ( $EVAL_ERROR ne q{} ) {
            croak("Error in eval, have \$EVAL_ERROR =\n\nBEGIN EVAL ERROR\n\n$EVAL_ERROR\n\nEND EVAL ERROR\n\ncroaking");
        }
        is( $EVAL_RETVAL, 'Hello again, world', q{Inline::C, call greet_bind() returns correct value} );
    },
    q{Inline::C, call greet_bind() lives}
);

# JAxH()
lives_and(
    sub {
        my $EVAL_RETVAL = eval 'use Inline C=>q{SV*JAxH(char*x){return newSVpvf("Just Another %s Hacker",x);}};';
        if ( $EVAL_ERROR ne q{} ) {
            croak("Error in eval, have \$EVAL_ERROR =\n\nBEGIN EVAL ERROR\n\n$EVAL_ERROR\n\nEND EVAL ERROR\n\ncroaking");
        }
        is( $EVAL_RETVAL, undef, q{Inline::C, define JAxH() returns correct value} );
    },
    q{Inline::C, define JAxH() lives}
);
lives_and(
    sub {
        my $EVAL_RETVAL = eval 'JAxH("Perl");';
        if ( $EVAL_ERROR ne q{} ) {
            croak("Error in eval, have \$EVAL_ERROR =\n\nBEGIN EVAL ERROR\n\n$EVAL_ERROR\n\nEND EVAL ERROR\n\ncroaking");
        }
        is( $EVAL_RETVAL, 'Just Another Perl Hacker', q{Inline::C, call JAxH() returns correct value} );
    },
    q{Inline::C, call JAxH() lives}
);

# greetings_char()
my $greetings_char_eval_string = <<'EOF';
#use Inline C => <<'END_OF_C_CODE';  # NEED FIX: Inline::C pegex elipses issue https://github.com/ingydotnet/inline-c-pm/issues/25
use Inline C => <<'END_OF_C_CODE' => using => 'ParseRecDescent';
char* greetings_char(SV* name1, ...) {
	Inline_Stack_Vars;
	int i;
	SV* retval = newSVpv("", 0);
	for (i = 0; i < Inline_Stack_Items; i++)
	{
		retval = newSVpvf("%sHello %s!  ", SvPV(retval, PL_na), SvPV(Inline_Stack_Item(i), PL_na));
//		printf("in greetings_char(), for() loop i = %ld, have retval ='%s'\n", i, SvPV_nolen(retval));
	}
	return(SvPV_nolen(retval));
}
END_OF_C_CODE
EOF
lives_and(
    sub {
        my $EVAL_RETVAL = eval $greetings_char_eval_string;
        if ( $EVAL_ERROR ne q{} ) {
            croak("Error in eval, have \$EVAL_ERROR =\n\nBEGIN EVAL ERROR\n\n$EVAL_ERROR\n\nEND EVAL ERROR\n\ncroaking");
        }
        is( $EVAL_RETVAL, undef, q{Inline::C, define greetings_char() returns correct value} );
    },
    q{Inline::C, define greetings_char() lives}
);
lives_and(
    sub {
        my $EVAL_RETVAL = eval 'greetings_char("Larry", "Ingy", "Reini", "Neil", "Sisyphus", "Davido");';
        if ( $EVAL_ERROR ne q{} ) {
            croak("Error in eval, have \$EVAL_ERROR =\n\nBEGIN EVAL ERROR\n\n$EVAL_ERROR\n\nEND EVAL ERROR\n\ncroaking");
        }
        is( $EVAL_RETVAL,
            'Hello Larry!  Hello Ingy!  Hello Reini!  Hello Neil!  Hello Sisyphus!  Hello Davido!  ',
            q{Inline::C, call greetings_char() returns correct value}
        );
    },
    q{Inline::C, call greetings_char() lives}
);

# greetings_sv()
my $greetings_sv_eval_string = <<'EOF';
#use Inline C => <<'END_OF_C_CODE';  # NEED FIX: Inline::C pegex elipses issue https://github.com/ingydotnet/inline-c-pm/issues/25
use Inline C => <<'END_OF_C_CODE' => using => 'ParseRecDescent';
SV* greetings_sv(SV* retval, SV* name1, ...) {
	Inline_Stack_Vars;
	int i;
	for (i = 1; i < Inline_Stack_Items; i++)
	{
		retval = newSVpvf("%sHello %s!  ", SvPV(retval, PL_na), SvPV(Inline_Stack_Item(i), PL_na));
//		printf("in greetings_sv(), for() loop i = %ld, have retval ='%s'\n", i, SvPV_nolen(retval));
	}
	return retval;
}
END_OF_C_CODE
EOF
lives_and(
    sub {
        my $EVAL_RETVAL = eval $greetings_sv_eval_string;
        if ( $EVAL_ERROR ne q{} ) {
            croak("Error in eval, have \$EVAL_ERROR =\n\nBEGIN EVAL ERROR\n\n$EVAL_ERROR\n\nEND EVAL ERROR\n\ncroaking");
        }
        is( $EVAL_RETVAL, undef, q{Inline::C, define greetings_sv() returns correct value} );
    },
    q{Inline::C, define greetings_sv() lives}
);
lives_and(
    sub {
        my $greetings_sv_retval = q{};
        my $EVAL_RETVAL         = eval 'greetings_sv($greetings_sv_retval, "Larry", "Ingy", "Reini", "Neil", "Sisyphus", "Davido");';
        if ( $EVAL_ERROR ne q{} ) {
            croak("Error in eval, have \$EVAL_ERROR =\n\nBEGIN EVAL ERROR\n\n$EVAL_ERROR\n\nEND EVAL ERROR\n\ncroaking");
        }
        is( $EVAL_RETVAL,
            'Hello Larry!  Hello Ingy!  Hello Reini!  Hello Neil!  Hello Sisyphus!  Hello Davido!  ',
            q{Inline::C, call greetings_sv() returns correct value}
        );
    },
    q{Inline::C, call greetings_sv() lives}
);

# greetings_void()
my $greetings_void_eval_string = <<'EOF';
#use Inline C => <<'END_OF_C_CODE';  # NEED FIX: Inline::C pegex elipses issue https://github.com/ingydotnet/inline-c-pm/issues/25
use Inline C => <<'END_OF_C_CODE' => using => 'ParseRecDescent';
void greetings_void(SV* name1, ...) {
	Inline_Stack_Vars;
	int i;
	SV* retval = newSVpv("", 0);
	for (i = 0; i < Inline_Stack_Items; i++)
	{
		retval = newSVpvf("%sHello %s!  ", SvPV(retval, PL_na), SvPV(Inline_Stack_Item(i), PL_na));
//		printf("in greetings_void(), for() loop i = %ld, have retval ='%s'\n", i, SvPV_nolen(retval));
	}
	Inline_Stack_Reset;
	Inline_Stack_Push(sv_2mortal(retval));
	Inline_Stack_Done;
}
END_OF_C_CODE
EOF
lives_and(
    sub {
        my $EVAL_RETVAL = eval $greetings_void_eval_string;
        if ( $EVAL_ERROR ne q{} ) {
            croak("Error in eval, have \$EVAL_ERROR =\n\nBEGIN EVAL ERROR\n\n$EVAL_ERROR\n\nEND EVAL ERROR\n\ncroaking");
        }
        is( $EVAL_RETVAL, undef, q{Inline::C, define greetings_void() returns correct value} );
    },
    q{Inline::C, define greetings_void() lives}
);
lives_and(
    sub {
        my $EVAL_RETVAL = eval 'greetings_void("Larry", "Ingy", "Reini", "Neil", "Sisyphus", "Davido");';
        if ( $EVAL_ERROR ne q{} ) {
            croak("Error in eval, have \$EVAL_ERROR =\n\nBEGIN EVAL ERROR\n\n$EVAL_ERROR\n\nEND EVAL ERROR\n\ncroaking");
        }
        is( $EVAL_RETVAL,
            'Hello Larry!  Hello Ingy!  Hello Reini!  Hello Neil!  Hello Sisyphus!  Hello Davido!  ',
            q{Inline::C, call greetings_void() returns correct value}
        );
    },
    q{Inline::C, call greetings_void() lives}
);

# change()
my $change_eval_string = <<'EOF';
use Inline C => <<'END_OF_C_CODE';
int change(SV* var1, SV* var2) {
	sv_setpvn(var1, "Perl Rocks!", 11);
	sv_setpvn(var2, "Inline Rules!", 13);
	return 1;
}
END_OF_C_CODE
EOF
lives_and(
    sub {
        my $EVAL_RETVAL = eval $change_eval_string;
        if ( $EVAL_ERROR ne q{} ) {
            croak("Error in eval, have \$EVAL_ERROR =\n\nBEGIN EVAL ERROR\n\n$EVAL_ERROR\n\nEND EVAL ERROR\n\ncroaking");
        }
        is( $EVAL_RETVAL, undef, q{Inline::C, define change() returns correct value} );
    },
    q{Inline::C, define change() lives}
);
lives_and(
    sub {
        my ( $foo, $bar );
        my $EVAL_RETVAL = eval 'change($foo, $bar);';
        if ( $EVAL_ERROR ne q{} ) {
            croak("Error in eval, have \$EVAL_ERROR =\n\nBEGIN EVAL ERROR\n\n$EVAL_ERROR\n\nEND EVAL ERROR\n\ncroaking");
        }
        cmp_ok( $EVAL_RETVAL, q{==}, 1, q{Inline::C, call change() returns correct value} );
        is( $foo, 'Perl Rocks!',   'Inline::C, call change(), correctly change variable 1' );
        is( $bar, 'Inline Rules!', 'Inline::C, call change(), correctly change variable 2' );
    },
    q{Inline::C, call change() lives}
);

done_testing();