/*
   sdwarn.c --- example shutdown client.

   (c) Copyright 1995 SHW Wabnitz
   Written by Bernhard Fastenrath (fasten@shw.com)

   This file may be distributed under the terms
   of the GNU General Public License.
*/

#include <dos.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <exec/types.h>
#include <proto/exec.h>
#include <proto/dos.h>

#include "queue/queue.h"
#include "queue/shutdown.h"

#if defined (__GNUC__)
#include "queue/queue_inline.h"
#elif defined (__SASC_60)
#include "queue/queue_pragmas.h"
#endif

void CloseTimerDevice (void);
int CheckCLISession (void);

struct Library *QueueBase = NULL;
struct timerequest *TimerReq = NULL;
struct MsgPort *TimerPort = NULL;
ULONG TimerMask = 0;
ULONG QueueMask = 0;
ULONG CliNumber = 0;
BPTR CliStdout = NULL;
struct Process *MyProc;
APTR WindowPtr;

char *optarg = NULL;

int
getopt (int argc, char *argv[], char *opts)
{
  static int pos = 0;
  char *c;

  while (++pos < argc && argv[pos][0] != '-');
  if (pos >= argc)
    return -1;
  if ((c = strchr (opts, (int) argv[pos][1])) == 0)
    return (int) '?';
  if (*(c+1) != ':')
    return (int) *c;
  if (strlen (argv[pos]) > 2)
    optarg = argv[pos] + 2;
  else if (pos+1 < argc)
    optarg = argv[pos+1];
  else
    return (int) '?';
  return (int) *c;
}

int
CheckCLISession (void)
{
  struct Process *pr;
  int code = 0;

  if (!CliNumber)
    return 1;

  Forbid ();
  if (pr = FindCliProc (CliNumber))
  {
    /* We might have found a new CLI
       with the same number.
    */
    if (pr -> pr_COS == CliStdout)
      code = 1;
  }
  Permit ();
  return code;
}

void
CloseTimerDevice (void)
{
  if (!TimerPort)
    return;

  AbortIO ((struct IORequest *) TimerReq);
  WaitIO ((struct IORequest *) TimerReq);
  CloseDevice ((struct IORequest *) TimerReq);
  DeleteIORequest (TimerReq);
  DeleteMsgPort (TimerPort);
  TimerPort = NULL;
}

void
StartTimer (void)
{
  if (TimerPort)
  {
    TimerReq -> tr_node.io_Command = TR_ADDREQUEST;
    TimerReq -> tr_time.tv_secs  = 3;
    TimerReq -> tr_time.tv_micro = 0;
    SendIO ((struct IORequest *) TimerReq);
  }
}

ULONG
OpenTimerDevice (void)
{
  if (!(TimerPort = CreateMsgPort ()))
    return 0;

  if (!(TimerReq = (struct timerequest *)
	CreateIORequest (TimerPort, sizeof (struct timerequest))))
  {
    DeleteMsgPort (TimerPort);
    return 0;
  }
  if (OpenDevice (TIMERNAME, UNIT_VBLANK, (struct IORequest *) TimerReq, 0))
  {
    DeleteIORequest (TimerReq);
    DeleteMsgPort (TimerPort);
    return 0;
  }
  StartTimer ();
  return TimerMask = 1 << TimerPort -> mp_SigBit;
}

void
GoingDownMessage (int seconds)
{
  if (seconds / 3600)
    printf ("The system is going down in %d:%.2d hours.\n",
            seconds / 3600, (seconds % 3600) / 60);
  else if (seconds / 60)
    printf ("The system is going down in %d:%.2d minutes.\n",
            seconds / 60, seconds % 60);
  else
    printf ("The system is going down in %d seconds.\n", seconds);
}

int
main (int argc, char *argv[])
{
  QMessage *qmsg, *lqmsg;
  ShutdownMessage *sm;
  UpsInfo *ups;
  char *UpsEvents[] = UPS_EV_NAMES;
  int verbose = 0;
  ULONG sigbit, sigmask, rmask;
  QHandle qh;
  int opt;

  MyProc = (struct Process *) FindTask (0);
  WindowPtr = MyProc -> pr_WindowPtr;
  MyProc -> pr_WindowPtr = (void *) -1;

  while ((opt = getopt (argc, argv, "c:v")) != -1)
    switch (opt)
    {
      case 'v': verbose = 1; break;
      case 'c':
        if (CliNumber = atoi (optarg))
        {
          struct Process *pr;

          Forbid ();
          if (pr = FindCliProc (CliNumber))
	    CliStdout = pr -> pr_COS;
          Permit ();
          if (!CliStdout)
          {
	    printf ("Invalid CLI.\n", CliNumber);
	    exit ( EXIT_FAILURE );
          }
        }
	break;
      default:
        printf ("Usage: %s [-v] [-c <cli>].\n", argv[0]);
	exit (EXIT_FAILURE);
    }

  if (!(QueueBase = OpenLibrary ("queue.library", 0)))
  {
    printf ("Failed to open queue.library.\n");
    return EXIT_FAILURE;
  }
  if (CliNumber)
  {
    if (!OpenTimerDevice ())
    {
      printf ("Failed to open timer.device.\n");
      CloseLibrary (QueueBase);
      return EXIT_FAILURE;
    }
  }
  if ((sigbit = AllocSignal (-1)) != -1)
  {
    QueueMask = 1 << sigbit;
    sigmask = SIGBREAKF_CTRL_C | TimerMask | QueueMask;

    if (qh = QOpen ("shutdown", QMODE_LISTEN, sigbit))
    {
      while (1)
      {
	rmask = Wait (sigmask);
	if (rmask & TimerMask)
	  StartTimer ();

        if (rmask & QueueMask)
        {
	  lqmsg = NULL;
	  while (qmsg = QGetMsg (qh))
	  {
	    sm = (ShutdownMessage *) qmsg -> qm_Data;
            lqmsg = qmsg;

	    switch (sm -> sm_Status)
	    {
	      case SHUTDOWN_WARN:
	      case SHUTDOWN_NOW:
	        GoingDownMessage (sm -> sm_TimeLeft);
	        break;
	      case SHUTDOWN_INFO:
	        if (verbose)
                {
	          if (sm -> sm_Info == SDMI_UPS_MONITOR)
                  {
	            printf ("Shutdown info received:\n");
                    ups = sm -> sm_Extra;
                    if (ups -> ui_Event > UPS_EV_MAX)
                      printf ("Event = %d (unknown)\n", ups -> ui_Event);
                    else
                      printf ("Event = %s.\n", UpsEvents[ups -> ui_Event]);
	            printf ("Charge = %d.\n", ups -> ui_Charge);
	            printf ("Remaining time = %d.\n", ups -> ui_Time);	        
                  }
	          else
	            printf ("Shutdown info received.\n");
                }
	        break;
	      case SHUTDOWN_ABORT:
	        printf ("Shutdown has been aborted.\n");
	        break;
	      case SHUTDOWN_UMOUNT:
	        printf ("The system is going down, unmounting filesystems.\n");
	        break;
	      case SHUTDOWN_HALT:
	        printf ("The system is halted.\n");
	        break;
	      default:
	        printf ("Unknown message.\n");
	        break;
	    }
          }
	  if (lqmsg)
	    QReplyMsg (qh);
	}
        if (rmask & SIGBREAKF_CTRL_C || !CheckCLISession ())
        {
	  if (rmask & SIGBREAKF_CTRL_C)
	    printf ("CTRL-C\n");
	  else
	    printf ("\n%s: End of session.\n", argv[0]);
	  QClose (qh);
	  break;
        }
      }
    }
    else
      printf ("Can't open shutdown queue.\n");
  }
  else
    printf ("Can't allocate signal.\n");
  CloseTimerDevice ();
  CloseLibrary (QueueBase);
  MyProc -> pr_WindowPtr = WindowPtr;
  exit ( EXIT_SUCCESS );
}
