/***************************************************************************
 *
 * knetworkmanager-devicestore_dbus.cpp - A NetworkManager frontend for KDE
 *
 * Copyright (C) 2005, 2006 Novell, Inc.
 *
 * Author: Helmut Schaa <hschaa@suse.de>, <helmut.schaa@gmx.de>
 *
 * 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 2 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 **************************************************************************/

/* qt headers */
#include <qhostaddress.h>
#include <qvariant.h>

/* kde headers */
#include <kdebug.h>
#include <klocale.h>

/* QDBus headers*/
#include <dbus/qdbusdata.h>
#include <dbus/qdbusdatamap.h>

/* knetworkmanager headers */
#include "knetworkmanager.h"
#include "knetworkmanager-connection_setting_ipv4.h"


using namespace ConnectionSettings;

// reverse order the bytes
Q_UINT32 swap32(Q_UINT32 x)
{
	Q_UINT32 ret = 0;

	Q_UINT8* from = (Q_UINT8*) &x;
	Q_UINT8* to   = (Q_UINT8*) &ret;

	for (int i = 0; i < 4; ++i)
		to[3-i] = from[i];
	return ret;
}

/*
	class IPv4
*/
IPv4::IPv4(Connection* conn)
	: ConnectionSetting(conn, NM_SETTING_IP4_CONFIG_SETTING_NAME)
{
	_method = METHOD_DHCP;
	_ignore_auto_dns = false;
	_ignore_auto_routes = false;
}

QValueList<IPv4Address> IPv4::getAddresses() const
{
	return _addresses;
}

void IPv4::setAddresses(const QValueList<IPv4Address> & adr) 
{
	_addresses = adr;
	emitValidityChanged();
}

QValueList<QHostAddress> IPv4::getDNS() const
{
	return _dns;
}

void IPv4::setDNS(const QValueList<QHostAddress>& dns)
{
	_dns = dns;
	emitValidityChanged();
}

QStringList IPv4::getDNSSearch() const
{
	return _dns_search;
}

void IPv4::setDNSSearch(const QStringList & dnsSearch)
{
	_dns_search = dnsSearch;
	emitValidityChanged();
}

void
IPv4::setMethod(IPV4METHOD method)
{
	_method = method;
	emitValidityChanged();
}

IPv4::IPV4METHOD
IPv4::getMethod() const
{
	return _method;
}

void
IPv4::setIgnoreAutoDNS(bool ignore)
{
	_ignore_auto_dns = ignore;
	emitValidityChanged();
}

bool
IPv4::getIgnoreAutoDNS() const
{
	return _ignore_auto_dns;
}

void
IPv4::setIgnoreAutoRoutes(bool ignore)
{
	_ignore_auto_routes = ignore;
	emitValidityChanged();
}

bool
IPv4::getIgnoreAutoRoutes() const
{
	return _ignore_auto_routes;
}

bool
IPv4::isValid() const
{
	if (_method == METHOD_MANUAL)
	{
		// only check the manual settings

		// at least one address has to be specified
		if (_addresses.empty())
			return false;

		// check every address
		for (QValueList<IPv4Address>::ConstIterator it = _addresses.begin(); it != _addresses.end(); ++it)
		{
			if ((*it).address.isNull())
				return false;
			if ((*it).netmask.isNull())
				return false;
			// no need to check gateway as it is optional
		}

		// check DNS addresses but may be empty
		for (QValueList<QHostAddress>::ConstIterator it = _dns.begin(); it != _dns.end(); ++it)
		{
			if ((*it).isNull())
				return false;
		}

		// don't check DNS Search because it is optional ...
	}

	return true;
}

SettingsMap
IPv4::toMap() const
{
	SettingsMap map;

	if (_method == METHOD_DHCP)
		map.insert(NM_SETTING_IP4_CONFIG_METHOD, QDBusData::fromString(NM_SETTING_IP4_CONFIG_METHOD_AUTO));
	else if (_method == METHOD_AUTOIP)
		map.insert(NM_SETTING_IP4_CONFIG_METHOD, QDBusData::fromString(NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL));
	else if (_method == METHOD_SHARED)
		map.insert(NM_SETTING_IP4_CONFIG_METHOD, QDBusData::fromString(NM_SETTING_IP4_CONFIG_METHOD_SHARED));
	else if (_method == METHOD_MANUAL)
	{
		map.insert(NM_SETTING_IP4_CONFIG_METHOD, QDBusData::fromString(NM_SETTING_IP4_CONFIG_METHOD_MANUAL));

		// DNS search
		if (_dns_search.size() > 0)
		{
			QValueList<QDBusData> dns_search;
			QStringList::ConstIterator it = _dns_search.begin();
			for(;it != _dns_search.end(); ++it)
				dns_search.append(QDBusData::fromString(*it));
	
			map.insert(NM_SETTING_IP4_CONFIG_DNS_SEARCH, QDBusData::fromQValueList(dns_search));
		}

		// DNS addresses
		if (_dns.size() > 0)
		{
			QValueList<QDBusData> dns;
			QValueList<QHostAddress>::ConstIterator it_dns = _dns.begin();
			// the strange swap32 is needed as NM reads the address exactly the other way round as Qt
			for(;it_dns != _dns.end(); ++it_dns)
				dns.append(QDBusData::fromUInt32(swap32((*it_dns).toIPv4Address())));
		
			map.insert(NM_SETTING_IP4_CONFIG_DNS, QDBusData::fromQValueList(dns));
		}

		// IP's
		if (_addresses.size() > 0)
		{
			QValueList<QDBusData> ips;
			for (QValueList<IPv4Address>::ConstIterator it = _addresses.begin(); it != _addresses.end(); ++it)
			{
				QValueList<QDBusData> cur_ip;
				cur_ip.append(QDBusData::fromUInt32(swap32((*it).address.toIPv4Address())));
				cur_ip.append(QDBusData::fromUInt32(toCIDRSuffix((*it).netmask)));
				if (!(*it).gateway.isNull())
					cur_ip.append(QDBusData::fromUInt32(swap32((*it).gateway.toIPv4Address())));
				ips.append(QDBusData::fromQValueList(cur_ip));
			}
			map.insert(NM_SETTING_IP4_CONFIG_ADDRESSES, QDBusData::fromQValueList(ips));
		}
	}
	map.insert(NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES, QDBusData::fromBool(_ignore_auto_routes));
	map.insert(NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS, QDBusData::fromBool(_ignore_auto_dns));
	return map;
}

void
IPv4::fromMap(const SettingsMap& map)
{
	SettingsMap::ConstIterator it;

	if ((it = map.find(NM_SETTING_IP4_CONFIG_METHOD)) != map.end())
	{
		if (it.data().toString() == NM_SETTING_IP4_CONFIG_METHOD_AUTO || it.data().toString() == "dhcp")
			_method = METHOD_DHCP;
		else if (it.data().toString() == NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL || it.data().toString() == "autoip")
			_method = METHOD_AUTOIP;
		else if (it.data().toString() == NM_SETTING_IP4_CONFIG_METHOD_SHARED)
			_method = METHOD_SHARED;
		else if (it.data().toString() == NM_SETTING_IP4_CONFIG_METHOD_MANUAL)
			_method = METHOD_MANUAL;
	}

	// DNS search
	if ((it = map.find(NM_SETTING_IP4_CONFIG_DNS_SEARCH)) != map.end())
	{
		QValueList<QDBusData> dns_search = it.data().toQValueList();
		for (QValueList<QDBusData>::Iterator it = dns_search.begin(); it != dns_search.end(); ++it)
		{
			_dns_search.append( (*it).toString());
		}
	}

	// DNS addresses
	if ((it = map.find(NM_SETTING_IP4_CONFIG_DNS)) != map.end())
	{
		QValueList<QDBusData> dns = it.data().toQValueList();
		for (QValueList<QDBusData>::Iterator it = dns.begin(); it != dns.end(); ++it)
		{
			_dns.append( QHostAddress(swap32((*it).toUInt32())) );
		}
	}

	// IP's
	if ((it = map.find(NM_SETTING_IP4_CONFIG_ADDRESSES)) != map.end())
	{
		QValueList<QDBusData> ips = it.data().toQValueList();
		for (QValueList<QDBusData>::Iterator it2 = ips.begin(); it2 != ips.end(); ++it2)
		{
			QValueList<QDBusData> cur_ip = (*it2).toQValueList();
			IPv4Address address;

			address.address = swap32(cur_ip[0].toUInt32());

			if (cur_ip[1].toUInt32() >= 0 && cur_ip[1].toUInt32() <= 32)
				address.netmask = fromCIDRSuffix(cur_ip[1].toUInt32());
			else
				address.netmask = swap32(cur_ip[1].toUInt32());

			if (cur_ip.size() > 2)
				address.gateway = swap32(cur_ip[2].toUInt32());
		
			_addresses.append(address);
		}
	}

	if ((it = map.find(NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES)) != map.end())
		_ignore_auto_routes = it.data().toBool();

	if ((it = map.find(NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS)) != map.end())
		_ignore_auto_dns = it.data().toBool();
}

Q_UINT32 IPv4::toCIDRSuffix(const QHostAddress& adr) const
{
	Q_UINT32 netmask = adr.toIPv4Address();
	Q_UINT32 suffix = 0;
	while (netmask > 0)
	{
		suffix++;
		netmask = netmask << 1;
	}
	return suffix;
}

QHostAddress IPv4::fromCIDRSuffix(Q_UINT32 suffix)
{
	Q_UINT32 netmask = 0xFFFFFFFF;
	netmask = netmask << (32 - suffix);
	return QHostAddress(netmask);
}

