/* Blinker
   Copyright 2011-2019 Stefan Schuermans <stefan@blinkenarea.org>
   Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html
   a blinkenarea.org project */

#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>

#include <sstream>
#include <stdlib.h>
#include <string>
#include <string.h>

#include "InetTools.h"
#include "Udp6Addr.h"

namespace Blinker {

/// constructor
Udp6Addr::Udp6Addr()
{
  m_addr.sin6_family = AF_INET6;
  m_addr.sin6_port = htons(0);
  inet_pton(AF_INET6, "::", &m_addr.sin6_addr);
}

/// virtual destructor
Udp6Addr::~Udp6Addr()
{
}

/// comparison
//@{

int Udp6Addr::compare(const Udp6Addr &that) const
{
  if (m_addr.sin6_family < that.m_addr.sin6_family)
    return -1;
  if (m_addr.sin6_family > that.m_addr.sin6_family)
    return 1;
  int cmp = memcmp(&m_addr.sin6_addr, &that.m_addr.sin6_addr,
                   sizeof(m_addr.sin6_addr));
  if (cmp != 0)
    return cmp;
  if (ntohs(m_addr.sin6_port) < ntohs(that.m_addr.sin6_port))
    return -1;
  if (ntohs(m_addr.sin6_port) > ntohs(that.m_addr.sin6_port))
    return 1;
  return 0;
}

bool Udp6Addr::operator==(const Udp6Addr &that) const
{
  return compare(that) == 0;
}

bool Udp6Addr::operator!=(const Udp6Addr &that) const
{
  return compare(that) != 0;
}

bool Udp6Addr::operator<(const Udp6Addr &that) const
{
  return compare(that) < 0;
}

bool Udp6Addr::operator>(const Udp6Addr &that) const
{
  return compare(that) > 0;
}

bool Udp6Addr::operator<=(const Udp6Addr &that) const
{
  return compare(that) <= 0;
}

bool Udp6Addr::operator>=(const Udp6Addr &that) const
{
  return compare(that) >= 0;
}

//@}

/// return address family
int Udp6Addr::getFamily() const
{
  return AF_INET6;
}

/// return port (use this function only if absolutely necessary)
int Udp6Addr::getPort() const
{
  return ntohs(m_addr.sin6_port);
}

/// set port (use this function only if absolutely necessary)
void Udp6Addr::setPort(int port)
{
  m_addr.sin6_port = htons(port);
}

/**
 * @brief parse from string format
 * @param[in] str string format
 * @return if parsing was successful
 */
bool Udp6Addr::fromStr(const std::string &str)
{
  std::string::size_type posBraceColon;
  std::string strIp, strPort;
  struct in6_addr iaIp;
  unsigned long iPort;
  const char *szPort;
  char *szPortEnd;

  // split address into IP and port
  if (str.length() < 1 || str.at(0) != '[')
    return false;
  posBraceColon = str.find("]:");
  if (posBraceColon == std::string::npos)
    return false;
  strIp = str.substr(1, posBraceColon - 1);
  strPort = str.substr(posBraceColon + 2);

  // parse IP
  if (!inet_pton(AF_INET6, strIp.c_str(), &iaIp))
    return false;

  // parse port
  szPort = strPort.c_str();
  iPort = strtoul(szPort, &szPortEnd, 10);
  if (*szPort == 0 || *szPortEnd != 0)
    return false;

  m_addr.sin6_family = AF_INET6;
  m_addr.sin6_port = htons(iPort);
  m_addr.sin6_addr = iaIp;
  return true;
}

/**
 * @brief convert to string format
 * @return string format
 */
std::string Udp6Addr::toStr() const
{
  std::stringstream strm;
  char buf[INET6_ADDRSTRLEN];
  strm << "["
       << inet_ntop(AF_INET6, &m_addr.sin6_addr, buf, INET6_ADDRSTRLEN)
       << "]:" << ntohs(m_addr.sin6_port);
  return strm.str();
}

} // namespace Blinker

