/*
 * A utility to administrate a mail server, allowing user accounts to be created and
 * deleted.
 */

#include "Admin.h"
#include "Common/ObjectAdapter.h"
#include <ace/Get_Opt.h>

/*
 * @func Constructs an Admin object
 * @parm | orbVar | a reference to the ORB
 * @rdesc LM_ERROR message and exit on error
 */
Admin::Admin (CORBA::ORB_ptr orb)
  : orbVar_ (CORBA::ORB::_duplicate (orb))
  , mailServerVar_ (0)
{
  ChannelUtil util (orbVar_.in ());

  CORBA::Object_var objVar = util.readIOR (ACE_TEXT ("MailServer.ior"));
  if (CORBA::is_nil (objVar.in ()))
    {
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("\nCould not read the Mail Server's IOR\nPossible causes:\n")));
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("(1) the Mail Server has not been started\n")));
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("(2) the Mail Server was started but has been stopped\n")));
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("(3) the Mail Server was started in a different directory\n")));
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("(4) the Mail Server IOR file (MailServer.ior) has been deleted or corrupted\n")));
      throw TestException ();
    }

  mailServerVar_ = Mail::MailServer::_narrow (objVar.in ());
  if (CORBA::is_nil (mailServerVar_.in ()))
    {
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("Failed to narrow the mail server reference\n")));
      throw TestException ();
    }
}

/*
 * @func Creates an account for the specified user
 * @parm | username | the user's name
 * @rdesc LM_ERROR message if an account exists for this user
 */
void
Admin::createAccount (const char *username)
{
  try
    {
      mailServerVar_->createAccount (username);
    }
  catch (const Mail::AccountExists &ae)
    {
      ae._tao_print_exception (ACE_TEXT ("Account already exists\n"));
      throw;
    }
}

/*
 * @func Deletes an account for the specified user
 * @parm | username | the user's name
 * @rdesc LM_ERROR message if no account exists for this user
 */
void
Admin::deleteAccount (const char *username)
{
  try
    {
      mailServerVar_->deleteAccount (username);
    }
  catch (const Mail::NoSuchAccount &nsa)
    {
      nsa._tao_print_exception (ACE_TEXT ("Account does not exist\n"));
      throw;
    }
}

enum Admin_Action {
  act_NONE,
  act_CREATE,
  act_DELETE
};

enum Admin_Action command = act_NONE;
const char *username = 0;

int
parse_args (int argc, char *argv[])
{
  ACE_Get_Opt get_opts (argc, argv);
  get_opts.long_option (ACE_TEXT ("create"), 'c', ACE_Get_Opt::ARG_REQUIRED);
  get_opts.long_option (ACE_TEXT ("delete"), 'd', ACE_Get_Opt::ARG_REQUIRED);

  do
    {
      int c = get_opts ();
      if (c == -1)
        break;

      switch (c)
        {
        case 'c':
          if (command != act_NONE)
            {
              return -1;
            }
          command = act_CREATE;
          username = get_opts.opt_arg ();
          break;
        case 'd':
          if (command != act_NONE)
            {
              return -1;
            }
          command = act_DELETE;
          username = get_opts.opt_arg ();
          break;
        }
    }
  while (true);

  if (command == act_NONE)
    {
      return -1;
    }

  // Indicates sucessful parsing of the command line
  return 0;
}

int
main (int argc, char *argv[])
{
  try
    {
      ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Initialising ORB\n")));
      CORBA::ORB_var orbVar = CORBA::ORB_init (argc, argv);

      if (parse_args (argc, argv) != 0)
        {
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("Usage: admin --create|--destroy <username>\n")),
                            1);
        }

      Admin admin (orbVar.in ());

      if (command == act_CREATE)
        {
          admin.createAccount (username);
        }
      else if (command == act_DELETE)
        {
          admin.deleteAccount (username);
        }
      else
        {
          ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Unrecognised command: %d\n"), command));
        }

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

  return 0;
}
