/*-
 * Copyright 2005 Guram Dukashvili
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
//---------------------------------------------------------------------------
#ifndef _service_H_
#define _service_H_
//---------------------------------------------------------------------------
namespace ksys {
//---------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
enum ServiceStatus {
  svcReady,
  svcStarting,
  svcStarted,
  svcSuspending,
  svcSuspended,
  svcResuming,
  svcResumed,
  svcStoping,
  svcStoped
};
//---------------------------------------------------------------------------
class Service {
  friend class Services;
  public:
    virtual ~Service();
    Service();

#if defined(__WIN32__) || defined(__WIN64__)
    Service &             serviceMain(uintptr_t n, DWORD dwArgc, LPWSTR * lpszArgv);
    Service &             handler(uintptr_t n, DWORD fdwControl);
#endif
    const utf8::String & args() const;
    Service & args(const utf8::String & s);
  protected:
#if !defined(__WIN32__) && !defined(__WIN64__)
    utf8::String          serviceName_;
    utf8::String          displayName_;
#endif
    ServiceStatus         status_;

    virtual void          install();
    virtual void          uninstall();

    virtual void          start();
    virtual void          stop();
    virtual void          suspend();
    virtual void          resume();
    virtual utf8::String  status();
    virtual bool          active();
#if defined(__WIN32__) || defined(__WIN64__)
    Semaphore             semaphore_;
    utf8::String          serviceName_;
    utf8::WideString      serviceNameW_;
    utf8::String          displayName_;
    DWORD                 serviceType_;
    DWORD                 startType_;
    DWORD                 errorControl_;
    DWORD                 tagId_;
    utf8::String          binaryPathName_;
    utf8::String          loadOrderGroup_;
    utf8::String          dependencies_;
    utf8::String          serviceStartName_;
    utf8::String          password_;

    SERVICE_STATUS_HANDLE statusHandle_;
    SERVICE_STATUS        serviceStatus_;
  private:
    Service &             install(SC_HANDLE hSCManager);
    Service &             uninstall(SC_HANDLE hSCManager);
#endif
   utf8::String args_;
 };
//---------------------------------------------------------------------------
inline const utf8::String & Service::args() const
{
  return args_;
}
//---------------------------------------------------------------------------
inline Service & Service::args(const utf8::String & s)
{
  args_ = s;
  return *this;
}
//---------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
class Services
#if !defined(__WIN32__) && !defined(__WIN64__)
 : public Thread
#endif
{
  friend class Service;
  public:
    ~Services();
    Services(const utf8::String & name = utf8::String() /* not used under windows */);

    Services &  clear();
    Services &  add(Service * service);

    Services &  install();
    Services &  uninstall();
    Services &  start(const utf8::String & serviceName);
    Services &  stop(const utf8::String & serviceName);
    Services &  suspend(const utf8::String & serviceName);
    Services &  resume(const utf8::String & serviceName);
    Services &  query(const utf8::String & serviceName);

    Services &  stopServiceCtrlDispatcher();
    Services &  startServiceCtrlDispatcher(bool daemonize = true, bool startServices = true);
#if defined(__WIN32__) || defined(__WIN64__)
    static Service *              services_[];
#endif
  protected:
    Service &   serviceByName(const utf8::String & serviceName);
  private:
#if defined(__WIN32__) || defined(__WIN64__)
    SC_HANDLE                     handle_;
    Array< SERVICE_TABLE_ENTRYW>  serviceStartTable_;

    static VOID (WINAPI * const serviceMains_[])(DWORD, LPWSTR *);
    static VOID (WINAPI * const handlers_[])(DWORD);
#else
    Vector< Service>              services_;
    utf8::String                  controlName_;
    AutoPtr< SharedMemoryQueue>   control_;
    void threadExecute();
    enum ServiceDispatcherCmd { 
      svcStart,
      svcStop,
      svcSuspend,
      svcResume,
      svcQuery,
      svcStopDispatcher
    };
#endif
};
//---------------------------------------------------------------------------
#if defined(__WIN32__) || defined(__WIN64__)
//---------------------------------------------------------------------------
template< uintptr_t N> VOID WINAPI serviceMainTemplate(DWORD dwArgc, LPWSTR * lpszArgv)
{
  Services::services_[N]->serviceMain(N, dwArgc, lpszArgv);
}
//---------------------------------------------------------------------------
template< uintptr_t N> VOID WINAPI handlerTemplate(DWORD fdwControl)
{
  Services::services_[N]->handler(N, fdwControl);
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
} // namespace ksys
//---------------------------------------------------------------------------
#endif /* _service_H_ */
