#!perl

use warnings FATAL => 'all';
use strict;

use Test::More tests => 150;

BEGIN{ use_ok('Ruby', ':DEFAULT') }

use Ruby -function => qw(rb_const Rational Float Integer String lambda(&) rb_c),
	-require => 'rational',
	-class => 'GC', 'String',
	-module => 'Kernel';

is(rb_const(RUBY_VERSION), $Ruby::Version, 'RUBY_VERSION eq $Ruby::Version');

is_deeply rb_eval("nil"), nil, "rb_eval";

is_deeply rb_eval("true"), true, "rb_eval";

is_deeply rb_eval("false"), false, "rb_eval";

is_deeply rb_eval("nil ? true : false"), false, "rb_eval";

is_deeply rb_eval("if(!nil) then 10 else 11 end"), Integer(10), "rb_eval";

isa_ok(nil, "Ruby::Object");

isa_ok(nil, "Ruby::Object");

can_ok(nil, 'inspect');

ok(nil->respond_to('inspect'), "respond_to");
ok(nil->kind_of('Object'), "kind_of");

is nil->send("inspect"), "nil", "send";
is nil->inspect(), "nil", "NilClass#inspect";

is nil->object_id, nil->send("object_id"), "funcall";

nil->class->alias('is_nil', 'nil?');
ok nil->is_nil, "alias";

is sprintf('%s', true), 'true', 'stringify';
is sprintf('%d', Integer(10)), '10', 'numify';

ok true,   "bool test (true)";
ok !nil,   "bool test (nil)";
ok !false, "bool test (false)";
ok !!String('ok'), "bool test (string)";
ok !!String(''),   "bool test (empty string)";

is String('foo'), String('foo'), "str == str";
is String('foo')->clone, String('foo'), "str.clone == str";

is String('str')->stringify, 'str';
is String('1.23')->numify,   1.23;
is String('1000')->numify,   1000;
is String('100000000000000000000000000000000000')->numify, 100000000000000000000000000000000000;
ok String('')->boolify, "string is always true";

is Integer(100)->stringify, '100';
is Integer(100)->numify,     100;
ok Integer(0)->boolify, "integer is always true";

is Float(1.23)->stringify, '1.23';
is Float(1.23)->numify,     1.23;
ok Float(0)->boolify, "float is always true";

is Rational(Integer(1), Integer(2))->numify, 1/2, "rational->numify";
is Rational(Integer(1), Integer(2))->stringify, "1/2", "rational->stringify";

cmp_ok Integer(10), "eq", Integer(10), "Ruby::Integer eq Ruby::Integer";
cmp_ok Integer(10), "eq", 10, "Ruby::Integer eq Perl::Integer";
cmp_ok         10 , "eq", Integer(10), "Perl::Integer eq Ruby::Integer";

cmp_ok Integer(10), "==", Integer(10), "R::I == R::I";
cmp_ok Integer(10), "!=", Integer(11), "R::I != R::I";

cmp_ok Integer(10), "==", 10, "R::I == P::I";
cmp_ok Integer(10), "!=", 11, "R::I == P::I";

cmp_ok 10, "==", Integer(10), "P::I == R::I";
cmp_ok 10, "!=", Integer(11), "P::I == R::I";

cmp_ok Integer(10) <=> Integer(10), "==", 0, "R::I <=> R::I";
cmp_ok Integer(10) <=> Integer( 9),  ">", 0;
cmp_ok Integer(10) <=> Integer(11),  "<", 0;

cmp_ok Integer(10) <=> (10), "==", 0, "R::I <=> P::I";
cmp_ok Integer(10) <=> ( 9), " >", 0;
cmp_ok Integer(10) <=> (11),  "<", 0;

cmp_ok 10 <=> Integer(10), "==", 0, "P::I <=> R::I";
cmp_ok 10 <=> Integer( 9), " >", 0;
cmp_ok 10 <=> Integer(11),  "<", 0;

ok((Integer(10)+Integer(10))->kind_of('Integer'), "R::I + R::I => R::I");
ok((Integer(10)+10)         ->kind_of('Integer'), "R::I + P::I => R::I");
ok((Integer(10)+ 0.5)       ->kind_of('Float'),   "R::I + P::F => R::F");
ok((Integer(10)+'10')       ->kind_of('Numeric'), "R::I + P::IS => R::Numeric");
ok((Integer(10)+'0.5')      ->kind_of('Numeric'), "R::I + P::FS => R::Numeric");

is Integer(10) + Integer(2), 12, "R::I + R::I";
is Integer(10) + 2         , 12, "R::I + P::I";

is Integer(10) - Integer(2),  8, "R::I - R::I";
is Integer(10) - 2         ,  8, "R::I - P::I";

is Integer(10) * Integer(2), 20, "R::I * R::I";
is Integer(10) * 2         , 20, "R::I * P::I";

is Integer(10) / Integer(2),  5, "R::I / R::I";
is Integer(10) / 2         ,  5, "R::I / P::I";

is Integer(10) % Integer(4),  2, "R::I % R::I";
is Integer(10) % 4         ,  2, "R::I % P::I";

is Integer(10) ** Integer(2), 100, "R::I ** R::I";
is Integer(10) ** 2         , 100, "R::I ** P::I";

is Integer(010) | Integer(002), 012, "R::I | R::I";
is Integer(010) | 002,          012, "R::I | P::I";

is Integer(070) & Integer(011), 010, "R::I & R::I";
is Integer(070) & 011,          010, "R::I & P::I";

is Integer(070) ^ Integer(071), 001, "R::I ^ R::I";
is Integer(070) ^ 071,          001, "R::I ^ P::I";

is -Integer(10), Integer(-10), "-R::I";
is  Integer(077) & ~Integer(01),  Integer(076),  "~R::I";

isa_ok abs(Float(-10)), "Ruby::Object";
is     abs(Float(-10)), Float(10),   "abs() is overloaded";

isa_ok int(Float(1.5)), "Ruby::Object", "int()";
is     int(Float(1.5)), Integer(1),   "int() is overloaded";

isa_ok sqrt(Float(100)), "Ruby::Object", "sqrt()";
is     sqrt(Float(100)), Float(10), "sqrt() is overloaded";

isa_ok sin(Float(100)), "Ruby::Object", "sin()";
isa_ok cos(Float(100)), "Ruby::Object", "cos()";
isa_ok exp(Float(100)), "Ruby::Object", "exp()";
isa_ok log(Float(100)), "Ruby::Object", "log()";
isa_ok atan2(Float(1), Float(1)), "Ruby::Object", "atan2()";

my $i = Integer(10);

$i += Integer(1);

is $i, 11, "R::I += R::I";
isa_ok $i, "Ruby::Object";

$i += 1;

is $i, 12, "R::I += P::I";
isa_ok $i, "Ruby::Object";

$i++;

is $i, 13, "R::I++";
isa_ok $i, "Ruby::Object";


ok !eval{ 10 + Integer(10) }, "Don't P::I + R::I";

is Integer(100)->inspect, 100, "inspect Integer";

my $plus_one = lambda{ $_[0] + 1 };

ok($plus_one->respond_to('call'), 'make lambda');

is $plus_one->call(Integer(10)), 11, "lambda->call";
is $plus_one->call(Integer(10)), 11, " ... retry";

is $plus_one->(Integer(11)), 12, "lambda as code";
is $plus_one->(Integer(11)), 12, " ... retry";

my $count = 0;
Integer(3)->times(sub{
	my($i) = @_;

	is $count, $i, "do block with param";

	$count++;
});


$count = 0;
rb_eval('[1, 2, 3, 4, 5]')->each(sub{
	is($_[0], ++$count, 'ary.each');
});
rb_eval('{:foo => :bar}')->each(sub{
	is $_[0], 'foo', 'hash.each';
	is $_[1], 'bar', 'hash.each';
});


rb_eval(<<'.', __PACKAGE__);

	def add(x, y)
		x.to_f + y.to_f
	end

	def pkg()
		__PACKAGE__
	end

	def call_functions()
		f('f()');
		g('g()');
		h('h()');
	end

.

cmp_ok add(1, 2), '==', 3, 'eval & import';

is pkg(), 'main', '__PACKAGE__ in ruby';

sub f{
	is $_[0], 'f()',  "call f() in ruby";
}
sub g{
	is $_[0], 'g()', "call g() in ruby";
}
sub h{
	is $_[0], 'h()', "call h() in ruby";
}

call_functions();


is(rb_c(Kernel), "Kernel", "rb_c");
is(Kernel::->class, rb_c(Module), "import module");
is(String::->class, rb_c(Class),  "import class");
is(String::->new('')->class, rb_c(String), "new");


# rubyify / to_perl

ok rubyify('')   ->kind_of('Perl::Scalar'), "rubyify str";
ok rubyify( 0)   ->kind_of('Perl::Scalar'),  "rubyify num";
ok rubyify(undef)->kind_of('Perl::Scalar'), "rubyify undef";

ok rubyify([]) ->kind_of('Perl::Array'),  "rubyify array";
ok rubyify({}) ->kind_of('Perl::Hash'),   "rubyify hash";
ok rubyify(\&f)->kind_of('Perl::Code'),   "rubyify code";
ok rubyify(*f) ->kind_of('Perl::Glob'),   "rubyify glob";

ok rubyify(*STDIN)    ->kind_of('Perl::Glob'), "rubyfiy *STDIN  -> Perl::Glob";
ok rubyify(\*STDIN)   ->kind_of('Perl::IO'),   "rubyify \*STDIN -> Perl::IO";
ok rubyify(*STDIN{IO})->kind_of('Perl::IO'),   "rubyify *STDIN{IO} -> Perl::IO";


ok rubyify(Integer(1))->kind_of('Integer'), "rubyify rubyint";

is_deeply rubyify([1, 2, 3])->to_perl, [1, 2, 3], "to_perl perlarray";
is_deeply rubyify({foo => 'bar'})->to_perl, {foo => 'bar'}, "to_perl perlhash";

is_deeply rb_eval('[1, 2, 3]')->to_perl, [1, 2, 3], "to_perl rubyarray";
is_deeply rb_eval('{1 => 2, "foo" => "bar"}')->to_perl, {1 => 2, "foo" => "bar"}, "to_perl rubyhash";

# global variable

our $stdin;
Ruby->import(-variable => '$stdin');
ok $stdin, "import global variable";
ok $stdin->kind_of('IO');

our $rubyout;
Ruby->import(-variable => [qw($stdout $rubyout)]);

ok $rubyout;
ok $rubyout->kind_of('IO');

{package T; our $stderr;}
Ruby->import(-variable => [qw($stderr $T::stderr)]);
ok $T::stderr->kind_of('IO'),'export $gvar to T::';

rb_eval('$gvar = 10');

Ruby->import(-variable => '$gvar');
our $gvar;

is $gvar, 10, "get gvar";
$gvar = 20;
is $gvar, 20, "set gvar";

is rb_eval('$gvar'), 20, "get in ruby";
rb_eval('$gvar = $gvar.to_i * 2');

is $gvar, 40, "set in ruby";


for(1 .. 10){
	$gvar++; GC->start;
}

is $gvar, 50, "set/get \$gvar";

END{
	pass "test end";
}

