/*
 * A simple mail tool with a text based interface that can be used to send
 * and receive mail messages.
 */

#include "MailTool.h"

/*
 * @func Constructs a MailTool object
 * @parm | orbVar | reference to the ORB
 * @parm | username | the user's name
 * @rdesc NoSuchAccount is thrown if an account does not exist for the user
 */
MailTool::MailTool (CORBA::ORB_ptr orb, const char *username)
  : mailBoxVar_ (0)
{
  ChannelUtil util (orb);

  ACE_CString iorFileName = ACE_CString (username) + ACE_TEXT (".ior");
  if (!util.fileExists (iorFileName.c_str ()))
    {
      throw Mail::NoSuchAccount ();
    }

  CORBA::Object_var objVar = util.readIOR (iorFileName.c_str ());
  mailBoxVar_ = Mail::MailBox::_narrow (objVar.in ());
}

/*
 * @func Sends a message to the mailbox
 * @parm message | the message to send
 * @parm to | string sequence of recipients
 * @rdesc LM_ERROR message on error
 */
void
MailTool::sendMessage (const char *message, Mail::StringSeq &to)
{
  mailBoxVar_->sendMessage (message, to);
}

/*
 * @func Receives a message from the mailbox
 * @parm sender | string to hold the sender information
 * @parm message | string to hold the message information
 * @rdesc boolean indicating if a message was waiting: if true, sender and message
 *        will contain the information
 */
CORBA::Boolean
MailTool::receiveMessage (CORBA::String_out sender, CORBA::String_out message)
{
  return mailBoxVar_->receiveMessage (sender, message);
}

/*
 * @func Utility method: converts a comma-separated list of names to a string sequence
 * @parm list | string with the comma-separated names
 * @rdesc string sequence of the individual names
 */
Mail::StringSeq *
parseList (char *list, const char *separators)
{
  Mail::StringSeq *to = 0;
  ACE_NEW_RETURN (to,
                  Mail::StringSeq,
                  0);
  Mail::StringSeq_var toVar = to;

  char *token;
  token = ACE_OS::strtok (list, separators);
  int count = 0;
  while (token != 0)
    {
      // Add it to the sequence.
      toVar->length (++count);
      (*toVar)[count-1] = CORBA::string_dup (token);

      // Repeat.
      token = ACE_OS::strtok (0, separators);
    }

  return toVar._retn ();
}

/*
 * @func Utility method: get a line of input from the console, without a trailing carriage return / linefeed
 * @parm buffer | caller-allocated buffer for the input
 * @parm size | the size of the buffer
 */
void
getInput (char *buffer, int size)
{
  ACE_OS::fgets (buffer, size, stdin);
  buffer[ACE_OS::strcspn (buffer, ACE_TEXT ("\n"))] = '\0';
}

int
main (int argc, char *argv[])
{
  try
    {
      char *username = 0;

      if (argc < 2)
        {
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("Usage: mailtool <username>\n")),
                            1);
        }
      username = argv[1];

      ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Initialising ORB\n")));
      CORBA::ORB_var orbVar = CORBA::ORB_init (argc, argv);

      MailTool *pMailTool = 0;
      try
        {
          ACE_NEW_THROW_EX (pMailTool,
                            MailTool (orbVar.in (), username),
                            CORBA::NO_MEMORY ());
        }
      catch (const Mail::NoSuchAccount &)
        {
          ACE_ERROR ((LM_ERROR, ACE_TEXT ("User %s does not exist\n"), username));
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("Please use the admin tool to create users\n")),
                            1);
        }
      ACE_Auto_Ptr<MailTool> holder (pMailTool);

      const int BUFFER_SIZE = 256;
      char line[BUFFER_SIZE];
      char recipients[BUFFER_SIZE];
      char message[BUFFER_SIZE];
      CORBA::Boolean keepGoing = 1;

      while (keepGoing)
        {
          // Display options and get the user's selection.
          ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(S)end, (R)eceive or (Q)uit?\n")));
          getInput (line, BUFFER_SIZE);

          if ((ACE_OS::strcmp (line, ACE_TEXT ("send")) == 0) ||
              (ACE_OS::strcmp (line, ACE_TEXT ("s")) == 0))
            {
              // Send a message.
              ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Please enter the recipients of this mail message. " \
                                              "You may have more than one recipient as long as they " \
                                              "are seperated by the ',' character\n")));
              getInput (recipients, BUFFER_SIZE);
              Mail::StringSeq_var toVar = parseList (recipients, ACE_TEXT (","));

              ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Please enter the message to send\n")));
              getInput (message, BUFFER_SIZE);
              pMailTool->sendMessage (message, toVar);
            }
          else if ((ACE_OS::strcmp (line, ACE_TEXT ("receive")) == 0) ||
                   (ACE_OS::strcmp (line, ACE_TEXT ("r")) == 0))
            {
              // Try to receive a message.
              CORBA::String_var senderVar;
              CORBA::String_var messageVar;

              if (pMailTool->receiveMessage (senderVar.out (), messageVar.out ()))
                {
                  ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Received mail from %s\n"), senderVar.in ()));
                  ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Message: %s\n"), messageVar.in ()));
                }
              else
                {
                  ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("No new messages\n")));
                }
            }
          else if ((ACE_OS::strcmp (line, ACE_TEXT ("quit")) == 0) ||
                   (ACE_OS::strcmp (line, ACE_TEXT ("q")) == 0))
            {
              ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Exiting\n")));
              keepGoing = 0;
            }
          else
            {
              ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Unrecognised command: %s\n"), line));
            }
        }

      orbVar->destroy ();
    }
  catch (const CORBA::Exception &ex)
    {
      ex._tao_print_exception (ACE_TEXT ("ERROR: Exception in MailTool.cpp:"));
      return 1;
    }

  return 0;
}
