require("director_protected")

-- Create test objects
local b = director_protected.Bar()
local f = b:create()

-- FooBar: overrides ping
local fb = director_protected.Bar()
swig_override(fb, 'ping', function(self)
  return "FooBar::ping();"
end)

-- FooBar2: overrides ping and pang
local fb2 = director_protected.Bar()
swig_derive(fb2, {
  ping = function(self)
    return "FooBar2::ping();"
  end,
  pang = function(self)
    return "FooBar2::pang();"
  end
})

-- FooBar3: overrides cheer
local fb3 = director_protected.Bar()
swig_override(fb3, 'cheer', function(self)
  return "FooBar3::cheer();"
end)

-- Test overridden used() calls
assert(fb:used() == "Foo::pang();Bar::pong();Foo::pong();FooBar::ping();",
  "bad FooBar::used: " .. fb:used())
assert(fb2:used() == "FooBar2::pang();Bar::pong();Foo::pong();FooBar2::ping();",
  "bad FooBar2::used: " .. fb2:used())

-- Test pong() calls
assert(b:pong() == "Bar::pong();Foo::pong();Bar::ping();",
  "bad Bar::pong: " .. b:pong())
assert(f:pong() == "Bar::pong();Foo::pong();Bar::ping();",
  "bad Foo::pong: " .. f:pong())
assert(fb:pong() == "Bar::pong();Foo::pong();FooBar::ping();",
  "bad FooBar::pong: " .. fb:pong())

-- Test cheer override
assert(fb3:cheer() == "FooBar3::cheer();",
  "bad fb3::cheer: " .. fb3:cheer())

-- Test callping/callcheer (call protected methods internally from C++)
assert(fb2:callping() == "FooBar2::ping();",
  "bad fb2:callping: " .. fb2:callping())
assert(fb2:callcheer() == "FooBar2::pang();Bar::pong();Foo::pong();FooBar2::ping();",
  "bad fb2:callcheer: " .. fb2:callcheer())
assert(fb3:callping() == "Bar::ping();",
  "bad fb3:callping: " .. fb3:callping())
assert(fb3:callcheer() == "FooBar3::cheer();",
  "bad fb3:callcheer: " .. fb3:callcheer())

-- Note: In Lua, director objects CAN call protected methods (they go through the
-- SwigDirector public accessor). This differs from Python where protected access
-- is additionally restricted via swig_get_inner(). This is by design.
-- Director objects (b, fb, fb2, fb3) can call protected methods:
assert(b:ping() == "Bar::ping();", "bad b:ping")
assert(b:cheer() == "Foo::pang();Bar::pong();Foo::pong();Bar::ping();", "bad b:cheer")

-- Non-director objects (f, returned by Bar::create() which does new Bar() in C++)
-- CANNOT call protected methods - the darg null check catches this:
local ret, msg = pcall(function() f:ping() end)
assert(not ret, "f:ping() should fail - f is not a director")

ret, msg = pcall(function() f:pang() end)
assert(not ret, "f:pang() should fail - f is not a director")

ret, msg = pcall(function() f:cheer() end)
assert(not ret, "f:cheer() should fail - f is not a director")

-- NestedDerived is never wrapped (protected nested class); check identify() still dispatches to it.
local outer = director_protected.NestedOuter()
local base = outer:makeDerived()
assert(base:identify() == 1, "bad NestedOuter:makeDerived():identify()")
