"""
Test lldb data formatter for libc++ std::unique_ptr.
"""


import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil


class TestCase(TestBase):
    def make_expected_type(self, pointee_type: str, qualifiers: str = "") -> str:
        if qualifiers:
            qualifiers = " " + qualifiers

        if self.expectedCompiler(["clang"]) and self.expectedCompilerVersion(
            [">", "16.0"]
        ):
            return f"std::unique_ptr<{pointee_type}>{qualifiers}"
        else:
            return f"std::unique_ptr<{pointee_type}, std::default_delete<{pointee_type}> >{qualifiers}"

    def make_expected_basic_string_ptr(self) -> str:
        if self.expectedCompiler(["clang"]) and self.expectedCompilerVersion(
            [">", "16.0"]
        ):
            return f"std::unique_ptr<std::string>"
        else:
            return (
                "std::unique_ptr<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, "
                "std::default_delete<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >"
            )

    @add_test_categories(["libc++"])
    def test_unique_ptr_variables(self):
        """Test `frame variable` output for `std::unique_ptr` types."""
        self.build()

        lldbutil.run_to_source_breakpoint(
            self, "// break here", lldb.SBFileSpec("main.cpp")
        )

        valobj = self.expect_var_path(
            "up_empty",
            type=self.make_expected_type("int"),
            summary="nullptr",
            children=[ValueCheck(name="pointer")],
        )
        self.assertEqual(
            valobj.child[0].GetValueAsUnsigned(lldb.LLDB_INVALID_ADDRESS), 0
        )

        self.expect(
            "frame variable *up_empty", substrs=["(int) *up_empty = <parent is NULL>"]
        )

        valobj = self.expect_var_path(
            "up_int",
            type=self.make_expected_type("int"),
            summary="10",
            children=[ValueCheck(name="pointer")],
        )
        self.assertNotEqual(valobj.child[0].unsigned, 0)

        valobj = self.expect_var_path(
            "up_int_ref",
            type=self.make_expected_type("int", qualifiers="&"),
            summary="10",
            children=[ValueCheck(name="pointer")],
        )
        self.assertNotEqual(valobj.child[0].unsigned, 0)

        valobj = self.expect_var_path(
            "up_int_ref_ref",
            type=self.make_expected_type("int", qualifiers="&&"),
            summary="10",
            children=[ValueCheck(name="pointer")],
        )
        self.assertNotEqual(valobj.child[0].unsigned, 0)

        valobj = self.expect_var_path(
            "up_str",
            type=self.make_expected_basic_string_ptr(),
            summary='"hello"',
            children=[ValueCheck(name="pointer", summary='"hello"')],
        )

        valobj = self.expect_var_path("up_user", type=self.make_expected_type("User"))
        self.assertRegex(valobj.summary, "^User @ 0x0*[1-9a-f][0-9a-f]+$")
        self.assertNotEqual(valobj.child[0].unsigned, 0)

        valobj = self.expect_var_path(
            "*up_user",
            type="User",
            children=[
                ValueCheck(name="id", value="30"),
                ValueCheck(name="name", summary='"steph"'),
            ],
        )
        self.assertEqual(str(valobj), '(User) *pointer = (id = 30, name = "steph")')

        valobj = self.expect_var_path(
            "up_non_empty_deleter",
            type="std::unique_ptr<int, NonEmptyIntDeleter>",
            summary="1234",
            children=[
                ValueCheck(name="pointer"),
                ValueCheck(
                    name="deleter", children=[ValueCheck(name="dummy_", value="9999")]
                ),
            ],
        )
        self.assertNotEqual(valobj.child[0].unsigned, 0)

        self.expect_var_path("up_user->id", type="int", value="30")
        self.expect_var_path("up_user->name", type="std::string", summary='"steph"')
