module SuperSpecs
  module S1
    class A
      def foo(a)
        a << "A#foo"
        bar(a)
      end
      def bar(a)
        a << "A#bar"
      end
    end
    class B < A
      def foo(a)
        a << "B#foo"
        super(a)
      end
      def bar(a)
        a << "B#bar"
        super(a)
      end
    end
  end

  module S2
    class A
      def baz(a)
        a << "A#baz"
      end
    end
    class B < A
      def foo(a)
        a << "B#foo"
        baz(a)
      end
    end
    class C < B
      def baz(a)
        a << "C#baz"
        super(a)
      end
    end
  end

  module S3
    class A
      def foo(a)
        a << "A#foo"
      end
      def self.foo(a)
        a << "A.foo"
      end
      def self.bar(a)
        a << "A.bar"
        foo(a)
      end
    end
    class B < A
      def self.foo(a)
        a << "B.foo"
        super(a)
      end
      def self.bar(a)
        a << "B.bar"
        super(a)
      end
    end
  end

  module S4
    class A
      def foo(a)
        a << "A#foo"
      end
    end
    class B < A
      def foo(a, b)
        a << "B#foo(a,#{b})"
        super(a)
      end
    end
  end

  class S5
    def here
      :good
    end
  end

  class S6 < S5
    def under
      yield
    end

    def here
      under {
        super
      }
    end
  end

  class S7 < S5
    define_method(:here) { super() }
  end

  module MS1
    module ModA
      def foo(a)
        a << "ModA#foo"
        bar(a)
      end
      def bar(a)
        a << "ModA#bar"
      end
    end
    class A
      include ModA
    end
    module ModB
      def bar(a)
        a << "ModB#bar"
        super(a)
      end
    end
    class B < A
      def foo(a)
        a << "B#foo"
        super(a)
      end
      include ModB
    end
  end

  module MS2
    class A
      def baz(a)
        a << "A#baz"
      end
    end
    module ModB
      def foo(a)
        a << "ModB#foo"
        baz(a)
      end
    end
    class B < A
      include ModB
    end
    class C < B
      def baz(a)
        a << "C#baz"
        super(a)
      end
    end
  end

  module MultiSuperTargets
    module M
      def foo
        super
      end
    end

    class BaseA
      def foo
        :BaseA
      end
    end

    class BaseB
      def foo
        :BaseB
      end
    end

    class A < BaseA
      include M
    end

    class B < BaseB
      include M
    end
  end

  module MS3
    module ModA
      def foo(a)
        a << "ModA#foo"
      end
      def bar(a)
        a << "ModA#bar"
        foo(a)
      end
    end
    class A
      def foo(a)
        a << "A#foo"
      end
      class << self
        include ModA
      end
    end
    class B < A
      def self.foo(a)
        a << "B.foo"
        super(a)
      end
      def self.bar(a)
        a << "B.bar"
        super(a)
      end
    end
  end

  module MS4
    module Layer1
      def example
        5
      end
    end

    module Layer2
      include Layer1
      def example
        super
      end
    end

    class A
      include Layer2
      public :example
    end
  end

  class MM_A
    undef_method :is_a?
  end

  class MM_B < MM_A
    def is_a?(blah)
      # should fire the method_missing below
      super
    end

    def method_missing(*)
      false
    end
  end

  class Alias1
    def name
      [:alias1]
    end
  end

  class Alias2 < Alias1
    def initialize
      @times = 0
    end

    def name
      if @times >= 10
        raise "runaway super"
      end

      @times += 1

      # Use this so that we can see collect all supers that we see.
      # One bug that arises is that we call Alias2#name from Alias2#name
      # as it's superclass. In that case, either we get a runaway recursion
      # super OR we get the return value being [:alias2, :alias2, :alias1]
      # rather than [:alias2, :alias1].
      #
      # Which one depends on caches and how super is implemented.
      [:alias2] + super
    end
  end

  class Alias3 < Alias2
    alias_method :name3, :name
    # In the method table for Alias3 now should be a special alias entry
    # that references Alias2 and Alias2#name (probably as an object).
    #
    # When name3 is called then, Alias2 (NOT Alias3) is presented as the
    # current module to Alias2#name, so that when super is called,
    # Alias2's superclass is next.
    #
    # Otherwise, Alias2 is next, which is where name was to begin with,
    # causing the wrong #name method to be called.
  end

  module AliasWithSuper
    module AS1
      def foo
        :a
      end
    end

    module BS1
      def foo
        [:b, super]
      end
    end

    class Base
      extend AS1
      extend BS1
    end

    class Trigger < Base
      class << self
        def foo_quux
          foo_baz
        end

        alias_method :foo_baz, :foo
        alias_method :foo, :foo_quux
      end
    end
  end

  module RestArgsWithSuper
    class A
      def a(*args)
        args
      end
    end

    class B < A
      def a(*args)
        args << "foo"

        super
      end
    end
  end

  class AnonymousModuleIncludedTwiceBase
    def self.whatever
      mod = Module.new do
        def a(array)
          array << "anon"
          super
        end
      end

      include mod
    end

    def a(array)
      array << "non-anon"
    end
  end

  class AnonymousModuleIncludedTwice < AnonymousModuleIncludedTwiceBase
    whatever
    whatever
  end

  module ZSuperWithBlock
    class A
      def a
        yield
      end

      def b(&block)
        block.call
      end

      def c
        yield
      end
    end

    class B < A
      def a
        super { 14 }
      end

      def b
        block_ref = -> { 15 }
        [super { 14 }, super(&block_ref)]
      end

      def c
        block_ref = -> { 16 }
        super(&block_ref)
      end
    end
  end

  module ZSuperWithOptional
    class A
      def m(x, y, z)
        z
      end
    end

    class B < A
      def m(x, y, z = 14)
        super
      end
    end

    class C < A
      def m(x, y, z = 14)
        z = 100
        super
      end
    end
  end

  module ZSuperWithRest
    class A
      def m(*args)
        args
      end

      def m_modified(*args)
        args
      end
    end

    class B < A
      def m(*args)
        super
      end

      def m_modified(*args)
        args[1] = 14
        super
      end
    end
  end

  module ZSuperWithRestAndOthers
    class A
      def m(a, b, *args)
        args
      end

      def m_modified(a, b, *args)
        args
      end
    end

    class B < A
      def m(a, b, *args)
        super
      end

      def m_modified(a, b, *args)
        args[1] = 14
        super
      end
    end
  end

  module ZSuperWithRestAndPost
    class A
      def m(*args, a, b)
        args
      end

      def m_modified(*args, a, b)
        args
      end
    end

    class B < A
      def m(*args, a, b)
        super
      end

      def m_modified(*args, a, b)
        args[1] = 14
        super
      end
    end
  end

  module ZSuperWithRestOthersAndPost
    class A
      def m(a, *args, b)
        args
      end

      def m_modified(a, *args, b)
        args
      end
    end

    class B < A
      def m(a, *args, b)
        super
      end

      def m_modified(a, *args, b)
        args[1] = 14
        super
      end
    end
  end

  module ZSuperWithRestReassigned
    class A
      def a(*args)
        args
      end
    end

    class B < A
      def a(*args)
        args = ["foo"]

        super
      end
    end
  end

  module ZSuperWithRestReassignedWithScalar
    class A
      def a(*args)
        args
      end
    end

    class B < A
      def a(*args)
        args = "foo"

        super
      end
    end
  end

  module ZSuperWithUnderscores
    class A
      def m(*args)
        args
      end

      def m3(*args)
        args
      end

      def m4(*args)
        args
      end

      def m_default(*args)
        args
      end

      def m_rest(*args)
        args
      end

      def m_pre_default_rest_post(*args)
        args
      end

      def m_kwrest(**kw)
        kw
      end

      def m_modified(*args)
        args
      end
    end

    class B < A
      def m(_, _)
        super
      end

      def m3(_, _, _)
        super
      end

      def m4(_, _, _, _)
        super
      end

      def m_default(_ = 0)
        super
      end

      def m_rest(*_)
        super
      end

      def m_pre_default_rest_post(_, _, _=:a, _=:b, *_, _, _)
        super
      end

      def m_kwrest(**_)
        super
      end

      def m_modified(_, _)
        _ = 14
        super
      end
    end
  end

  module ZSuperInBlock
    class A
      def m(arg:)
        arg
      end
    end

    class B < A
      def m(arg:)
        proc { super }.call
      end
    end
  end

  module Keywords
    class Arguments
      def foo(**args)
        args
      end
    end

    # ----

    class RequiredArguments < Arguments
      def foo(a:)
        super
      end
    end

    class OptionalArguments < Arguments
      def foo(b: 'b')
        super
      end
    end

    class PlaceholderArguments < Arguments
      def foo(**args)
        super
      end
    end

    # ----

    class RequiredAndOptionalArguments < Arguments
      def foo(a:, b: 'b')
        super
      end
    end

    class RequiredAndPlaceholderArguments < Arguments
      def foo(a:, **args)
        super
      end
    end

    class OptionalAndPlaceholderArguments < Arguments
      def foo(b: 'b', **args)
        super
      end
    end

    # ----

    class RequiredAndOptionalAndPlaceholderArguments < Arguments
      def foo(a:, b: 'b', **args)
        super
      end
    end
  end

  module RegularAndKeywords
    class Arguments
      def foo(a, **options)
        [a, options]
      end
    end

    # -----

    class RequiredArguments < Arguments
      def foo(a, b:)
        super
      end
    end

    class OptionalArguments < Arguments
      def foo(a, c: 'c')
        super
      end
    end

    class PlaceholderArguments < Arguments
      def foo(a, **options)
        super
      end
    end

    # -----

    class RequiredAndOptionalArguments < Arguments
      def foo(a, b:, c: 'c')
        super
      end
    end

    class RequiredAndPlaceholderArguments < Arguments
      def foo(a, b:, **options)
        super
      end
    end

    class OptionalAndPlaceholderArguments < Arguments
      def foo(a, c: 'c', **options)
        super
      end
    end

    # -----

    class RequiredAndOptionalAndPlaceholderArguments < Arguments
      def foo(a, b:, c: 'c', **options)
        super
      end
    end
  end

  module SplatAndKeywords
    class Arguments
      def foo(*args, **options)
        [args, options]
      end
    end

    class AllArguments < Arguments
      def foo(*args, **options)
        super
      end
    end
  end

  module FromBasicObject
    def __send__(name, *args, &block)
      super
    end
  end

  module IntermediateBasic
    include FromBasicObject
  end

  class IncludesFromBasic
    include FromBasicObject

    def foobar; 43; end
  end

  class IncludesIntermediate
    include IntermediateBasic

    def foobar; 42; end
  end

  module SingletonCase
    class Base
      def foobar(array)
        array << :base
      end
    end

    class Foo < Base
      def foobar(array)
        array << :foo
        super
      end
    end
  end

  module SingletonAliasCase
    class Base
      def foobar(array)
        array << :base
      end

      def alias_on_singleton
        object = self
        singleton = (class << object; self; end)
        singleton.__send__(:alias_method, :new_foobar, :foobar)
      end
    end

    class Foo < Base
      def foobar(array)
        array << :foo
        super
      end
    end
  end
end
