use v6.d;

use Test;

plan 40;

# L<S12/Single inheritance/An "isa" is just a trait that happens to be another class>

class Foo {
    has $.bar is rw;
    has $.value is rw;
    method baz { return 'Foo::baz' }
    method getme($self:) returns Foo { return $self }
}

class Foo::Bar is Foo {
    has $.bar2 is rw;
    method baz { return 'Foo::Bar::baz' }
    method fud { return 'Foo::Bar::fud' }
    method super_baz ($self:) { return $self.Foo::baz() }
}

class Unrelated {
    method something { 'bad' };
}

my $foo_bar = Foo::Bar.new();
isa-ok($foo_bar, Foo::Bar);

ok(!defined($foo_bar.bar2()), '... we have our autogenerated accessor');
ok(!defined($foo_bar.bar()), '... we inherited the superclass autogenerated accessor');

lives-ok { $foo_bar.bar = 'BAR' }, '... our inherited the superclass autogenerated accessor is rw';
is($foo_bar.bar(), 'BAR', '... our inherited the superclass autogenerated accessor is rw');

lives-ok { $foo_bar.bar2 = 'BAR2'; }, '... our autogenerated accessor is rw';

is($foo_bar.bar2(), 'BAR2', '... our autogenerated accessor is rw');

is($foo_bar.baz(), 'Foo::Bar::baz', '... our subclass overrides the superclass method');

is($foo_bar.super_baz(), 'Foo::baz', '... our subclass can still access the superclass method through Foo::');
is($foo_bar.fud(), 'Foo::Bar::fud', '... sanity check on uninherited method');

is($foo_bar.getme, $foo_bar, 'can call inherited methods');
is($foo_bar.getme.baz, "Foo::Bar::baz", 'chained method dispatch on altered method');

ok(!defined($foo_bar.value), 'value can be used for attribute name in derived classes');
my $fud;

lives-ok { $fud = $foo_bar.getme.fud }, 'chained method dispatch on altered method';
is($fud, "Foo::Bar::fud", "returned value is correct");

is $foo_bar.Foo::baz, 'Foo::baz', '$obj.Class::method syntax works';
dies-ok { $foo_bar.Unrelated::something() },
    'Cannot call unrelated method with $obj.Class::method syntax';

# See thread "Quick OO .isa question" on p6l started by Ingo Blechschmidt:
# L<"http://www.nntp.perl.org/group/perl.perl6.language/22220">

ok  Foo::Bar.isa(Foo),      "subclass.isa(superclass) is true";
ok  Foo::Bar.isa(Foo::Bar), "subclass.isa(same_subclass) is true";
ok Foo::Bar.HOW.isa(Foo::Bar, Foo),      "subclass.HOW.isa(superclass) is true";
ok Foo::Bar.HOW.isa(Foo::Bar, Foo::Bar), "subclass.HOW.isa(same_subclass) is true";

{
    my $test = '$obj.$meth is canonical (audreyt says)';
    class Abc {
        method foo () { "found" }
    }
    class Child is Abc { }
    is( EVAL('my $meth = "foo"; my $obj= Child.new; $obj."$meth"()'), 'found', $test);
}

# Erroneous dispatch found by TimToady++

class ClassX {
    method j () { 'X' }
};
class ClassZ is ClassX {}
class ClassY is ClassX {
    method k () { ClassZ.new.j() }
    method j () { 'Y' }
};

is(ClassZ.new.j(), 'X', 'inherited method dispatch works');
is(ClassY.new.k(), 'X', 'inherited method dispatch works inside another class with same-named method');

{
    my class A {
      has @.x = <a b c>;
      has $.w = 9;

      method y($i) { return @.x[$i]; }
    }

    my class B is A {
      has $.w = 10;
      method z($i) { return $.y($i); }
    }

    is( B.new.z(1), 'b', 'initializer carries through' );
    is( B.new.w, 10, 'initializer can be overridden by derived classes' );
}

# test that you can inherit from a class with :: in the name.
{
    class A::B {
        method ab { 'a'; };
    };

    class A::B::C is A::B {
        method abc { 'b'; };
    }
    my $o = A::B::C.new;

    ok defined($o), 'can instantiate object from class A::B::C';
    is $o.ab,  'a', 'can access inherited method';
    is $o.abc, 'b', 'can access directly defined method';
}

# Make sure inheritance from Mu works (got broken in Rakudo once).
eval-lives-ok 'class NotAny is Mu { }; NotAny.new', 'inheritance from Mu works';
{
    class DirectMu is Mu { };
    ok DirectMu !~~ Any, 'class inheriting from Mu is not Any';
    ok !( any(DirectMu.^parents).gist eq '(Any)'), 'and Any does not appear in the list of parents either';
}

throws-like 'class RT64642 is ::Nowhere {}', X::Inheritance::UnknownParent,
    'dies: class D is ::C {}';

# check that inheriting from Array works
{
    class ArrayChild is Array {
        method summary() { self.join(', ') }
    }

    my $a = ArrayChild.new;
    $a.push('foo');
    $a.push('bar');
    is $a.join('|'), 'foo|bar', 'inheritance from Array';
    is $a.summary, 'foo, bar', 'and ArrayChild methods work';

    my @a := ArrayChild.new;
    @a.push: 3, 5;
    is @a.summary, '3, 5', 'new methods still work in @ variables';

}

# RT #82814
{
    my class A {
        method new { self.bless }
    };
    my class B is A {
        has $.c is rw;
        method new {
            my $obj = callsame;
            $obj.c = 42;
            return $obj
        }
    }
    is B.new.c, 42, 'nextsame in constructor works';
}

# RT #75376
{
    my class RT75376::A { };
    lives-ok { our class RT75376::B is RT75376::A { } },
        'our-scoped class can inherit from my-scoped class';
    ok (RT75376::B.^mro[0] ~~ RT75376::B and RT75376::B.^mro[1] ~~ RT75376::A),
        'our-scoped class inherited from my-scoped class has proper inheritance hierarchy';
}

# RT #125689
throws-like 'class X is nosuchtrait { }', X::Inheritance::UnknownParent,
    'Get unkown parent error even when class is called X';

# vim: ft=perl6
