//
// daytime.cpp
// ~~~~~~~~~~~
// Demonstrates a service that runs as a daytime server. To
// test the service telnet to 127.0.0.1 on port 13.
//
// To register:   daytime /register
// To unregister: daytime /unregister
// To debug:      daytime /debug
// To start:      net start DaytimeService
// To stop:       net stop DaytimeService
// To pause:      net pause DaytimeService
// To continue:   net continue DaytimeService
//

#include <owl/pch.h>
#include <classlib/time.h>
#include <winsock.h>
#include <string.h>
#include "service.h"


class TDaytimeService : public TService
{
  public:

    const char* GetName() const { return "DaytimeService"; }
    const char* GetDisplayName() const { return "Daytime Service"; }
    bool Init();
    void Run();
    void Cleanup();
    void PauseBegin();
    void PauseEnd();

  private:
    SOCKET Socket;
};


bool TDaytimeService::Init()
{
  // load the winsock dll
  //
  WSADATA wsaData;
  if (WSAStartup(MAKEWORD(1, 1), &wsaData))
  {
    Log("Unable to load Winsock DLL");
    return false;
  }

  // create a socket for accepting connections
  //
  Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (Socket == INVALID_SOCKET)
  {
    Log("Unable to create socket");
    return false;
  }

  // bind the socket to the port
  //
  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_port = htons(IPPORT_DAYTIME);
  if (bind(Socket, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR)
  {
    Log("Unable to bind socket");
    closesocket(Socket);
    return false;
  }

  // mark the socket as a listener
  //
  if (listen(Socket, SOMAXCONN) == SOCKET_ERROR)
  {
    Log("Unable to mark socket for listening");
    closesocket(Socket);
    return false;
  }

  return true;
}


void TDaytimeService::Run()
{
  SOCKET accepted;

  while (!CheckStop())
  {
    // wait for a period for something to accept from the socket
    //
    struct fd_set readfds;
    struct timeval timeout;
    FD_ZERO(&readfds);
    FD_SET(Socket, &readfds);
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    int n = select(FD_SETSIZE, &readfds, 0, 0, &timeout);

    // select failed, log the error and exit
    //
    if (n < 0)
    {
      Log("Call to select failed");
      SetStatus(SERVICE_STOPPED, 1);
      return;
    }

    // select timed out
    //
    if (n == 0)
      continue;

    // Accept the waiting connection
    //
    accepted = accept(Socket, 0, 0);
    if (accepted == INVALID_SOCKET)
    {
      Log("Call to accept failed");
      SetStatus(SERVICE_STOPPED, 1);
      return;
    }

    // Send the day and time down the socket
    //
    string dateString = TTime().AsString();
    send(accepted, dateString.c_str(), dateString.length(), 0);
    closesocket(accepted);
  }
}


void TDaytimeService::Cleanup()
{
  closesocket(Socket);

  WSACleanup();
}


void TDaytimeService::PauseBegin()
{
  Log("DaytimeService pausing.");
}


void TDaytimeService::PauseEnd()
{
  Log("DaytimeService continuing.");
}


int main(int argc, char* argv[])
{
  TDaytimeService service;
  if (argc > 1 && stricmp(argv[1], "/register") == 0)
    service.Register();
  else if (argc > 1 && stricmp(argv[1], "/unregister") == 0)
    service.Unregister();
  else if (argc > 1 && stricmp(argv[1], "/debug") == 0)
    service.Start(argc, argv);
  else
    service.Start();

  return 0;
}