/*
    PIP - Platform Independent Primitives
	Network address
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "pinetworkaddress.h"

// clang-format off
#ifdef QNX
#  include <netdb.h>
#else
#  ifdef WINDOWS
#	include <winsock2.h>
#  else
#	include <arpa/inet.h>
#	include <netdb.h>
#	ifdef LWIP
#	  include <lwip/sockets.h>
#	endif
#  endif
#endif
// clang-format on


//! \class PISystemTime pisystemtime.h
//! \details
//! \~english \section PISystemTime_sec0 Synopsis
//! \~russian \section PISystemTime_sec0 Краткий обзор
//! \~english
//! This class provide arithmetic functions for POSIX system time.
//! This time represents as seconds and nanosecons in integer formats.
//! You can take current system time with function \a PISystemTime::current(),
//! compare times, sum or subtract two times, convert time to/from
//! seconds, milliseconds, microseconds or nanoseconds.
//!
//! \~russian
//! Этот класс предоставляет арифметику для системного времени в формате POSIX.
//! Это время представлено в виде целочисленных секунд и наносекунд.
//! Можно взять текущее время с помощью метода \a PISystemTime::current(),
//! сравнивать, суммировать и вычитать времена, преобразовывать в/из
//! секунд, миллисекунд, микросекунд и наносекунд.
//!
//! \~english \section PISystemTime_sec1 Example
//! \~russian \section PISystemTime_sec1 Пример
//! \~\snippet pitimer.cpp system_time
//!


PINetworkAddress::PINetworkAddress(uint _ip, ushort _port) {
	set(_ip, _port);
}


PINetworkAddress::PINetworkAddress(const PIString & ip_port) {
	set(ip_port);
}


PINetworkAddress::PINetworkAddress(const PIString & _ip, ushort _port) {
	set(_ip, _port);
}


PIString PINetworkAddress::ipString() const {
	PIString ret = PIString::fromNumber(ip_b[0]);
	ret += "." + PIString::fromNumber(ip_b[1]);
	ret += "." + PIString::fromNumber(ip_b[2]);
	ret += "." + PIString::fromNumber(ip_b[3]);
	return ret;
}


PIString PINetworkAddress::toString() const {
	return ipString() + ":" + PIString::fromNumber(port_);
}


void PINetworkAddress::setIP(uint _ip) {
	ip_ = _ip;
}


void PINetworkAddress::setIP(const PIString & _ip) {
	initIP(_ip);
}


void PINetworkAddress::setPort(ushort _port) {
	port_ = _port;
}


void PINetworkAddress::set(const PIString & ip_port) {
	PIString _ip;
	int p(0);
	splitIPPort(ip_port, &_ip, &p);
	port_ = p;
	initIP(_ip);
}


void PINetworkAddress::set(const PIString & _ip, ushort _port) {
	initIP(_ip);
	port_ = _port;
}


void PINetworkAddress::set(uint _ip, ushort _port) {
	ip_   = _ip;
	port_ = _port;
}


void PINetworkAddress::clear() {
	ip_   = 0;
	port_ = 0;
}


bool PINetworkAddress::isNull() const {
	return (ip_ == 0) && (port_ == 0);
}


PINetworkAddress PINetworkAddress::resolve(const PIString & host_port) {
	PIString host;
	int port(0);
	splitIPPort(host_port, &host, &port);
	return resolve(host, port);
}


PINetworkAddress PINetworkAddress::resolve(const PIString & host, ushort port) {
	PINetworkAddress ret(0, port);
	hostent * he = gethostbyname(host.dataAscii());
	if (!he) return ret;
	if (he->h_addr_list[0]) ret.setIP(*((uint *)(he->h_addr_list[0])));
	return ret;
}


void PINetworkAddress::splitIPPort(const PIString & ipp, PIString * _ip, int * _port) {
	// piCout << "parse" << ipp;
	int sp = ipp.findLast(":");
	if (_ip != 0) *_ip = (sp >= 0 ? ipp.left(sp) : ipp);
	if (_port != 0 && sp >= 0) *_port = ipp.right(ipp.length() - ipp.find(":") - 1).toInt();
}


void PINetworkAddress::initIP(const PIString & _ip) {
	ip_ = inet_addr(_ip.dataAscii());
}
