/*
 * Utility class for the mail notification service example.  This class
 * provides access to a single event channel created with persistent
 * ConnectionReliability and EventReliability.
 */

#include "ChannelUtil.h"
#include "Common/ObjectAdapter.h"

/*
 * @func Constructs a ChannelUtil object
 * @parm | orbVar | a reference to the ORB
 * @rdesc LM_ERROR message and exit on error
 */
ChannelUtil::ChannelUtil (CORBA::ORB_ptr orb)
  : orbVar_ (CORBA::ORB::_duplicate (orb))
  , rootPoaVar_ (0)
  , transientPoaVar_ (0)
  , persistentPoaVar_ (0)
  , channelFactoryVar_ (0)
  , channelVar_ (0)
{
  if (CORBA::is_nil (orbVar_.in ()))
    {
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("ERROR: Passed a null ORB reference\n")));
      throw TestException ();
    }
}

/*
 * @func Returns a reference to the event channel
 * @rdesc Reference to the event channel, or LM_ERROR message and exit on error
 */
CosNotifyChannelAdmin::EventChannel_ptr
ChannelUtil::getChannel (void)
{
  if (CORBA::is_nil (channelFactoryVar_.in ()))
    {
      // Obtain a reference to the event channel factory.
      ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Resolving to notification service\n")));
      CORBA::Object_var objVar =
        orbVar_->resolve_initial_references (ACE_TEXT ("NotificationService"));
      if (CORBA::is_nil (objVar.in ()))
        {
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("ERROR: Could not resolve to the notification service\n")),
                            0);
        }
      channelFactoryVar_ = CosNotifyChannelAdmin::EventChannelFactory::_narrow (objVar.in ());
      if (CORBA::is_nil (channelFactoryVar_.in ()))
        {
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("ERROR: Nil reference\n")),
                            0);
        }
    }

  // Try to obtain the channel with ID 0.
  if (CORBA::is_nil (channelVar_.in ()))
    {
      try
        {
          channelVar_ = channelFactoryVar_->get_event_channel (0);

          ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("INFO: Found channel with id 0\n")));
        }
      catch (const CosNotifyChannelAdmin::ChannelNotFound &)
        {
          // No action needed at this point.
        }
    }

  // If it could not be found, create a new channel.
  if (CORBA::is_nil (channelVar_.in ()))
    {
      CosNotifyChannelAdmin::ChannelID id;
      CosNotification::QoSProperties qos = CosNotification::QoSProperties (2);
      CosNotification::AdminProperties adm = CosNotification::AdminProperties (0);

      qos.length (2);
      qos[0].name = CORBA::string_dup (CosNotification::ConnectionReliability);
      qos[0].value <<= CosNotification::Persistent;
      qos[1].name = CORBA::string_dup (CosNotification::EventReliability);
      qos[1].value <<= CosNotification::Persistent;

      channelVar_ = channelFactoryVar_->create_channel (qos, adm, id);

      ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("INFO: Created channel with id %i\n"), id));
    }

  return CosNotifyChannelAdmin::EventChannel::_duplicate (channelVar_.in ());
}

/*
 * @func Returns a reference to the root POA.
 * @rdesc Reference to the root POA, or LM_ERROR message and exit if an error occurs.
 */
PortableServer::POA_ptr
ChannelUtil::getRootPoa (void)
{
  if (CORBA::is_nil (rootPoaVar_.in ()))
    {
      // Obtain a reference to the root POA.
      ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Resolving to root POA\n")));
      CORBA::Object_var objVar =
        orbVar_->resolve_initial_references (ACE_TEXT ("RootPOA"));
      if (CORBA::is_nil (objVar.in ()))
        {
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("ERROR: Could not resolve to the root POA\n")),
                            0);
        }
      rootPoaVar_ = PortableServer::POA::_narrow (objVar.in ());
      if (CORBA::is_nil (rootPoaVar_.in ()))
        {
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("ERROR: Nil reference\n")),
                            0);
        }
    }

  return PortableServer::POA::_duplicate (rootPoaVar_.in ());
}

/*
 * @func Returns a reference to the default transient POA.
 * @rdesc Reference to the default transient POA
 */
PortableServer::POA_ptr
ChannelUtil::getDefaultTransientPoa (void)
{
  if (CORBA::is_nil (transientPoaVar_.in ()))
    {
      transientPoaVar_ = createTransientPoa (ACE_TEXT ("ChannelUtilTransientPOA"));
    }

  return PortableServer::POA::_duplicate (transientPoaVar_.in ());
}

/*
 * @func Returns a reference to the default persistent POA.
 * @rdesc Reference to the default persistent POA
 */
PortableServer::POA_ptr
ChannelUtil::getDefaultPersistentPoa (void)
{
  if (CORBA::is_nil (persistentPoaVar_.in ()))
    {
      persistentPoaVar_ = createPersistentPoa (ACE_TEXT ("ChannelUtilPersistentPOA"));
    }

  return PortableServer::POA::_duplicate (persistentPoaVar_.in ());
}

/*
 * @func Creates a transient POA (with the root POA as parent).
 * @parm | name | the adapter name for the transient POA
 * @rdesc Reference to the transient POA
 */
PortableServer::POA_ptr
ChannelUtil::createTransientPoa (const char *name)
{
  return createPoa (name, 0);
}

/*
 * @func Creates a persistent POA (with the root POA as parent).
 * @parm | name | the adapter name for the persistent POA
 * @rdesc Reference to the persistent POA
 */
PortableServer::POA_ptr
ChannelUtil::createPersistentPoa (const char *name)
{
  return createPoa (name, 1);
}

/*
 * @func Creates a child POA from the root POA, which will use the root POA's manager.
 * @parm | name | the adapter name for the POA
 * @parm | persistent | indicates if the POA is to be persistent (true) or transient (false)
 * @rdesc Reference to the child POA
 */
PortableServer::POA_ptr
ChannelUtil::createPoa (const char *name, CORBA::Boolean persistent)
{
  // Ensure the root POA reference is valid.
  if (CORBA::is_nil (rootPoaVar_.in ()))
    {
      PortableServer::POA_var poaVar = getRootPoa ();
    }

  // Declare the required policies.
  CORBA::PolicyList policies;
  policies.length (2);
  PortableServer::LifespanPolicyValue lifespan;
  PortableServer::IdAssignmentPolicyValue idAssignment;

  // Set the policies depending on the type of POA required.
  lifespan =     (persistent ? PortableServer::PERSISTENT : PortableServer::TRANSIENT);
  idAssignment = (persistent ? PortableServer::USER_ID    : PortableServer::SYSTEM_ID);
  policies[0] = rootPoaVar_->create_lifespan_policy (lifespan);
  policies[1] = rootPoaVar_->create_id_assignment_policy (idAssignment);

  PortableServer::POAManager_var rootManagerVar =
    rootPoaVar_->the_POAManager ();
  // Create the POA and activate the manager.
  PortableServer::POA_var poaVar =
    rootPoaVar_->create_POA (name, rootManagerVar.in (), policies);
  PortableServer::POAManager_var poaManagerVar =
    poaVar->the_POAManager ();
  poaManagerVar->activate ();

  return poaVar._retn ();
}

/*
 * @func Checks to see if a file exists
 * @parm | filename | the name of the file
 * @rdesc Boolean indicating if the file exists or not
 */
CORBA::Boolean
ChannelUtil::fileExists (const char *filename)
{
  FILE *f = ACE_OS::fopen (filename, ACE_TEXT ("r"));
  if (f == 0)
    {
      // Failed to open file.
      return 0;
    }

  // File exists and was opened, so close it.
  ACE_OS::fclose (f);
  return 1;
}

/*
 * @func Deletes a file
 * @parm | filename | the name of the file
 */
void
ChannelUtil::deleteFile (const char *filename)
{
  ACE_OS::unlink (filename);
}

/*
 * @func Writes a CORBA object's IOR out to a file
 * @parm | filename | the name of the file for the IOR
 * @parm | obj | the CORBA object
 * @rdesc LM_ERROR message if  the file could not be written
 */
void
ChannelUtil::writeIOR (const char *filename, CORBA::Object_ptr obj)
{
  CORBA::String_var ior = orbVar_->object_to_string (obj);
  FILE *output_file = ACE_OS::fopen (filename, ACE_TEXT ("w"));
  if (output_file == 0)
    {
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("Could not open output file for writing IOR: %s\n"), filename));
      return;
    }

  ACE_OS::fprintf (output_file, ACE_TEXT ("%s\n"), ior.in ());
  ACE_OS::fclose (output_file);
}

/*
 * @func Creates a CORBA object reference from an IOR in a file
 * @parm | filename | the name of the file with the IOR
 * @rdesc The CORBA object reference, or LM_ERROR message if object could not be created
 */
CORBA::Object_ptr
ChannelUtil::readIOR (const char *filename)
{
  FILE *input_file = ACE_OS::fopen (filename, ACE_TEXT ("r"));
  if (input_file == 0)
    {
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("Could not open input file for reading IOR: %s\n"), filename));
      return 0;
    }

  char ior[1024];
  ACE_OS::fgets (ior, 1024, input_file);
  ACE_OS::fclose (input_file);

  CORBA::Object_var objVar = orbVar_->string_to_object (ior);

  if (CORBA::is_nil (objVar.in ()))
    {
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Could not get valid object reference from IOR in file: %s\n"),
                  filename));
      return 0;
    }

  return objVar._retn ();
}
