// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/testing/testing.h"
#include "impeller/toolkit/interop/impeller.h"
#include "impeller/toolkit/interop/object.h"

// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks)

namespace impeller::interop::testing {

IMPELLER_DEFINE_HANDLE(FlagHandle);

class FlagObject final
    : public Object<FlagObject, IMPELLER_INTERNAL_HANDLE_NAME(FlagHandle)> {
 public:
  explicit FlagObject(bool& destruction_flag)
      : destruction_flag_(destruction_flag) {
    FML_CHECK(!destruction_flag_) << "Destruction flag must be cleared.";
  }

  ~FlagObject() {
    FML_CHECK(!destruction_flag_) << "Already destructed.";
    destruction_flag_ = true;
  }

 private:
  bool& destruction_flag_;
};

IMPELLER_DEFINE_HANDLE(TestHandle);

class TestObject final
    : public Object<TestObject, IMPELLER_INTERNAL_HANDLE_NAME(TestHandle)> {
 public:
  TestObject(int arg1, double arg2, char arg3)
      : arg1_(arg1), arg2_(arg2), arg3_(arg3) {}

  ~TestObject() = default;

  auto GetArg1() const { return arg1_; }

  auto GetArg2() const { return arg2_; }

  auto GetArg3() const { return arg3_; }

 private:
  int arg1_ = {};
  double arg2_ = {};
  char arg3_ = {};
};

TEST(InteropObjectTest, CanCreateScoped) {
  bool destructed = false;
  {
    auto object = Adopt(new FlagObject(destructed));  //
  }
  ASSERT_TRUE(destructed);

  destructed = false;
  {
    auto object = Ref(new FlagObject(destructed));
    // New objects start with retain count of 1.
    object->Release();
  }
  ASSERT_TRUE(destructed);
}

TEST(InteropObjectTest, CanCreate) {
  auto object = Create<TestObject>(1, 1.3, 'c');
  ASSERT_EQ(object->GetArg1(), 1);
  ASSERT_EQ(object->GetArg2(), 1.3);
  ASSERT_EQ(object->GetArg3(), 'c');
}

TEST(InteropObjectTest, CanCopyAssignMove) {
  auto o = Create<TestObject>(1, 2.3, 'd');
  ASSERT_EQ(o->GetRefCountForTests(), 1u);
  {
    auto o1 = o;  // NOLINT(performance-unnecessary-copy-initialization)
    ASSERT_EQ(o->GetRefCountForTests(), 2u);
    auto o2 = o;  // NOLINT(performance-unnecessary-copy-initialization)
    ASSERT_EQ(o->GetRefCountForTests(), 3u);
    auto o3 = o1;  // NOLINT(performance-unnecessary-copy-initialization)
    ASSERT_EQ(o->GetRefCountForTests(), 4u);
  }
  ASSERT_EQ(o->GetRefCountForTests(), 1u);

  {
    auto o1(o);  // NOLINT(performance-unnecessary-copy-initialization)
    ASSERT_EQ(o->GetRefCountForTests(), 2u);
    ASSERT_EQ(o1->GetRefCountForTests(), 2u);
  }

  auto move_o = std::move(o);
  ASSERT_EQ(move_o->GetRefCountForTests(), 1u);
}

}  // namespace impeller::interop::testing

// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
