/*
 * Copyright (C) 2006-2008	Justin Karneges
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef QJDNSSHARED_P_H
#define QJDNSSHARED_P_H

// SafeTimer
#include "qjdns_p.h"
#include "qjdnsshared.h"
#include <QWaitCondition>
#include <QThread>
#include <QObject>
#include <QMutex>
#include <QStringList>
#include <QSet>
#include <QHash>

class JDnsShutdownAgent : public QObject
{
	Q_OBJECT
public:
	void start();

signals:
	void started();
};

class JDnsShutdownWorker : public QObject
{
	Q_OBJECT
public:
	QList<QJDnsShared*> list;

	JDnsShutdownWorker(const QList<QJDnsShared*> &_list);

signals:
	void finished();

private slots:
	void jdns_shutdownFinished();
};

class JDnsShutdown : public QThread
{
	Q_OBJECT
public:
	QMutex m;
	QWaitCondition w;
	QList<QJDnsShared*> list;
	JDnsShutdownAgent *agent;
	JDnsShutdownWorker *worker;
	int phase;

	void waitForShutdown(const QList<QJDnsShared*> &_list);

protected:
	virtual void run() override;

private slots:
	void agent_started();
	void worker_finished();
};

class QJDnsSharedDebugPrivate : public QObject
{
	Q_OBJECT
public:
	QJDnsSharedDebug *q;
	QMutex m;
	QStringList lines;
	bool dirty;

	QJDnsSharedDebugPrivate(QJDnsSharedDebug *_q);
	void addDebug(const QString &name, const QStringList &_lines);

private slots:
	void doUpdate();
};

//----------------------------------------------------------------------------
// Handle
//----------------------------------------------------------------------------

// QJDns uses integer handle ids, but they are only unique within
//   the relevant QJDns instance.  Since we want our handles to be
//   unique across all instances, we'll make an instance/id pair.
class Handle
{
public:
	QJDns *jdns;
	int id;

	Handle() : jdns(0), id(-1)
	{
	}

	Handle(QJDns *_jdns, int _id) : jdns(_jdns), id(_id)
	{
	}

	bool operator==(const Handle &a) const
	{
		if(a.jdns == jdns && a.id == id)
			return true;
		return false;
	}

	bool operator!=(const Handle &a) const
	{
		return !(operator==(a));
	}
};

class QJDnsSharedPrivate : public QObject
{
	Q_OBJECT
public:
	class Instance
	{
	public:
		QJDns *jdns;
		QHostAddress addr;
		int index;

		Instance() : jdns(0)
		{
		}
	};

	enum PreprocessMode
	{
		None,			 // don't muck with anything
		FillInAddress,	 // for A/AAAA
		FillInPtrOwner6, // for PTR, IPv6
		FillInPtrOwner4, // for PTR, IPv4
	};

	QJDnsShared *q;
	QJDnsShared::Mode mode;
	bool shutting_down;
	QJDnsSharedDebug *db;
	QString dbname;

	QList<Instance*> instances;
	QHash<QJDns*,Instance*> instanceForQJDns;

	QSet<QJDnsSharedRequest*> requests;
	QHash<Handle,QJDnsSharedRequest*> requestForHandle;

	QJDnsSharedPrivate(QJDnsShared *_q);
	QJDnsSharedRequest *findRequest(QJDns *jdns, int id) const;
	void jdns_link(QJDns *jdns);
	int getNewIndex() const;
	void addDebug(int index, const QString &line);
	void doDebug(QJDns *jdns, int index);
	PreprocessMode determinePpMode(const QJDns::Record &in);
	QJDns::Record manipulateRecord(const QJDns::Record &in, PreprocessMode ppmode, bool *modified = 0);
	bool addInterface(const QHostAddress &addr);
	void removeInterface(const QHostAddress &addr);

	void queryStart(QJDnsSharedRequest *obj, const QByteArray &name, int qType);
	void queryCancel(QJDnsSharedRequest *obj);
	void publishStart(QJDnsSharedRequest *obj, QJDns::PublishMode m, const QJDns::Record &record);
	void publishUpdate(QJDnsSharedRequest *obj, const QJDns::Record &record);
	void publishCancel(QJDnsSharedRequest *obj);

public slots:
	void late_shutdown();

private slots:
	void jdns_resultsReady(int id, const QJDns::Response &results);
	void jdns_published(int id);
	void jdns_error(int id, QJDns::Error e);
	void jdns_shutdownFinished();
	void jdns_debugLinesReady();
};

class QJDnsSharedRequestPrivate : public QObject
{
	Q_OBJECT
public:
	QJDnsSharedRequest *q;
	QJDnsSharedPrivate *jsp;

	// current action
	QJDnsSharedRequest::Type type;
	QByteArray name;
	int qType;
	QJDns::PublishMode pubmode;
	QJDnsSharedPrivate::PreprocessMode ppmode;
	QJDns::Record pubrecord;

	// a single request might have to perform multiple QJDns operations
	QList<Handle> handles;

	// keep a list of handles that successfully publish
	QList<Handle> published;

	// use to weed out dups for multicast
	QList<QJDns::Record> queryCache;

	bool success;
	QJDnsSharedRequest::Error error;
	QList<QJDns::Record> results;
	SafeTimer lateTimer;
	QJDnsSharedRequestPrivate(QJDnsSharedRequest *_q);
	void resetSession();

private slots:
	void lateTimer_timeout();
};

#endif
