/*
 * Notify log example.
 */

#include "NotifyLogExample.h"

#define LOG_EVENTS 4

NotifyLogExample::NotifyLogExample (CORBA::ORB_ptr orb, ObjectAdapter &adapter)
  : orbVar_ (CORBA::ORB::_duplicate (orb)),
    adapter_ (adapter),
    notifyLogFactoryVar_ (0),
    proxyVar_ (0)
{
  if (CORBA::is_nil (orbVar_.in ()))
    {
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("ERROR: ORB not initialised\n")));
    }
}

int
NotifyLogExample::init (void)
{
  // Resolve to the notify log factory.
  ACE_DEBUG ((LM_INFO, ACE_TEXT ("Resolving to notify log factory\n")));
  try
    {
      CORBA::Object_var objVar =
        orbVar_->resolve_initial_references (ACE_TEXT ("NotifyLogFactory"));
      notifyLogFactoryVar_ =
        DsNotifyLogAdmin::NotifyLogFactory::_narrow (objVar.in ());

      if (CORBA::is_nil (notifyLogFactoryVar_.in ()))
        {
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("ERROR: Narrowed to a null notify log factory\n")),
                            -1);
        }

      return 0;
    }
  catch (const CORBA::Exception &ex)
    {
      ex._tao_print_exception (ACE_TEXT ("ERROR: Failed to resolve to notify log factory\n"));
      return -1;
    }
}

DsNotifyLogAdmin::NotifyLog_ptr
NotifyLogExample::create (void)
{
  try
    {
      DsLogAdmin::LogFullActionType logFullAction = DsLogAdmin::wrap;
      CORBA::ULongLong max_size = 0; // 0 means "infinite"
      CosNotification::QoSProperties qos = CosNotification::QoSProperties (0);
      CosNotification::QoSProperties adm = CosNotification::QoSProperties (0);
      DsLogAdmin::LogId logId = 0;

      DsLogAdmin::CapacityAlarmThresholdList *tmp = 0;
      ACE_NEW_THROW_EX (tmp,
                        DsLogAdmin::CapacityAlarmThresholdList (75),
                        CORBA::NO_MEMORY ());
      DsLogAdmin::CapacityAlarmThresholdList_var thresVar = tmp;

      DsNotifyLogAdmin::NotifyLog_var notifyLogVar =
        notifyLogFactoryVar_->create (logFullAction, max_size, thresVar.in (), qos, adm, logId);

      if (CORBA::is_nil (notifyLogVar.in ()))
        {
          ACE_ERROR ((LM_ERROR, ACE_TEXT ("ERROR: Notify log is null\n")));
          throw TestException ();
        }

      ACE_DEBUG ((LM_INFO, ACE_TEXT ("Created log with id: %i\n"), logId));
      return notifyLogVar._retn ();
    }
  catch (const DsLogAdmin::InvalidLogFullAction &ilfa)
    {
      ilfa._tao_print_exception (ACE_TEXT ("ERROR: Notify log creation failed with InvalidLogFullAction\n"));
      throw;
    }
  catch (const DsLogAdmin::InvalidThreshold &it)
    {
      it._tao_print_exception (ACE_TEXT ("ERROR: Notify log creation failed with InvalidThreshold\n"));
      throw;
    }
  catch (const CosNotification::UnsupportedQoS &uq)
    {
      uq._tao_print_exception (ACE_TEXT ("ERROR: Notify log creation failed with UnsupportedQoS\n"));
      throw;
    }
  catch (const CosNotification::UnsupportedAdmin &ua)
    {
      ua._tao_print_exception (ACE_TEXT ("ERROR: Notify log creation failed with UnsupportedAdmin\n"));
      throw;
    }
  catch (const CORBA::Exception &ex)
    {
      ex._tao_print_exception (ACE_TEXT ("ERROR: Event log creation failed\n"));
      throw;
    }
}

void
NotifyLogExample::setFilter (DsNotifyLogAdmin::NotifyLog_ptr channel)
{
  const char *grammar = ACE_TEXT ("EXTENDED_TCL");
  const char *constraint = ACE_TEXT ("$._type_id == 'string'");

  try
    {
      CosNotifyFilter::FilterFactory_var filterFactoryVar =
        channel->default_filter_factory ();

      // Create the filter.
      CosNotifyFilter::Filter_var filterVar =
        filterFactoryVar->create_filter (grammar);

      // Set up the constraints.
      CosNotification::EventTypeSeq evts;
      evts.length (0);
      CosNotifyFilter::ConstraintExp exp;
      exp.event_types = evts;
      exp.constraint_expr = CORBA::string_dup (constraint);
      CosNotifyFilter::ConstraintExpSeq exps;
      exps.length (1);
      exps[0] = exp;

      // Add the constraints.
      CosNotifyFilter::ConstraintInfoSeq_var tmp =
        filterVar->add_constraints (exps);

      // Set the filter.
      channel->set_filter (filterVar.in ());
    }
  catch (const CosNotifyFilter::InvalidGrammar &ig)
    {
      ig._tao_print_exception (ACE_TEXT ("ERROR: Failed to create filter -- InvalidGrammar\n"));
      throw;
    }
  catch (const CosNotifyFilter::InvalidConstraint &ic)
    {
      ic._tao_print_exception (ACE_TEXT ("ERROR: Failed to create filter -- InvalidConstraint\n"));
      throw;
    }

  ACE_DEBUG ((LM_INFO, ACE_TEXT ("Filter set on the notify log\n")));
}

void
NotifyLogExample::connect (DsNotifyLogAdmin::NotifyLog_ptr channel)
{
  CosNotifyChannelAdmin::ClientType type = CosNotifyChannelAdmin::ANY_EVENT;

  try
    {
      // Create the supplier reference.
      PortableServer::POA_var poaVar = adapter_.getPOA ();
      CORBA::Object_var objVar =
        poaVar->servant_to_reference (this);
      CosNotifyComm::PushSupplier_var supplierVar =
        CosNotifyComm::PushSupplier::_narrow (objVar.in ());

      // Obtain a proxy.
      CosNotifyChannelAdmin::ChannelID id;
      CosNotifyChannelAdmin::SupplierAdmin_var adminVar =
        channel->default_supplier_admin ();
      CosNotifyChannelAdmin::ProxyConsumer_var pcVar =
        adminVar->obtain_notification_push_consumer (type, id);
      proxyVar_ =
        CosNotifyChannelAdmin::ProxyPushConsumer::_narrow (pcVar.in ());

      // Perform the connection.
      proxyVar_->connect_any_push_supplier (supplierVar.in ());
    }
  catch (const CosEventChannelAdmin::AlreadyConnected &ac)
    {
      ac._tao_print_exception (ACE_TEXT ("ERROR: Failed to connect -- proxy is already connected\n"));
      throw;
    }
  catch (const CosNotifyChannelAdmin::AdminLimitExceeded &ale)
    {
      ale._tao_print_exception (ACE_TEXT ("ERROR: Failed to connect -- maximum number of suppliers exceeded\n"));
      throw;
    }
  catch (const CORBA::Exception &ex)
    {
      ex._tao_print_exception (ACE_TEXT ("ERROR: Failed to connect\n"));
      throw;
    }

  ACE_DEBUG ((LM_INFO, ACE_TEXT ("Connected supplier to log channel\n")));
}

void
NotifyLogExample::generate (void)
{
  DsLogAdmin::Anys data (LOG_EVENTS);
  data.length (LOG_EVENTS);

  double d = 42.424242;
  int n = 123456789;
  char a = 'a';
  CORBA::String_var s = CORBA::string_dup (ACE_TEXT ("Hello world"));

  data[0] <<= s;
  data[1] <<= d;
  data[2] <<= n;
  data[3] <<= a;

  for (int i = 0; i < LOG_EVENTS; i++)
    {
      try
        {
          proxyVar_->push (data[i]);
        }
      catch (const CORBA::Exception &ex)
        {
          ex._tao_print_exception (ACE_TEXT ("ERROR: Failed to push message\n"));
          throw;
        }
    }

  ACE_DEBUG ((LM_INFO, ACE_TEXT ("Generated some log messages\n")));
}

void
NotifyLogExample::disconnect (void)
{
  ACE_DEBUG ((LM_INFO, ACE_TEXT ("Disconnecting supplier from log channel\n")));

  try
    {
      proxyVar_->disconnect_push_consumer ();
    }
  catch (const CORBA::SystemException &se)
    {
      se._tao_print_exception (ACE_TEXT ("ERROR: Failure while disconnecting\n"));
      throw;
    }

  ACE_DEBUG ((LM_INFO, ACE_TEXT ("Disconnected supplier from log channel\n")));
}

void
NotifyLogExample::disconnect_push_supplier (void)
{
  ACE_DEBUG ((LM_INFO, ACE_TEXT ("Disconnect callback invoked\n")));
}

void
NotifyLogExample::subscription_change (CosNotification::EventTypeSeq const &/*added*/,
                                       CosNotification::EventTypeSeq const &/*removed*/)
{
  ACE_DEBUG ((LM_INFO, ACE_TEXT ("Subscription change callback invoked\n")));
}

int
main (int argc, char **argv)
{
  try
    {
      DsNotifyLogAdmin::NotifyLog_var logVar;

      // Get the orb reference and start the thread (non-blocking).
      ACE_DEBUG ((LM_INFO, ACE_TEXT ("Initialising ORB\n")));
      CORBA::ORB_var orbVar = CORBA::ORB_init (argc, argv);

      // Create the example.
      ObjectAdapter adapter (orbVar.in ());
      NotifyLogExample *pExample = 0;
      ACE_NEW_THROW_EX (pExample,
                        NotifyLogExample (orbVar.in (), adapter),
                        CORBA::NO_MEMORY ());
      PortableServer::ServantBase_var holder (pExample);
      if (pExample->init ())
        {
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("ERROR: Couldn't init servant\n")),
                            -1);
        }
      adapter.createTransient (pExample);

      // Run the example.
      logVar = pExample->create ();
      pExample->setFilter (logVar.in ());
      pExample->connect (logVar.in ());
      pExample->generate ();
      pExample->disconnect ();

      orbVar->destroy ();
    }
  catch (const CORBA::Exception &ex)
    {
      ex._tao_print_exception (ACE_TEXT ("ERROR: Test failed\n"));
      return -1;
    }

  return 0;
}
