//=============================================================================
//
//   File : RootService.cpp
//   Creation date : Fri Aug 08 18:00:00 2008 GMT by Fabio Bas
//
//   This file is part of the KVIrc IRC client distribution
//   Copyright (C) 2008 Fabio Bas (ctrlaltca at gmail dot com)
//
//   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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//   Original Copyright follows:
//=============================================================================

/***************************************************************************
                          RootService.cpp -  description
                             -------------------
    begin                : Mon Jul 25 2005
    copyright            : (C) 2005 by Diederik van der Boor
    email                : vdboor --at-- codingdomain.com
 ***************************************************************************/

#include "RootService.h"
#include "XmlFunctions.h"
#include "KviLocale.h"
#include "kvi_out.h"
#include "KviApplication.h"
#include "KviConsoleWindow.h"

#include <QDebug>

namespace UPnP
{
	// The constructor
	RootService::RootService(const QString & hostname, int port, const QString & rootUrl)
	    : Service(hostname, port, rootUrl), m_szHostname(hostname), m_iPort(port)
	{
	}

	// The destructor
	RootService::~RootService()
	    = default;

	// Recursively add all devices and embedded devices to the deviceServices_ map
	void RootService::addDeviceServices(const QDomNode & device)
	{
		qDebug() << "UPnP discovered device " << XmlFunctions::getNodeValue(device, "/UDN") << Qt::endl;

		if(XmlFunctions::getNodeValue(device, "/deviceType") == InternetGatewayDeviceType)
		{
			QString description;
			description = XmlFunctions::getNodeValue(device, "/friendlyName");
			if(description.isNull())
				description = XmlFunctions::getNodeValue(device, "/modelDescription");
			if(description.isNull())
				description = XmlFunctions::getNodeValue(device, "/modelName") + " " + XmlFunctions::getNodeValue(device, "/modelNumber");
			if(description.isNull())
				description = __tr2qs("Unknown");

			qDebug() << "Model: " << description << Qt::endl;

			g_pApp->activeConsole()->output(KVI_OUT_GENERICSTATUS, __tr2qs_ctx("[UPNP]: found gateway device: %s", "upnp"), description.toUtf8().data());
		}
		// Insert the given device node
		// The "key" is the device/UDN tag, the value is a list of device/serviceList/service nodes
		m_deviceServices.insert(XmlFunctions::getNodeValue(device, "/UDN"),
		    device.namedItem("serviceList").childNodes());

		// Find all embedded device nodes
		QDomNodeList embeddedDevices = device.namedItem("deviceList").childNodes();
		for(int i = 0; i < embeddedDevices.count(); i++)
		{
			if(embeddedDevices.item(i).nodeName() != "device")
				continue;
			addDeviceServices(embeddedDevices.item(i));
		}
	}

	// Return the device type
	QString RootService::getDeviceType() const
	{
		return m_szDeviceType;
	}

	// Return a service from the cached root device entry
	ServiceParameters RootService::getServiceById(const QString & serviceId) const
	{
		// Get a /root/device/serviceList/service/ tag

		ServiceParameters params;
		QMap<QString, QDomNodeList>::const_iterator i = m_deviceServices.constBegin();
		while(i != m_deviceServices.constEnd())
		{
			if(getServiceById(serviceId, i.key(), params))
				return params;
			++i;
		}

		//calling function check this field to understand if the struct is null
		params.controlUrl = QString();
		return params;
	}

	// Return a service from a cached embedded device entry
	bool RootService::getServiceById(const QString & serviceId, const QString & deviceUdn, ServiceParameters & params) const
	{
		// Get a /root/device/deviceList/device/.../serviceList/service/serviceId tag
		QDomNode service = XmlFunctions::getNodeChildByKey(m_deviceServices[deviceUdn], "serviceId", serviceId);

		if(!service.isNull())
		{
			params.hostname = m_szHostname;
			params.port = m_iPort;
			params.controlUrl = XmlFunctions::getNodeValue(service, "/controlURL");
			params.scpdUrl = XmlFunctions::getNodeValue(service, "/SCPDURL");
			params.serviceId = XmlFunctions::getNodeValue(service, "/serviceId");
			params.serviceType = XmlFunctions::getNodeValue(service, "/serviceType");

			return true;
		}
		else
		{
			return false;
		}
	}

	// Return a service from the cached root device entry
	ServiceParameters RootService::getServiceByType(const QString & serviceType) const
	{
		// Get a /root/device/serviceList/service/ tag

		ServiceParameters params;
		QMap<QString, QDomNodeList>::const_iterator i = m_deviceServices.constBegin();
		while(i != m_deviceServices.constEnd())
		{
			if(getServiceByType(serviceType, i.key(), params))
				return params;
			++i;
		}

		//calling function check this field to understand if the struct is null
		params.controlUrl = QString();
		return params;
	}

	// Return a service from a cached embedded device entry
	bool RootService::getServiceByType(const QString & serviceType, const QString & deviceUdn, ServiceParameters & params) const
	{
		// Get a /root/device/deviceList/device/.../serviceList/service/serviceType tag
		QDomNode service = XmlFunctions::getNodeChildByKey(m_deviceServices[deviceUdn], "serviceType", serviceType);

		if(!service.isNull())
		{
			params.hostname = m_szHostname;
			params.port = m_iPort;
			params.controlUrl = XmlFunctions::getNodeValue(service, "/controlURL");
			params.scpdUrl = XmlFunctions::getNodeValue(service, "/SCPDURL");
			params.serviceId = XmlFunctions::getNodeValue(service, "/serviceId");
			params.serviceType = serviceType;

			return true;
		}
		else
		{
			qWarning() << "UPnP::RootService::getServiceByType -"
			           << " type '" << serviceType << "' not found for device '" << deviceUdn << "'." << Qt::endl;

			return false;
		}
	}

	// The control point received a response to callInformationUrl()
	void RootService::gotInformationResponse(const QDomNode & response)
	{
		// Register all device UDN nodes for later
		m_deviceServices.clear();
		addDeviceServices(XmlFunctions::getNode(response, "/device"));

		// Fetch the required data
		m_szDeviceType = XmlFunctions::getNodeValue(response, "/device/deviceType");
		m_szRootUdn = XmlFunctions::getNodeValue(response, "/device/UDN");

		// The rootUdn_ is used to retrieve
		// the /root/device/serviceList/service
		// nodes from the deviceServices_ map
	}

	// Query the device for its service list
	void RootService::queryDevice()
	{
		callInformationUrl();
	}

} // end of namespae
