#include <array>
#include <cstdint>
#include <vector>

#include <gtest/gtest.h>

#include "origincar_base/serial_frame_parser.hpp"

using origincar_base_core::SerialFrameParser;

namespace
{
std::array<uint8_t, SerialFrameParser::kFrameSize> makeFrame(uint8_t seed)
{
  std::array<uint8_t, SerialFrameParser::kFrameSize> frame{};
  frame[0] = 0x7B;
  frame[23] = 0x7D;
  for (std::size_t index = 1; index < 23; ++index) {
    frame[index] = static_cast<uint8_t>(seed + index);
  }
  return frame;
}
}  // namespace

TEST(SerialFrameParserTest, ReassemblesFrameAcrossPartialReads)
{
  SerialFrameParser parser;
  const auto expected = makeFrame(10);
  std::array<uint8_t, SerialFrameParser::kFrameSize> actual{};

  parser.append(expected.data(), 7);
  EXPECT_FALSE(parser.popFrame(&actual));

  parser.append(expected.data() + 7, expected.size() - 7);
  ASSERT_TRUE(parser.popFrame(&actual));
  EXPECT_EQ(actual, expected);
}

TEST(SerialFrameParserTest, DiscardsNoiseBeforeValidFrame)
{
  SerialFrameParser parser;
  const auto expected = makeFrame(20);
  const std::vector<uint8_t> noise{0x00, 0x01, 0x7D, 0x55};
  std::array<uint8_t, SerialFrameParser::kFrameSize> actual{};

  parser.append(noise.data(), noise.size());
  parser.append(expected.data(), expected.size());

  ASSERT_TRUE(parser.popFrame(&actual));
  EXPECT_EQ(actual, expected);
}

TEST(SerialFrameParserTest, RejectsInvalidTailAndContinuesSearching)
{
  SerialFrameParser parser;
  auto invalid = makeFrame(30);
  invalid[23] = 0x00;
  const auto expected = makeFrame(40);
  std::array<uint8_t, SerialFrameParser::kFrameSize> actual{};

  parser.append(invalid.data(), invalid.size());
  parser.append(expected.data(), expected.size());

  ASSERT_TRUE(parser.popFrame(&actual));
  EXPECT_EQ(actual, expected);
}
