/******************************************************************************
 *
 *  Copyright 2017 The Android Open Source Project
 *
 *  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 <bluetooth/types/address.h>
#include <gtest/gtest.h>

static const char* test_addr = "12:34:56:78:9a:bc";
static const char* test_addr2 = "cb:a9:87:65:43:21";

TEST(RawAddressUnittest, test_constructor_array) {
  RawAddress bdaddr({0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc});

  ASSERT_EQ(0x12, bdaddr.address[0]);
  ASSERT_EQ(0x34, bdaddr.address[1]);
  ASSERT_EQ(0x56, bdaddr.address[2]);
  ASSERT_EQ(0x78, bdaddr.address[3]);
  ASSERT_EQ(0x9A, bdaddr.address[4]);
  ASSERT_EQ(0xBC, bdaddr.address[5]);

  std::string ret = bdaddr.ToString();

  ASSERT_STREQ(test_addr, ret.c_str());
}

TEST(RawAddressUnittest, test_is_empty) {
  RawAddress empty = RawAddress::FromString("00:00:00:00:00:00").value();
  ASSERT_TRUE(empty.IsEmpty());

  RawAddress not_empty = RawAddress::FromString("00:00:00:00:00:01").value();
  ASSERT_FALSE(not_empty.IsEmpty());
}

TEST(RawAddressUnittest, test_to_from_str) {
  RawAddress bdaddr = RawAddress::FromString(test_addr).value();

  ASSERT_EQ(0x12, bdaddr.address[0]);
  ASSERT_EQ(0x34, bdaddr.address[1]);
  ASSERT_EQ(0x56, bdaddr.address[2]);
  ASSERT_EQ(0x78, bdaddr.address[3]);
  ASSERT_EQ(0x9A, bdaddr.address[4]);
  ASSERT_EQ(0xBC, bdaddr.address[5]);

  std::string ret = bdaddr.ToString();

  ASSERT_STREQ(test_addr, ret.c_str());
}

TEST(RawAddressUnittest, test_from_octets) {
  static const uint8_t test_addr_array[] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc};

  RawAddress bdaddr = RawAddress::FromOctets(test_addr_array);

  ASSERT_EQ(0x12, bdaddr.address[0]);
  ASSERT_EQ(0x34, bdaddr.address[1]);
  ASSERT_EQ(0x56, bdaddr.address[2]);
  ASSERT_EQ(0x78, bdaddr.address[3]);
  ASSERT_EQ(0x9A, bdaddr.address[4]);
  ASSERT_EQ(0xBC, bdaddr.address[5]);

  std::string ret = bdaddr.ToString();

  ASSERT_STREQ(test_addr, ret.c_str());
}

TEST(RawAddressTest, test_equals) {
  RawAddress bdaddr1 = RawAddress::FromString(test_addr).value();
  RawAddress bdaddr2 = RawAddress::FromString(test_addr).value();
  EXPECT_TRUE(bdaddr1 == bdaddr2);
  EXPECT_FALSE(bdaddr1 != bdaddr2);
  EXPECT_TRUE(bdaddr1 == bdaddr1);
  EXPECT_FALSE(bdaddr1 != bdaddr1);

  RawAddress bdaddr3 = RawAddress::FromString(test_addr2).value();
  EXPECT_FALSE(bdaddr2 == bdaddr3);
  EXPECT_TRUE(bdaddr2 != bdaddr3);
}

TEST(RawAddressTest, test_less_than) {
  RawAddress bdaddr1 = RawAddress::FromString(test_addr).value();
  RawAddress bdaddr2 = RawAddress::FromString(test_addr).value();
  EXPECT_FALSE(bdaddr1 < bdaddr2);
  EXPECT_FALSE(bdaddr1 < bdaddr1);

  RawAddress bdaddr3 = RawAddress::FromString(test_addr2).value();
  EXPECT_TRUE(bdaddr2 < bdaddr3);
  EXPECT_FALSE(bdaddr3 < bdaddr2);
}

TEST(RawAddressTest, test_more_than) {
  RawAddress bdaddr1 = RawAddress::FromString(test_addr).value();
  RawAddress bdaddr2 = RawAddress::FromString(test_addr).value();
  EXPECT_FALSE(bdaddr1 > bdaddr2);
  EXPECT_FALSE(bdaddr1 > bdaddr1);

  RawAddress bdaddr3 = RawAddress::FromString(test_addr2).value();
  EXPECT_FALSE(bdaddr2 > bdaddr3);
  EXPECT_TRUE(bdaddr3 > bdaddr2);
}

TEST(RawAddressTest, test_less_than_or_equal) {
  RawAddress bdaddr1 = RawAddress::FromString(test_addr).value();
  RawAddress bdaddr2 = RawAddress::FromString(test_addr).value();
  EXPECT_TRUE(bdaddr1 <= bdaddr2);
  EXPECT_TRUE(bdaddr1 <= bdaddr1);

  RawAddress bdaddr3 = RawAddress::FromString(test_addr2).value();
  EXPECT_TRUE(bdaddr2 <= bdaddr3);
  EXPECT_FALSE(bdaddr3 <= bdaddr2);
}

TEST(RawAddressTest, test_more_than_or_equal) {
  RawAddress bdaddr1 = RawAddress::FromString(test_addr).value();
  RawAddress bdaddr2 = RawAddress::FromString(test_addr).value();
  EXPECT_TRUE(bdaddr1 >= bdaddr2);
  EXPECT_TRUE(bdaddr1 >= bdaddr1);

  RawAddress bdaddr3 = RawAddress::FromString(test_addr2).value();
  EXPECT_FALSE(bdaddr2 >= bdaddr3);
  EXPECT_TRUE(bdaddr3 >= bdaddr2);
}

TEST(RawAddressTest, test_copy) {
  RawAddress bdaddr1 = RawAddress::FromString(test_addr).value();
  RawAddress bdaddr2 = bdaddr1;

  EXPECT_TRUE(bdaddr1 == bdaddr2);
}

TEST(RawAddressTest, IsValidAddress) {
  EXPECT_FALSE(RawAddress::IsValidAddress(""));
  EXPECT_FALSE(RawAddress::IsValidAddress("000000000000"));
  EXPECT_FALSE(RawAddress::IsValidAddress("00:00:00:00:0000"));
  EXPECT_FALSE(RawAddress::IsValidAddress("00:00:00:00:00:0"));
  EXPECT_FALSE(RawAddress::IsValidAddress("00:00:00:00:00:0;"));
  EXPECT_TRUE(RawAddress::IsValidAddress("00:00:00:00:00:00"));
  EXPECT_TRUE(RawAddress::IsValidAddress("AB:cd:00:00:00:00"));
  EXPECT_FALSE(RawAddress::IsValidAddress("aB:cD:eF:Gh:iJ:Kl"));
}

TEST(RawAddressTest, BdAddrFromString) {
  EXPECT_EQ(RawAddress::FromString("00:00:00:00:00:00"), RawAddress::kEmpty);
  EXPECT_EQ(RawAddress::FromString("ab:01:4C:d5:21:9f"),
            RawAddress({0xab, 0x01, 0x4c, 0xd5, 0x21, 0x9f}));
  EXPECT_EQ(RawAddress::FromString("ab:01:4C:d5:21"), std::nullopt);
  EXPECT_EQ(RawAddress::FromString("ab:01:4C:d5:21aaa"), std::nullopt);
  EXPECT_EQ(RawAddress::FromString("ab:01:4C:d5:21:xx"), std::nullopt);
}

TEST(RawAddressTest, BdAddrFromArray) {
  std::array<uint8_t, 6> mac = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
  RawAddress bdaddr(mac);

  std::string ret = bdaddr.ToString();
  ASSERT_STREQ("11:22:33:44:55:66", ret.c_str());
}

TEST(RawAddress, ToStringTest) {
  std::array<uint8_t, 6> addr_bytes = {0x11, 0x22, 0x33, 0x44, 0x55, 0xab};
  RawAddress addr(addr_bytes);
  const std::string redacted_loggable_str = "xx:xx:xx:xx:55:ab";
  const std::string loggbable_str = "11:22:33:44:55:ab";
  std::string ret1 = addr.ToString();
  ASSERT_STREQ(ret1.c_str(), loggbable_str.c_str());
  std::string ret2 = addr.ToRedactedStringForLogging();
  ASSERT_STREQ(ret2.c_str(), redacted_loggable_str.c_str());
}
