#include "vehicle_agent_gateway/proto_frame.hpp"

#include <arpa/inet.h>
#include <unistd.h>

#include <cstring>

namespace vehicle_agent_gateway
{

// 以小端字节序把 uint32_t 写入 buf。
static void write_le32(uint8_t * buf, uint32_t v)
{
  buf[0] = static_cast<uint8_t>(v & 0xFF);
  buf[1] = static_cast<uint8_t>((v >> 8) & 0xFF);
  buf[2] = static_cast<uint8_t>((v >> 16) & 0xFF);
  buf[3] = static_cast<uint8_t>((v >> 24) & 0xFF);
}

// 以小端字节序从 buf 读取 uint32_t。
static uint32_t read_le32(const uint8_t * buf)
{
  return static_cast<uint32_t>(buf[0]) |
         (static_cast<uint32_t>(buf[1]) << 8) |
         (static_cast<uint32_t>(buf[2]) << 16) |
         (static_cast<uint32_t>(buf[3]) << 24);
}

// 确保把 buf 里所有字节都写到 fd，处理 EINTR 重试。
static bool write_all(int fd, const uint8_t * buf, size_t len)
{
  size_t written = 0;
  while (written < len) {
    ssize_t n = ::write(fd, buf + written, len - written);
    if (n <= 0) {
      return false;
    }
    written += static_cast<size_t>(n);
  }
  return true;
}

// 确保从 fd 读满 len 字节，处理 EINTR 重试。
static bool read_all(int fd, uint8_t * buf, size_t len)
{
  size_t got = 0;
  while (got < len) {
    ssize_t n = ::read(fd, buf + got, len - got);
    if (n <= 0) {
      return false;
    }
    got += static_cast<size_t>(n);
  }
  return true;
}

bool send_frame(int fd, MsgType type, const std::string & payload)
{
  // 帧头：8字节（4字节 msg_type + 4字节 payload_len），均小端。
  uint8_t header[8];
  write_le32(header, static_cast<uint32_t>(type));
  write_le32(header + 4, static_cast<uint32_t>(payload.size()));

  if (!write_all(fd, header, 8)) {
    return false;
  }
  if (!payload.empty()) {
    if (!write_all(fd, reinterpret_cast<const uint8_t *>(payload.data()), payload.size())) {
      return false;
    }
  }
  return true;
}

bool recv_frame(int fd, MsgType & out_type, std::string & out_payload)
{
  uint8_t header[8];
  if (!read_all(fd, header, 8)) {
    return false;
  }

  out_type = static_cast<MsgType>(read_le32(header));
  const uint32_t payload_len = read_le32(header + 4);

  out_payload.resize(payload_len);
  if (payload_len > 0) {
    if (!read_all(fd, reinterpret_cast<uint8_t *>(out_payload.data()), payload_len)) {
      return false;
    }
  }
  return true;
}

}  // namespace vehicle_agent_gateway
