/*
 * Utility class for the simple 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))
{
  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)
{
  CosNotifyChannelAdmin::EventChannel_var channelVar;

  // Obtain a reference to the event channel factory.
  ACE_DEBUG ((LM_INFO, 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 ((LM_ERROR, ACE_TEXT ("ERROR: Could not resolve to the notification service\n")));
      throw TestException ();
    }

  CosNotifyChannelAdmin::EventChannelFactory_var channelFactoryVar =
    CosNotifyChannelAdmin::EventChannelFactory::_narrow (objVar.in ());

  // Try to obtain the channel with ID 0.
  try
    {
      channelVar = channelFactoryVar->get_event_channel (0);

      ACE_DEBUG ((LM_INFO, ACE_TEXT ("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;

      try
        {
          channelVar = channelFactoryVar->create_channel (qos, adm, id);

          ACE_DEBUG ((LM_INFO, ACE_TEXT ("Created channel with id %i\n"), id));
        }
      catch (const CORBA::Exception &ex)
        {
          ex._tao_print_exception (ACE_TEXT ("ERROR: Failed to create the event channel\n"));
          throw;
        }
    }

  return channelVar._retn ();
}

/*
 * @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)
{
  ACE_DEBUG ((LM_INFO, 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 ((LM_ERROR,
                  ACE_TEXT ("ERROR: Could not resolve to the root POA\n")));
      throw TestException ();
    }

  PortableServer::POA_var rootPoaVar =
    PortableServer::POA::_narrow (objVar.in ());
  PortableServer::POAManager_var managerVar =
    rootPoaVar->the_POAManager ();
  managerVar->activate ();

  return rootPoaVar._retn ();
}

/*
 * @func Creates a structured event containing the specified number
 * @parm | count | number to store in the event
 * @rdesc The structured event
 */
CosNotification::StructuredEvent *
ChannelUtil::createStructuredEvent (const int count)
{
  CosNotification::StructuredEvent *pEvent = 0;
  ACE_NEW_THROW_EX (pEvent,
                    CosNotification::StructuredEvent (),
                    CORBA::NO_MEMORY ());
  CosNotification::StructuredEvent_var holder (pEvent);

  // Header.
  CosNotification::EventType type;
  type.domain_name = CORBA::string_dup (ACE_TEXT ("TestDomain"));
  type.type_name = CORBA::string_dup (ACE_TEXT ("TestType"));

  CosNotification::FixedEventHeader fixedHeader;
  fixedHeader.event_name = CORBA::string_dup (ACE_TEXT ("TestEvent"));
  fixedHeader.event_type = type;

  CosNotification::PropertySeq variableHeader;
  variableHeader.length (0);
  CosNotification::EventHeader header;
  header.fixed_header = fixedHeader;
  header.variable_header = variableHeader;
  pEvent->header = header;

  // Filterable data.
  CosNotification::PropertySeq filterable;
  filterable.length (0);
  pEvent->filterable_data = filterable;

  // Remainder-of-body.
  pEvent->remainder_of_body <<= count;

  return holder._retn ();
}

/*
 * @func Creates an Any event containing the specified number
 * @parm | count | number to store in the event
 * @rdesc The Any event
 */
CORBA::Any_ptr
ChannelUtil::createAnyEvent (const int count)
{
  CORBA::Any_ptr pAny = 0;
  ACE_NEW_THROW_EX (pAny,
                    CORBA::Any (),
                    CORBA::NO_MEMORY ());
  CORBA::Any_var holder (pAny);

  *pAny <<= count;
  return holder._retn ();
}

/*
 * @func Prints the details of a structured event to a buffer
 * @parm | buffer | a suitably-allocated buffer
 * @parm | event | the structured event to display
 * @parm | full | boolean flag: true to display all event information, false to display only the body
 */
void
ChannelUtil::printEvent (char *buffer, const CosNotification::StructuredEvent &event, CORBA::Boolean full)
{
  // Header information.
  if (full)
    {
      ACE_OS::strcpy (buffer, ACE_TEXT ("\nFIXED HEADER:\n"));
      ACE_OS::strcat (buffer, ACE_TEXT ("Domain: <"));
      ACE_OS::strcat (buffer, event.header.fixed_header.event_type.domain_name);
      ACE_OS::strcat (buffer, ACE_TEXT (">, Type: <"));
      ACE_OS::strcat (buffer, event.header.fixed_header.event_type.type_name);
      ACE_OS::strcat (buffer, ACE_TEXT (">, Event: <"));
      ACE_OS::strcat (buffer, event.header.fixed_header.event_name);
      ACE_OS::strcat (buffer, ACE_TEXT (">\n"));

      ACE_OS::strcat (buffer, ACE_TEXT ("VARIABLE HEADER:\n"));
      printPropertySeq (buffer, event.header.variable_header);
    }

  // Filterable data information.
  if (full)
    {
      ACE_OS::strcat (buffer, ACE_TEXT ("FILTERABLE DATA:\n"));
      printPropertySeq (buffer, event.filterable_data);
    }

  // Remainder-of-body information.
  if (full)
    {
      ACE_OS::strcat (buffer, ACE_TEXT ("REMAINDER-OF-BODY:\n"));
      printAny (buffer, event.remainder_of_body);
    }
  else
    {
      ACE_OS::strcpy (buffer, ACE_TEXT ("Body: "));
      printAny (buffer, event.remainder_of_body);
    }
}

/*
 * @func Prints the details of an Any event to a buffer
 * @parm | buffer | a suitably-allocated buffer
 * @parm | event | the Any event to display
 * @parm | full | boolean flag: true to display all event information, false to display only the body
 */
void
ChannelUtil::printEvent (char *buffer, const CORBA::Any &event, CORBA::Boolean full)
{
  CosNotification::StructuredEvent *strEvent;
  if (event >>= strEvent)
    {
      // Any contained a structured event.
      printEvent (buffer, *strEvent, full);
    }
  else
    {
      // Any was a simple event-service style event.
      ACE_OS::strcpy (buffer, ACE_TEXT ("Body: "));
      printAny (buffer, event);
    }
}

void
ChannelUtil::printPropertySeq (char *buffer, const CosNotification::PropertySeq &seq)
{
for (CORBA::ULong i = 0; i < seq.length (); i++)
  {
    ACE_OS::strcat (buffer, ACE_TEXT ("Name: <"));
    ACE_OS::strcat (buffer, seq[i].name);
    ACE_OS::strcat (buffer, ACE_TEXT (">, Value: <"));
    printAny (buffer, seq[i].value);
    ACE_OS::strcat (buffer, ACE_TEXT (">\n"));
  }
}

void
ChannelUtil::printAny (char *buffer, const CORBA::Any &any)
{
  char val[20];

  CORBA::Short s;
  if (any >>= s)
    {
      ACE_OS::sprintf (val, ACE_TEXT ("%i"), s);
      ACE_OS::strcat (buffer, val);
      return;
    }

  CORBA::UShort us;
  if (any >>= us)
    {
      ACE_OS::sprintf (val, ACE_TEXT ("%i"), us);
      ACE_OS::strcat (buffer, val);
      return;
    }

  CORBA::Long l;
  if (any >>= l)
    {
      ACE_OS::sprintf (val, ACE_TEXT ("%i"), l);
      ACE_OS::strcat (buffer, val);
      return;
    }

  CORBA::ULong ul;
  if (any >>= ul)
    {
      ACE_OS::sprintf (val, ACE_TEXT ("%i"), ul);
      ACE_OS::strcat (buffer, val);
      return;
    }

  CORBA::LongLong ll;
  if (any >>= ll)
    {
      ACE_OS::sprintf (val, ACE_INT64_FORMAT_SPECIFIER, ll);
      ACE_OS::strcat (buffer, val);
      return;
    }

  CORBA::ULongLong ull;
  if (any >>= ull)
    {
      ACE_OS::sprintf (val, ACE_UINT64_FORMAT_SPECIFIER, ull);
      ACE_OS::strcat (buffer, val);
      return;
    }

  CORBA::Float f;
  if (any >>= f)
    {
      ACE_OS::sprintf (val, ACE_TEXT ("%f"), f);
      ACE_OS::strcat (buffer, val);
      return;
    }

  CORBA::Double d;
  if (any >>= d)
    {
      ACE_OS::sprintf (val, ACE_TEXT ("%f"), d);
      ACE_OS::strcat (buffer, val);
      return;
    }

  CORBA::LongDouble ld;
  if (any >>= ld)
    {
      ACE_OS::sprintf (val, ACE_TEXT ("%Lf"), static_cast<long double> (ld));
      ACE_OS::strcat (buffer, val);
      return;
    }

  CORBA::Boolean b;
  if (any >>= CORBA::Any::to_boolean (b))
    {
      if (b == 1)
        {
          ACE_OS::strcat (buffer, ACE_TEXT ("TRUE"));
        }
      else
        {
          ACE_OS::strcat (buffer, ACE_TEXT ("FALSE"));
        }
      return;
    }

  const char *str;
  if (any >>= str)
    {
      ACE_OS::strcat (buffer, str);
      return;
    }

  // Unhandled case.
  ACE_OS::strcat (buffer, ACE_TEXT ("[UNKNOWN_ANY]"));
  return;
}
