#include "vehicle_agent_gateway/tcp_client.hpp"

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

#include <cstring>

namespace vehicle_agent_gateway
{

TcpClient::TcpClient(const std::string & host, uint16_t port, int timeout_ms)
: host_(host), port_(port), timeout_ms_(timeout_ms)
{
}

int TcpClient::connect_to_server() const
{
  int fd = ::socket(AF_INET, SOCK_STREAM, 0);
  if (fd < 0) {
    return -1;
  }

  // 设置接收超时，避免 recv_frame 永久阻塞。
  struct timeval tv;
  tv.tv_sec = timeout_ms_ / 1000;
  tv.tv_usec = (timeout_ms_ % 1000) * 1000;
  ::setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
  ::setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));

  struct sockaddr_in addr;
  std::memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(port_);

  if (::inet_pton(AF_INET, host_.c_str(), &addr.sin_addr) <= 0) {
    ::close(fd);
    return -1;
  }

  if (::connect(fd, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) < 0) {
    ::close(fd);
    return -1;
  }

  return fd;
}

bool TcpClient::send_recv(
  MsgType req_type,
  const std::string & req_payload,
  MsgType rsp_type,
  std::string & rsp_payload)
{
  int fd = connect_to_server();
  if (fd < 0) {
    return false;
  }

  bool ok = send_frame(fd, req_type, req_payload);
  if (ok) {
    MsgType recv_type;
    ok = recv_frame(fd, recv_type, rsp_payload);
    // 校验响应帧类型是否符合预期。
    if (ok && recv_type != rsp_type) {
      ok = false;
    }
  }

  ::close(fd);
  return ok;
}

}  // namespace vehicle_agent_gateway
