// Copyright 2023 The Pigweed Authors
//
// 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
//
//     https://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 "pw_bluetooth_sapphire/internal/host/hci/advertising_report_parser.h"

#include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
#include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
#include "pw_bluetooth_sapphire/internal/host/transport/control_packets.h"
#include "pw_unit_test/framework.h"

namespace bt::hci::test {
namespace {

TEST(AdvertisingReportParserTest, EmptyReport) {
  StaticByteBuffer bytes(0x3E, 0x02, 0x02, 0x00);

  auto event = EventPacket::New(bytes.size() - sizeof(hci_spec::EventHeader));
  event->mutable_view()->mutable_data().Write(bytes);
  event->InitializeFromBuffer();

  hci::AdvertisingReportParser parser(*event);
  EXPECT_FALSE(parser.HasMoreReports());

  const hci_spec::LEAdvertisingReportData* data;
  int8_t rssi;
  EXPECT_FALSE(parser.GetNextReport(&data, &rssi));
}

TEST(AdvertisingReportParserTest, SingleReportMalformed) {
  // clang-format off

  StaticByteBuffer bytes(
      0x3E, 0x0B, 0x02, 0x01,  // HCI event header and LE Meta Event params
      0x03, 0x02,              // event_type, address_type
      0x01, 0x02, 0x03, 0x04, 0x05, 0x06,  // address
      0x00                                 // |length_data|. RSSI is missing
      );

  // clang-format on

  auto event = EventPacket::New(bytes.size() - sizeof(hci_spec::EventHeader));
  event->mutable_view()->mutable_data().Write(bytes);
  event->InitializeFromBuffer();

  hci::AdvertisingReportParser parser(*event);
  EXPECT_TRUE(parser.HasMoreReports());
  EXPECT_FALSE(parser.encountered_error());

  const hci_spec::LEAdvertisingReportData* data;
  int8_t rssi;
  EXPECT_FALSE(parser.GetNextReport(&data, &rssi));
  EXPECT_TRUE(parser.encountered_error());
}

TEST(AdvertisingReportParserTest, SingleReportNoData) {
  // clang-format off

  StaticByteBuffer bytes(
      0x3E, 0x0C, 0x02, 0x01,  // HCI event header and LE Meta Event params
      0x03, 0x02,              // event_type, address_type
      0x01, 0x02, 0x03, 0x04, 0x05, 0x06,  // address
      0x00, 0x7F                           // |length_data|, RSSI
      );

  // clang-format on

  auto event = EventPacket::New(bytes.size() - sizeof(hci_spec::EventHeader));
  event->mutable_view()->mutable_data().Write(bytes);
  event->InitializeFromBuffer();

  AdvertisingReportParser parser(*event);
  EXPECT_TRUE(parser.HasMoreReports());

  const hci_spec::LEAdvertisingReportData* data;
  int8_t rssi;
  EXPECT_TRUE(parser.GetNextReport(&data, &rssi));
  EXPECT_EQ(hci_spec::LEAdvertisingEventType::kAdvNonConnInd, data->event_type);
  EXPECT_EQ(hci_spec::LEAddressType::kPublicIdentity, data->address_type);
  EXPECT_EQ("06:05:04:03:02:01", data->address.ToString());
  EXPECT_EQ(0u, data->length_data);
  EXPECT_EQ(hci_spec::kRSSIInvalid, rssi);

  // No other reports
  EXPECT_FALSE(parser.HasMoreReports());
  EXPECT_FALSE(parser.GetNextReport(&data, &rssi));

  EXPECT_FALSE(parser.encountered_error());
}

TEST(AdvertisingReportParserTest, ReportsValidInvalid) {
  // clang-format off

  StaticByteBuffer bytes(
      0x3E, 0x16, 0x02, 0x02,  // HCI event header and LE Meta Event params
      0x03, 0x02,              // event_type, address_type
      0x01, 0x02, 0x03, 0x04, 0x05, 0x06,  // address
      0x00, 0x7F,                          // |length_data|, RSSI
      0x03, 0x02,                          // event_type, address_type
      0x01, 0x02, 0x03, 0x04, 0x05, 0x06,  // address
      0x0A, 0x7F                           // malformed |length_data|, RSSI
      );

  // clang-format on

  auto event = EventPacket::New(bytes.size() - sizeof(hci_spec::EventHeader));
  event->mutable_view()->mutable_data().Write(bytes);
  event->InitializeFromBuffer();

  AdvertisingReportParser parser(*event);
  EXPECT_TRUE(parser.HasMoreReports());
  EXPECT_FALSE(parser.encountered_error());

  const hci_spec::LEAdvertisingReportData* data;
  int8_t rssi;
  EXPECT_TRUE(parser.GetNextReport(&data, &rssi));
  EXPECT_EQ(hci_spec::LEAdvertisingEventType::kAdvNonConnInd, data->event_type);
  EXPECT_EQ(hci_spec::LEAddressType::kPublicIdentity, data->address_type);
  EXPECT_EQ("06:05:04:03:02:01", data->address.ToString());
  EXPECT_EQ(0u, data->length_data);
  EXPECT_EQ(hci_spec::kRSSIInvalid, rssi);

  // There are more reports...
  EXPECT_TRUE(parser.HasMoreReports());
  EXPECT_FALSE(parser.encountered_error());

  // ...but the report is malformed.
  EXPECT_FALSE(parser.GetNextReport(&data, &rssi));
  EXPECT_TRUE(parser.encountered_error());
}

TEST(AdvertisingReportParserTest, ReportsAllValid) {
  // clang-format off

  StaticByteBuffer bytes(
      0x3E, 0x28, 0x02, 0x03,              // HCI event header and LE Meta Event params
      0x03, 0x02,                          // event_type, address_type
      0x01, 0x02, 0x03, 0x04, 0x05, 0x06,  // address
      0x00, 0x7F,                          // |length_data|, RSSI

      0x00, 0x01,                          // event_type, address_type
      0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,  // address
      0x03, 0x01, 0x02, 0x03, 0x0F,        // |length_data|, data, RSSI

      0x01, 0x00,                          // event_type, address_type
      0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,  // address
      0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // |length_data|, data
      0x01                                 // RSSI
      );

  // clang-format on

  auto event = EventPacket::New(bytes.size() - sizeof(hci_spec::EventHeader));
  event->mutable_view()->mutable_data().Write(bytes);
  event->InitializeFromBuffer();

  AdvertisingReportParser parser(*event);
  EXPECT_TRUE(parser.HasMoreReports());

  const hci_spec::LEAdvertisingReportData* data;
  int8_t rssi;
  EXPECT_TRUE(parser.GetNextReport(&data, &rssi));
  EXPECT_EQ(hci_spec::LEAdvertisingEventType::kAdvNonConnInd, data->event_type);
  EXPECT_EQ(hci_spec::LEAddressType::kPublicIdentity, data->address_type);
  EXPECT_EQ("06:05:04:03:02:01", data->address.ToString());
  EXPECT_EQ(0u, data->length_data);
  EXPECT_EQ(hci_spec::kRSSIInvalid, rssi);

  // There are more reports
  EXPECT_TRUE(parser.HasMoreReports());
  EXPECT_TRUE(parser.GetNextReport(&data, &rssi));
  EXPECT_EQ(hci_spec::LEAdvertisingEventType::kAdvInd, data->event_type);
  EXPECT_EQ(hci_spec::LEAddressType::kRandom, data->address_type);
  EXPECT_EQ("0C:0B:0A:09:08:07", data->address.ToString());
  EXPECT_EQ(3, data->length_data);
  EXPECT_TRUE(ContainersEqual(std::array<uint8_t, 3>{{0x01, 0x02, 0x03}},
                              data->data,
                              data->length_data));
  EXPECT_EQ(15, rssi);

  // There are more reports
  EXPECT_TRUE(parser.HasMoreReports());
  EXPECT_TRUE(parser.GetNextReport(&data, &rssi));
  EXPECT_EQ(hci_spec::LEAdvertisingEventType::kAdvDirectInd, data->event_type);
  EXPECT_EQ(hci_spec::LEAddressType::kPublic, data->address_type);
  EXPECT_EQ("12:11:10:0F:0E:0D", data->address.ToString());
  EXPECT_EQ(5, data->length_data);
  EXPECT_TRUE(
      ContainersEqual(std::array<uint8_t, 5>{{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}},
                      data->data,
                      data->length_data));
  EXPECT_EQ(1, rssi);

  // No more reports.
  EXPECT_FALSE(parser.HasMoreReports());
  EXPECT_FALSE(parser.GetNextReport(&data, &rssi));

  EXPECT_FALSE(parser.encountered_error());
}

TEST(AdvertisingReportParserTest, ReportCountLessThanPayloadSize) {
  // clang-format off

  StaticByteBuffer bytes(
      0x3E, 0x28, 0x02,  // HCI event header and LE Meta Event param
      0x01,              // Event count is 1, even though packet contains 3
      0x03, 0x02,        // event_type, address_type
      0x01, 0x02, 0x03, 0x04, 0x05, 0x06,  // address
      0x00, 0x7F,                          // |length_data|, RSSI

      0x00, 0x01,                          // event_type, address_type
      0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,  // address
      0x03, 0x01, 0x02, 0x03, 0x0F,        // |length_data|, data, RSSI

      0x01, 0x00,                          // event_type, address_type
      0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,  // address
      0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // |length_data|, data
      0x01                                 // RSSI
      );

  // clang-format on

  auto event = EventPacket::New(bytes.size() - sizeof(hci_spec::EventHeader));
  event->mutable_view()->mutable_data().Write(bytes);
  event->InitializeFromBuffer();

  AdvertisingReportParser parser(*event);
  EXPECT_TRUE(parser.HasMoreReports());
  EXPECT_FALSE(parser.encountered_error());

  const hci_spec::LEAdvertisingReportData* data;
  int8_t rssi;
  EXPECT_TRUE(parser.GetNextReport(&data, &rssi));
  EXPECT_EQ(hci_spec::LEAdvertisingEventType::kAdvNonConnInd, data->event_type);
  EXPECT_EQ(hci_spec::LEAddressType::kPublicIdentity, data->address_type);
  EXPECT_EQ("06:05:04:03:02:01", data->address.ToString());
  EXPECT_EQ(0u, data->length_data);
  EXPECT_EQ(hci_spec::kRSSIInvalid, rssi);

  // Since the packet is malformed (the event payload contains 3 reports while
  // the header states there is only 1) this should return false.
  EXPECT_FALSE(parser.encountered_error());
  EXPECT_FALSE(parser.HasMoreReports());
  EXPECT_TRUE(parser.encountered_error());

  EXPECT_FALSE(parser.GetNextReport(&data, &rssi));
  EXPECT_TRUE(parser.encountered_error());
}

TEST(AdvertisingReportParserTest, ReportCountGreaterThanPayloadSize) {
  // clang-format off

  StaticByteBuffer bytes(
      0x3E, 0x0C, 0x02,  // HCI event header and LE Meta Event param
      0x02,              // Event count is 2, even though packet contains 1
      0x03, 0x02,        // event_type, address_type
      0x01, 0x02, 0x03, 0x04, 0x05, 0x06,  // address
      0x00, 0x7F                           // |length_data|, RSSI
      );

  // clang-format on

  auto event = EventPacket::New(bytes.size() - sizeof(hci_spec::EventHeader));
  event->mutable_view()->mutable_data().Write(bytes);
  event->InitializeFromBuffer();

  AdvertisingReportParser parser(*event);
  EXPECT_TRUE(parser.HasMoreReports());

  const hci_spec::LEAdvertisingReportData* data;
  int8_t rssi;
  EXPECT_TRUE(parser.GetNextReport(&data, &rssi));
  EXPECT_EQ(hci_spec::LEAdvertisingEventType::kAdvNonConnInd, data->event_type);
  EXPECT_EQ(hci_spec::LEAddressType::kPublicIdentity, data->address_type);
  EXPECT_EQ("06:05:04:03:02:01", data->address.ToString());
  EXPECT_EQ(0u, data->length_data);
  EXPECT_EQ(hci_spec::kRSSIInvalid, rssi);

  EXPECT_FALSE(parser.encountered_error());

  // Since the packet is malformed (the event payload contains 1 report while
  // the header states there are 2) this should return false.
  EXPECT_FALSE(parser.HasMoreReports());
  EXPECT_FALSE(parser.GetNextReport(&data, &rssi));

  EXPECT_TRUE(parser.encountered_error());
}

}  // namespace
}  // namespace bt::hci::test
