//
//  interface.cpp
//
//  Copyright (c) 2019 2020 Andrea Bondavalli. All rights reserved.
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//  MIT License
//

#include <boost/asio.hpp>
#include <utility>

#include "log.hpp"

using namespace boost::asio;
using namespace boost::asio::ip;

std::pair<uint32_t, std::string> get_interface_ip(
    const std::string& interface_name) {
  int fd = socket(AF_INET, SOCK_DGRAM, 0);
  if (fd < 0) {
    BOOST_LOG_TRIVIAL(warning)
        << "Cannot retrieve IP address for interface " << interface_name;
    return {0, ""};
  }
  struct ifreq ifr;
  ifr.ifr_addr.sa_family = AF_INET;
  strncpy(ifr.ifr_name, interface_name.c_str(), IFNAMSIZ - 1);
  if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
    close(fd);
    BOOST_LOG_TRIVIAL(warning)
        << "Cannot retrieve IP address for interface " << interface_name;
    return {0, ""};
  }
  close(fd);

  struct sockaddr_in* sockaddr =
      reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_addr);
  uint32_t addr = ntohl(sockaddr->sin_addr.s_addr);
  std::string str_addr(ip::address_v4(addr).to_string());
  /*BOOST_LOG_TRIVIAL(debug) << "interface " << interface_name
                             << " IP address " << str_addr;*/

  return {addr, str_addr};
}

std::pair<std::array<uint8_t, 6>, std::string> get_interface_mac(
    const std::string& interface_name) {
  std::array<uint8_t, 6> mac{0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  int fd = socket(AF_INET, SOCK_DGRAM, 0);
  if (fd < 0) {
    BOOST_LOG_TRIVIAL(error)
        << "Cannot retrieve MAC address for interface " << interface_name;
    return {mac, ""};
  }

  struct ifreq ifr;
  ifr.ifr_addr.sa_family = AF_INET;
  strncpy(ifr.ifr_name, interface_name.c_str(), IFNAMSIZ - 1);
  if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
    close(fd);
    BOOST_LOG_TRIVIAL(error)
        << "Cannot retrieve MAC address for interface " << interface_name;
    return {mac, ""};
  }
  close(fd);

  uint8_t* sa = reinterpret_cast<uint8_t*>(ifr.ifr_hwaddr.sa_data);
  std::copy(sa, sa + 8, std::begin(mac));

  char str_mac[18];
  sprintf(str_mac, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", mac[0], mac[1], mac[2],
          mac[3], mac[4], mac[5]);
  /*BOOST_LOG_TRIVIAL(debug) << "interface " << interface_name
                             << " MAC address " << str_mac;*/

  return {mac, str_mac};
}

int get_interface_index(const std::string& interface_name) {
  int fd = socket(AF_INET, SOCK_DGRAM, 0);
  if (fd < 0) {
    BOOST_LOG_TRIVIAL(warning)
        << "Cannot retrieve index for interface " << interface_name;
    return -1;
  }
  struct ifreq ifr;
  ifr.ifr_addr.sa_family = AF_INET;
  strncpy(ifr.ifr_name, interface_name.c_str(), IFNAMSIZ - 1);
  if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
    close(fd);
    BOOST_LOG_TRIVIAL(warning)
        << "Cannot retrieve index for interface " << interface_name;
    return -1;
  }
  close(fd);
  /*BOOST_LOG_TRIVIAL(debug) << "interface " << interface_name
                             << " index " << ifr.ifr_ifindex;*/

  return ifr.ifr_ifindex;
}