//***************************************************************************
/*
 * fskcheck.c: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: fskcheck.c,v 1.1 2006/04/30 16:30:27 root Exp $
 */
//***************************************************************************

//***************************************************************************
// Includes
//***************************************************************************

#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/msg.h>

#include "def.h"
#include "msgservice.h"

static int fTimeout = no;

//*************************************************************************
// Timeout-Handler
//*************************************************************************

void timeoutHandler(...)
{
   fTimeout = true;
}

//*************************************************************************
// Global
//*************************************************************************

int choice(char** argv, int& cmd, const char*& msg, int& silent, long& timeout);

const char* commands[] =
{
   "Check",
   "Info",

   0
};

enum Command
{
   cmdUnknown = na,

   cmdCheck,
   cmdInfo,

   cmdCount
};

//***************************************************************************
// Class Sender
//***************************************************************************

class Sender : public MessageService
{
   public:

      Sender();
      ~Sender();

      int wait(int& event);
      int init();
      int send(int event, int type, const char* msg);

      long getTimeout()          { return timeout; }
      void setTimeout(long t)    { timeout = t; } 
      void setSilent(int s)      { silent = s; } 

   protected:

      // data

      int receiverID;
      int msgID;
      char buffer[sizeBuffer];
      char* message;
      Header* header;
      long timeout;
      int open;
      int silent;
};

//***************************************************************************
// Object
//***************************************************************************

Sender::Sender()
{
   struct msgbuf* theMsg  = (struct msgbuf*)buffer;

   header = (Header*)theMsg->mtext;
   message = theMsg->mtext + sizeof(Header);
   receiverID = na;
   msgID = na;
   timeout = 30;
   open = no;
   silent = yes;
}

Sender::~Sender()
{
   if (open)
   {
      if (msgctl(msgID, IPC_RMID, 0) < 0)
         if (!silent) printf("Error: Closing message queue failed, '%s'\n", strerror(errno));
   }
}

//***************************************************************************
// Init
//***************************************************************************

int Sender::init()
{
   // attach to VDR's message queue

   if ((receiverID = msgget(0xd8000000, 0)) < 0)
   {
      if (!silent) printf("Attach to message queue failed, '%s'\n", strerror(errno));
      return fail;
   }

   // create my own message queue

   if ((msgID = msgget(0xd8000001, IPC_CREAT|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0)
   {
      if (!silent) printf("Creation of message queue failed\n");
      return fail;
   }

   open = yes;

   return success;
}

//***************************************************************************
// Wait
//***************************************************************************

int Sender::wait(int& event)
{
   int result;
   int status = success;
   struct itimerval oldTimer,  newTimer;
   struct sigaction oldAction, newAction;

   struct msgbuf* theMsg = (struct msgbuf*)buffer;

   // set timeout-handler

   if (timeout)
   {
      // init register

      memset(&newAction, 0, sizeof(newAction));
      memset(&newTimer, 0, sizeof(newTimer));

      // set timer

      newTimer.it_value.tv_sec = timeout;
      setitimer(ITIMER_REAL, &newTimer, &oldTimer);

      // set handler

      newAction.sa_handler = (void(*)(int))timeoutHandler;
      sigaction(SIGALRM, &newAction, &oldAction);

      // clear timeout-flag

      fTimeout = no;
   }

   // receive auf

   if (!silent) printf("waiting for answer\n");

   if ((result = msgrcv(msgID, theMsg, sizeBuffer, 0, 0)) < 0)
   {
      if (fTimeout)
      {
         if (!silent) 
            printf("Timeout on message receive\n");
      }
      else
      {
         if (!silent) 
            printf("Receiving message failed, '%s'\n", strerror(errno));
      }

      status = fail;
   }

   if (timeout)
   {
      sigaction(SIGALRM, &oldAction, 0);
      setitimer(ITIMER_REAL, &oldTimer,0);
   }

   if (status == success)
      event = header->event;

   return status;
}

//***************************************************************************
// Send
//***************************************************************************

int Sender::send(int event, int type, const char* msg)
{
   struct msgbuf* theMsg = (struct msgbuf*)buffer;
   int sizeMsg = sizeof(Header);

   if (!msg) msg = "";

   if (strlen(msg) > sizeBuffer - sizeof(Header) - sizeof(theMsg->mtype))
   {
      if (!silent) printf("Message to big, aborting\n");
      return fail;
   }

   strcpy(message, msg);
   sizeMsg += strlen(message)+1;
   
   theMsg->mtype = 1;
   header->sender = msgID;
   header->receiver = receiverID;
   header->event = event;
   header->type = type;

   if (msgsnd(header->receiver, theMsg, sizeMsg, 0) < 0)
   {
      if (!silent) printf("send failed, '%s'\n", strerror(errno));
      return fail;
   }

   if (!silent) printf("send to (%ld) succeeded\n", header->receiver);

   return success;
}

//***************************************************************************
// Main
//***************************************************************************

int main(int argc, char** argv)
{
   Sender sender;
   int event;
   const char* msg;
   int cmd;
   long t;
   int silent = yes;

   t = sender.getTimeout();
   msg = 0;

   if (choice(argv, cmd, msg, silent, t) != success)
   {
      printf("Usage: fskcheck <command> [-t timeout][-v]\n");
      printf("         command:  check          - check if vdr currently protected\n");
      printf("         command:  info <message> - show message on osd\n");

      return fail;
   }

   // init

   sender.init(); 
   sender.setTimeout(t);
   sender.setSilent(silent);

   // select command

   switch (cmd)
   {
      case cmdCheck: event = Ms::evtCheck;  break;
      case cmdInfo:  event = Ms::evtShow;   break;
   }

   // send 

   if (sender.send(event, 0, msg) != success)
      return fail;

   // wait

   if (cmd == cmdCheck)
   {
      if (sender.wait(event) == success)
      {
         if (!silent) printf("Got (%d) \n", event);

         if (event == Ms::evtConfirm) return 1;
         if (event == Ms::evtAbort)   return 2;

         return 0;
      }

      else
         return 0;
   }

   return 0;
}

//***************************************************************************
// Choise
//***************************************************************************

int choice(char** argv, int& cmd, const char*& msg, int& silent, long& timeout)
{
   // skip program path

   argv++;

   // get command

   if (!(*argv)) 
      return fail;

   for (cmd = 0; cmd < cmdCount; cmd++)
      if (strcasecmp(commands[cmd], *argv) == 0)
         break;

   if (cmd >= cmdCount)
      return fail;

   argv++;

   while (*argv)
   {
      if (**argv == '-')
      {
         if (strcmp(*argv, "-v") == 0)
         {
            // silent

            silent = no;
         }
         else if (strcmp(*argv, "-t") == 0)
         {
            // timeout 

            if (!(*(++argv))) break;

            timeout = atoi(*argv);
         }
      }
      else
      {
         // the message

         msg = *argv;
      }

      argv++;
   }

   return success;
}
