/*
 * Copyright 2021 HIMSA II K/S - www.himsa.com.
 * Represented by EHIMA - www.ehima.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include 
#include 
#include 
#include "bta_groups.h"
#include "types/bluetooth/uuid.h"
#include "types/raw_address.h"
std::map mock_function_count_map;
namespace bluetooth {
namespace groups {
using ::testing::_;
using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::Mock;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArgPointee;
using ::testing::Test;
using bluetooth::groups::DeviceGroups;
using bluetooth::groups::DeviceGroupsCallbacks;
DeviceGroupsCallbacks* dev_callbacks;
RawAddress GetTestAddress(int index) {
  CHECK_LT(index, UINT8_MAX);
  RawAddress result = {
      {0xC0, 0xDE, 0xC0, 0xDE, 0x00, static_cast(index)}};
  return result;
}
class MockGroupsCallbacks : public DeviceGroupsCallbacks {
 public:
  MockGroupsCallbacks() = default;
  MockGroupsCallbacks(const MockGroupsCallbacks&) = delete;
  MockGroupsCallbacks& operator=(const MockGroupsCallbacks&) = delete;
  ~MockGroupsCallbacks() override = default;
  MOCK_METHOD((void), OnGroupAdded,
              (const RawAddress& address, const bluetooth::Uuid& uuid,
               int group_id),
              (override));
  MOCK_METHOD((void), OnGroupMemberAdded,
              (const RawAddress& address, int group_id), (override));
  MOCK_METHOD((void), OnGroupRemoved,
              (const bluetooth::Uuid& uuid, int group_id), (override));
  MOCK_METHOD((void), OnGroupMemberRemoved,
              (const RawAddress& address, int group_id), (override));
  MOCK_METHOD((void), OnGroupAddFromStorage,
              (const RawAddress& address, const bluetooth::Uuid& uuid,
               int group_id),
              (override));
};
class GroupsTest : public ::testing::Test {
 protected:
  void SetUp() override {
    mock_function_count_map.clear();
    callbacks.reset(new MockGroupsCallbacks());
  }
  void TearDown() override { DeviceGroups::CleanUp(callbacks.get()); }
  std::unique_ptr callbacks;
};
TEST_F(GroupsTest, test_initialize) {
  DeviceGroups::Initialize(callbacks.get());
  ASSERT_TRUE(DeviceGroups::Get());
  DeviceGroups::CleanUp(callbacks.get());
}
TEST_F(GroupsTest, test_initialize_twice) {
  DeviceGroups::Initialize(callbacks.get());
  DeviceGroups* dev_groups_p = DeviceGroups::Get();
  DeviceGroups::Initialize(callbacks.get());
  ASSERT_EQ(dev_groups_p, DeviceGroups::Get());
  DeviceGroups::CleanUp(callbacks.get());
  dev_groups_p->CleanUp(callbacks.get());
}
TEST_F(GroupsTest, test_cleanup_initialized) {
  DeviceGroups::Initialize(callbacks.get());
  DeviceGroups::CleanUp(callbacks.get());
  ASSERT_FALSE(DeviceGroups::Get());
}
TEST_F(GroupsTest, test_cleanup_uninitialized) {
  DeviceGroups::CleanUp(callbacks.get());
  ASSERT_FALSE(DeviceGroups::Get());
}
TEST_F(GroupsTest, test_groups_add_single_device) {
  EXPECT_CALL(*callbacks, OnGroupAdded(GetTestAddress(1), Uuid::kEmpty, 7));
  DeviceGroups::Initialize(callbacks.get());
  DeviceGroups::Get()->AddDevice(GetTestAddress(1), Uuid::kEmpty, 7);
  DeviceGroups::CleanUp(callbacks.get());
}
TEST_F(GroupsTest, test_groups_add_two_devices) {
  EXPECT_CALL(*callbacks, OnGroupAdded(GetTestAddress(1), _, 7));
  EXPECT_CALL(*callbacks, OnGroupMemberAdded(GetTestAddress(2), 7));
  DeviceGroups::Initialize(callbacks.get());
  DeviceGroups::Get()->AddDevice(GetTestAddress(1), Uuid::kEmpty, 7);
  DeviceGroups::Get()->AddDevice(GetTestAddress(2), Uuid::kEmpty, 7);
  DeviceGroups::CleanUp(callbacks.get());
}
TEST_F(GroupsTest, test_groups_remove_device) {
  EXPECT_CALL(*callbacks, OnGroupMemberRemoved(GetTestAddress(2), 7));
  DeviceGroups::Initialize(callbacks.get());
  DeviceGroups::Get()->AddDevice(GetTestAddress(2), Uuid::kEmpty, 7);
  DeviceGroups::Get()->RemoveDevice(GetTestAddress(2));
  ASSERT_EQ(kGroupUnknown,
            DeviceGroups::Get()->GetGroupId(GetTestAddress(2), Uuid::kEmpty));
  ASSERT_EQ(kGroupUnknown,
            DeviceGroups::Get()->GetGroupId(GetTestAddress(3), Uuid::kEmpty));
  DeviceGroups::CleanUp(callbacks.get());
}
TEST_F(GroupsTest, test_add_multiple_devices) {
  EXPECT_CALL(*callbacks, OnGroupAdded(GetTestAddress(2), Uuid::kEmpty, 7));
  EXPECT_CALL(*callbacks, OnGroupMemberAdded(_, 7)).Times(2);
  DeviceGroups::Initialize(callbacks.get());
  DeviceGroups::Get()->AddDevice(GetTestAddress(2), Uuid::kEmpty, 7);
  DeviceGroups::Get()->AddDevice(GetTestAddress(3), Uuid::kEmpty, 7);
  DeviceGroups::Get()->AddDevice(GetTestAddress(4), Uuid::kEmpty, 7);
  DeviceGroups::CleanUp(callbacks.get());
}
TEST_F(GroupsTest, test_remove_multiple_devices) {
  EXPECT_CALL(*callbacks, OnGroupMemberRemoved(_, _)).Times(3);
  DeviceGroups::Initialize(callbacks.get());
  DeviceGroups::Get()->AddDevice(GetTestAddress(2), Uuid::kEmpty, 7);
  DeviceGroups::Get()->AddDevice(GetTestAddress(3), Uuid::kEmpty, 7);
  DeviceGroups::Get()->AddDevice(GetTestAddress(4), Uuid::kEmpty, 7);
  DeviceGroups::Get()->RemoveDevice(GetTestAddress(2));
  DeviceGroups::Get()->RemoveDevice(GetTestAddress(3));
  DeviceGroups::Get()->RemoveDevice(GetTestAddress(4));
  DeviceGroups::CleanUp(callbacks.get());
}
TEST_F(GroupsTest, test_add_multiple_groups) {
  EXPECT_CALL(*callbacks, OnGroupAdded(_, _, _)).Times(2);
  DeviceGroups::Initialize(callbacks.get());
  DeviceGroups::Get()->AddDevice(GetTestAddress(1), Uuid::kEmpty, 8);
  DeviceGroups::Get()->AddDevice(GetTestAddress(1), Uuid::kEmpty, 9);
  DeviceGroups::CleanUp(callbacks.get());
}
TEST_F(GroupsTest, test_remove_multiple_groups) {
  Uuid uuid1 = Uuid::GetRandom();
  Uuid uuid2 = Uuid::GetRandom();
  ASSERT_NE(uuid1, uuid2);
  EXPECT_CALL(*callbacks, OnGroupAdded(_, _, _)).Times(2);
  DeviceGroups::Initialize(callbacks.get());
  DeviceGroups::Get()->AddDevice(GetTestAddress(1), uuid1, 8);
  DeviceGroups::Get()->AddDevice(GetTestAddress(1), uuid2, 9);
  DeviceGroups::Get()->AddDevice(GetTestAddress(2), uuid2, 9);
  EXPECT_CALL(*callbacks, OnGroupMemberRemoved(GetTestAddress(1), 8));
  EXPECT_CALL(*callbacks, OnGroupMemberRemoved(GetTestAddress(1), 9));
  EXPECT_CALL(*callbacks, OnGroupRemoved(uuid1, 8));
  EXPECT_CALL(*callbacks, OnGroupRemoved(uuid2, 9)).Times(0);
  DeviceGroups::Get()->RemoveDevice(GetTestAddress(1));
  DeviceGroups::CleanUp(callbacks.get());
}
TEST_F(GroupsTest, test_remove_device_fo_devices) {
  Uuid uuid1 = Uuid::GetRandom();
  Uuid uuid2 = Uuid::GetRandom();
  EXPECT_CALL(*callbacks, OnGroupAdded(_, _, _)).Times(2);
  DeviceGroups::Initialize(callbacks.get());
  DeviceGroups::Get()->AddDevice(GetTestAddress(1), uuid1, 8);
  DeviceGroups::Get()->AddDevice(GetTestAddress(1), uuid2, 9);
  EXPECT_CALL(*callbacks, OnGroupRemoved(uuid1, 8));
  EXPECT_CALL(*callbacks, OnGroupRemoved(uuid2, 9)).Times(0);
  DeviceGroups::Get()->RemoveDevice(GetTestAddress(1), 8);
  Mock::VerifyAndClearExpectations(&callbacks);
  EXPECT_CALL(*callbacks, OnGroupRemoved(uuid1, 8)).Times(0);
  EXPECT_CALL(*callbacks, OnGroupRemoved(uuid2, 9));
  DeviceGroups::Get()->RemoveDevice(GetTestAddress(1), 9);
}
TEST_F(GroupsTest, test_add_devices_different_group_id) {
  DeviceGroups::Initialize(callbacks.get());
  DeviceGroups::Get()->AddDevice(GetTestAddress(2), Uuid::kEmpty, 10);
  DeviceGroups::Get()->AddDevice(GetTestAddress(3), Uuid::kEmpty, 11);
  auto group_id_1 =
      DeviceGroups::Get()->GetGroupId(GetTestAddress(2), Uuid::kEmpty);
  auto group_id_2 =
      DeviceGroups::Get()->GetGroupId(GetTestAddress(3), Uuid::kEmpty);
  ASSERT_TRUE(group_id_1 != group_id_2);
  DeviceGroups::CleanUp(callbacks.get());
}
TEST_F(GroupsTest, test_group_id_assign) {
  int captured_gid1 = kGroupUnknown;
  int captured_gid2 = kGroupUnknown;
  DeviceGroups::Initialize(callbacks.get());
  EXPECT_CALL(*callbacks, OnGroupAdded(GetTestAddress(1), _, _))
      .WillOnce(SaveArg<2>(&captured_gid1));
  EXPECT_CALL(*callbacks, OnGroupAdded(GetTestAddress(2), _, _))
      .WillOnce(SaveArg<2>(&captured_gid2));
  int gid1 = DeviceGroups::Get()->AddDevice(GetTestAddress(1), Uuid::kEmpty,
                                            bluetooth::groups::kGroupUnknown);
  int gid2 = DeviceGroups::Get()->AddDevice(GetTestAddress(2), Uuid::kEmpty);
  int gid3 = DeviceGroups::Get()->AddDevice(GetTestAddress(2), Uuid::kEmpty);
  ASSERT_NE(bluetooth::groups::kGroupUnknown, gid1);
  ASSERT_NE(bluetooth::groups::kGroupUnknown, gid2);
  ASSERT_EQ(gid2, gid3);
  ASSERT_EQ(gid1, captured_gid1);
  ASSERT_EQ(gid2, captured_gid2);
  DeviceGroups::CleanUp(callbacks.get());
}
TEST_F(GroupsTest, test_storage_calls) {
  ASSERT_EQ(0, mock_function_count_map["btif_storage_load_bonded_groups"]);
  DeviceGroups::Initialize(callbacks.get());
  ASSERT_EQ(1, mock_function_count_map["btif_storage_load_bonded_groups"]);
  ASSERT_EQ(0, mock_function_count_map["btif_storage_add_groups"]);
  DeviceGroups::Get()->AddDevice(GetTestAddress(1), Uuid::kEmpty, 7);
  DeviceGroups::Get()->AddDevice(GetTestAddress(1), Uuid::kEmpty, 8);
  ASSERT_EQ(2, mock_function_count_map["btif_storage_add_groups"]);
  DeviceGroups::Get()->AddDevice(GetTestAddress(2), Uuid::kEmpty, 7);
  DeviceGroups::Get()->AddDevice(GetTestAddress(3), Uuid::kEmpty, 7);
  ASSERT_EQ(4, mock_function_count_map["btif_storage_add_groups"]);
  ASSERT_EQ(0, mock_function_count_map["btif_storage_remove_groups"]);
  DeviceGroups::Get()->RemoveDevice(GetTestAddress(1));
  DeviceGroups::Get()->RemoveDevice(GetTestAddress(2));
  DeviceGroups::Get()->RemoveDevice(GetTestAddress(3));
  ASSERT_EQ(3, mock_function_count_map["btif_storage_remove_groups"]);
  DeviceGroups::CleanUp(callbacks.get());
}
TEST_F(GroupsTest, test_storage_content) {
  int gid1 = bluetooth::groups::kGroupUnknown;
  int gid2 = bluetooth::groups::kGroupUnknown;
  Uuid uuid1 = Uuid::GetRandom();
  Uuid uuid2 = Uuid::GetRandom();
  ASSERT_NE(uuid1, uuid2);
  DeviceGroups::Initialize(callbacks.get());
  gid1 = DeviceGroups::Get()->AddDevice(GetTestAddress(1), uuid1, gid1);
  DeviceGroups::Get()->AddDevice(GetTestAddress(2), uuid1, gid1);
  gid2 = DeviceGroups::Get()->AddDevice(GetTestAddress(2), uuid2, gid2);
  ASSERT_NE(bluetooth::groups::kGroupUnknown, gid1);
  ASSERT_NE(gid1, gid2);
  std::vector dev1_storage;
  std::vector dev2_storage;
  // Store to byte buffer
  DeviceGroups::GetForStorage(GetTestAddress(1), dev1_storage);
  DeviceGroups::GetForStorage(GetTestAddress(2), dev2_storage);
  ASSERT_NE(0u, dev1_storage.size());
  ASSERT_TRUE(dev2_storage.size() > dev1_storage.size());
  // Clean it up
  DeviceGroups::CleanUp(callbacks.get());
  ASSERT_EQ(nullptr, DeviceGroups::Get());
  // Restore dev1 from the byte buffer
  DeviceGroups::Initialize(callbacks.get());
  EXPECT_CALL(*callbacks, OnGroupAdded(GetTestAddress(1), uuid1, gid1));
  DeviceGroups::AddFromStorage(GetTestAddress(1), dev1_storage);
  // Restore dev2 from the byte buffer
  EXPECT_CALL(*callbacks, OnGroupAdded(GetTestAddress(2), uuid2, gid2));
  EXPECT_CALL(*callbacks, OnGroupMemberAdded(GetTestAddress(2), gid1)).Times(1);
  DeviceGroups::AddFromStorage(GetTestAddress(2), dev2_storage);
  DeviceGroups::CleanUp(callbacks.get());
}
}  // namespace groups
}  // namespace bluetooth