/*  -*- C++ -*-
 *  Copyright (C) 2003-2005 Thiago Macieira <thiago@kde.org>
 *
 *
 *  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 KRESOLVER_P_H
#define KRESOLVER_P_H

#include <config.h>
#include <config-network.h>
#include <sys/types.h>


#include <QByteArray>
#include <QList>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QSemaphore>
#include <QEvent>

#include "k3resolver.h"

/* decide whether we need a mutex */
#if !defined(HAVE_GETPROTOBYNAME_R) || !defined(HAVE_GETSERVBYNAME_R) || !defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETSERVBYPORT_R)
# define NEED_MUTEX
extern QMutex getXXbyYYmutex;
#endif

/* some systems have the functions, but don't declare them */
#if defined(HAVE_GETSERVBYNAME_R) && !HAVE_GETSERVBYNAME_R_PROTO
extern "C" {
  struct servent;
  extern int getservbyname_r(const char* serv, const char* proto,
			     struct servent* servbuf,
			     char* buf, size_t buflen,
			     struct servent** result);
  extern int getservbyport_r(int port, const char* proto,
			     struct servent* servbuf,
			     char* buf, size_t buflen,
			     struct servent** result);

  struct protoent;
  extern int getprotobyname_r(const char* proto, struct protoent* protobuf,
			      char *buf, size_t buflen,
			      struct protoent** result);
  extern int getprotobynumber_r(int proto, struct protoent* protobuf,
				char *buf, size_t buflen,
				struct protoent** result);
}
#endif

/* decide whether res_init is thread-safe or not */
#if defined(__GLIBC__)
# undef RES_INIT_THREADSAFE
#endif

namespace KNetwork
{
  // defined in network/qresolverworkerbase.h
  class KResolverWorkerBase;
  class KResolverWorkerFactoryBase;
  class KResolverPrivate;

  namespace Internal
  {
    class KResolverManager;
    class KResolverThread;
    struct RequestData;

    struct InputData
    {
      QString node, service;
      QByteArray protocolName;
      int flags;
      int familyMask;
      int socktype;
      int protocol;
    };
  }

  class KResolverPrivate
  {
  public:
    // parent class. Should never be changed!
    KResolver* parent;
    bool deleteWhenDone : 1;
    bool waiting : 1;

    // class status. Should not be changed by worker threads!
    volatile int status;
    volatile int errorcode, syserror;

    // input data. Should not be changed by worker threads!
    Internal::InputData input;

    // mutex
    QMutex mutex;

    // output data
    KResolverResults results;

    explicit KResolverPrivate(KResolver* _parent,
		     const QString& _node = QString(),
		     const QString& _service = QString())
      : parent(_parent), deleteWhenDone(false), waiting(false),
	status(0), errorcode(0), syserror(0)
    {
      input.node = _node;
      input.service = _service;
      input.flags = 0;
      input.familyMask = KResolver::AnyFamily;
      input.socktype = 0;
      input.protocol = 0;

      results.setAddress(_node, _service);
    }
  };

  namespace Internal
  {
    struct RequestData
    {
      // worker threads should not change values in the input data
      KNetwork::KResolverPrivate *obj;
      const KNetwork::Internal::InputData *input;
      KNetwork::KResolverWorkerBase *worker; // worker class
      RequestData *requestor; // class that requested us

      volatile int nRequests; // how many requests that we made we still have left
    };

    /*
     * @internal
     * This class is the resolver manager
     */
    class KResolverManager
    {
    public:
      enum EventTypes
	{ ResolutionCompleted = 1576 }; // arbitrary value;

      /*
       * This wait condition is used to notify wait states (KResolver::wait) that
       * the resolver manager has finished processing one or more objects. All
       * objects in wait state will be woken up and will check if they are done.
       * If they aren't, they will go back to sleeping.
       */
      QWaitCondition notifyWaiters;

    private:
      /*
       * This variable is used to count the number of threads that are running
       */
      volatile unsigned short runningThreads;

      /*
       * This variable is used to count the number of threads that are currently
       * waiting for data.
       */
      unsigned short availableThreads;

      /*
       * This wait condition is used to notify worker threads that there is new
       * data available that has to be processed. All worker threads wait on this
       * waitcond for a limited amount of time.
       */
      QWaitCondition feedWorkers;

      // this mutex protects the data in this object
      QMutex mutex;

      // hold a list of all the current threads we have
      QList<KResolverThread*> workers;

      // hold a list of all the new requests we have
      QList<RequestData*> newRequests;

      // hold a list of all the requests in progress we have
      QList<RequestData*> currentRequests;

      // hold a list of all the workers we have
      QList<KNetwork::KResolverWorkerFactoryBase*> workerFactories;

      // private constructor
      KResolverManager();

    public:
      static KResolverManager* manager() KDE_NO_EXPORT;	// creates and returns the global manager

      // destructor
      ~KResolverManager();

      /*
       * Register this thread in the pool
       */
      void registerThread(KResolverThread* id);

      /*
       * Unregister this thread from the pool
       */
      void unregisterThread(KResolverThread* id);

      /*
       * Requests new data to work on.
       *
       * This function should only be called from a worker thread. This function
       * is thread-safe.
       *
       * If there is data to be worked on, this function will return it. If there is
       * none, this function will return a null pointer.
       */
      RequestData* requestData(KResolverThread* id, int maxWaitTime);

      /*
       * Releases the resources and returns the resolved data.
       *
       * This function should only be called from a worker thread. It is
       * thread-safe. It does not post the event to the manager.
       */
      void releaseData(KResolverThread *id, RequestData* data);

      /*
       * Registers a new worker class by way of its factory.
       *
       * This function is NOT thread-safe.
       */
      void registerNewWorker(KNetwork::KResolverWorkerFactoryBase *factory);

      /*
       * Enqueues new resolutions.
       */
      void enqueue(KNetwork::KResolver *obj, RequestData* requestor);

      /*
       * Dispatch a new request
       */
      void dispatch(RequestData* data);

      /*
       * Dequeues a resolution.
       */
      void dequeue(KNetwork::KResolver *obj);

      /*
       * Notifies the manager that the given resolution is about to
       * be deleted. This function should only be called by the
       * KResolver destructor.
       */
      void aboutToBeDeleted(KNetwork::KResolver *obj);

      /*
       * Notifies the manager that new events are ready.
       */
      void newEvent();

      /*
       * This function is called by the manager to receive a new event. It operates
       * on the eventSemaphore() semaphore, which means it will block till there
       * is at least one event to go.
       */
      void receiveEvent();

    private:
      /*
       * finds a suitable worker for this request
       */
      KNetwork::KResolverWorkerBase *findWorker(KNetwork::KResolverPrivate *p);

      /*
       * finds data for this request
       */
      RequestData* findData(KResolverThread*);

      /*
       * Handle completed requests.
       *
       * This function is called by releaseData above
       */
      void handleFinished();

      /*
       * Handle one completed request.
       *
       * This function is called by handleFinished above.
       */
      bool handleFinishedItem(RequestData* item);

      /*
       * Notifies the parent class that this request is done.
       *
       * This function deletes the request
       */
      void doNotifying(RequestData *p);

      /*
       * Dequeues and notifies an object that is in Queued state
       * Returns true if the object is no longer queued; false if it could not
       * be dequeued (i.e., it's running)
       */
      bool dequeueNew(KNetwork::KResolver* obj);
    };

    /*
     * @internal
     * This class is a worker thread in the resolver system.
     * This class must be thread-safe.
     */
    class KResolverThread: public QThread
    {
    private:
      // private constructor. Only the manager can create worker threads
      KResolverThread();
      RequestData* data;

    protected:
      virtual void run();		// here the thread starts

      friend class KNetwork::Internal::KResolverManager;
      friend class KNetwork::KResolverWorkerBase;

    public:
      bool checkResolver();	// see KResolverWorkerBase::checkResolver
      void acquireResolver();	// see KResolverWorkerBase::acquireResolver
      void releaseResolver();	// see KResolverWorkerBase::releaseResolver
    };

  } // namespace Internal

} // namespace KNetwork


#endif
