/*
 * Creates structured events representing stock quotes and pushes them to
 * an event channel.
 */

#include "StockQuote.h"
#include <orbsvcs/Shutdown_Utilities.h>

class Service_Shutdown_Functor : public Shutdown_Functor
{
public:
  Service_Shutdown_Functor (CORBA::ORB_ptr orb)
    : orb_ (CORBA::ORB::_duplicate (orb))
  {
  }

  void operator() (int which_signal)
  {
    ACE_DEBUG ((LM_DEBUG,
                ACE_TEXT ("shutting down on signal %d\n"),
                which_signal));
    (void) this->orb_->shutdown ();
  }

private:
  CORBA::ORB_var orb_;
};

namespace
{
  // All the companies about whom stock quotes will be generated.
  const int MAX_COMPANIES = 25;
  const char * const companies[MAX_COMPANIES] =
    {
      ACE_TEXT ("apple"),
      ACE_TEXT ("boeing"),
      ACE_TEXT ("coca-cola"),
      ACE_TEXT ("disney"),
      ACE_TEXT ("eds"),
      ACE_TEXT ("ford"),
      ACE_TEXT ("geico"),
      ACE_TEXT ("honeywell"),
      ACE_TEXT ("ibm"),
      ACE_TEXT ("j. p. morgan"),
      ACE_TEXT ("kellogs"),
      ACE_TEXT ("levi strauss"),
      ACE_TEXT ("microsoft"),
      ACE_TEXT ("nike"),
      ACE_TEXT ("oracle"),
      ACE_TEXT ("phillips"),
      ACE_TEXT ("quantum"),
      ACE_TEXT ("reebok"),
      ACE_TEXT ("sun microsystems"),
      ACE_TEXT ("texas instruments"),
      ACE_TEXT ("unisys"),
      ACE_TEXT ("viacom"),
      ACE_TEXT ("wells fargo"),
      ACE_TEXT ("xerox"),
      ACE_TEXT ("york international")
    };

  // A record of  the last quote for each company.
  short quotes[MAX_COMPANIES] =
    {
      100, 100, 100, 100, 100,
      100, 100, 100, 100, 100,
      100, 100, 100, 100, 100,
      100, 100, 100, 100, 100,
      100, 100, 100, 100, 100
    };
}

CosNotification::StructuredEvent *
StockQuote::createNextEvent (void)
{
  CosNotification::StructuredEvent *pEvent = 0;
  ACE_NEW_THROW_EX (pEvent,
                    CosNotification::StructuredEvent (),
                    CORBA::NO_MEMORY ());
  CosNotification::StructuredEvent_var holder (pEvent);

  // Event header.
  int priority = rand () % 10;
  CosNotification::EventType type;
  type.domain_name = CORBA::string_dup (ACE_TEXT ("News"));
  type.type_name = CORBA::string_dup (ACE_TEXT ("StockQuote"));

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

  CosNotification::PropertySeq varHeader;
  varHeader.length (2);
  varHeader[0].name = CORBA::string_dup (CosNotification::Timeout);
  varHeader[0].value <<= 10 * 100 * 1000 * 60 * 5;
  varHeader[1].name = CORBA::string_dup (CosNotification::Priority);
  varHeader[1].value <<= priority;
  pEvent->header.fixed_header = fixedHeader;
  pEvent->header.variable_header = varHeader;

  // Filterable data.
  int companyIndex = rand () % MAX_COMPANIES;
  const char *company = companies[companyIndex];
  CosNotification::PropertySeq filterable;
  filterable.length (3);
  filterable[0].name = CORBA::string_dup (ACE_TEXT ("bureau"));
  filterable[0].value <<=
    CORBA::String_var (CORBA::string_dup (ACE_TEXT ("Wall Street")));
  filterable[1].name = CORBA::string_dup (ACE_TEXT ("company"));
  filterable[1].value <<=
    CORBA::String_var (CORBA::string_dup (company));
  filterable[2].name = CORBA::string_dup (CosNotification::Priority);
  filterable[2].value <<= priority;
  pEvent->filterable_data = filterable;

  // Remainder-of-body
  CORBA::String_var news = generateNextMessage (companyIndex);
  pEvent->remainder_of_body <<= news;

  return holder._retn ();
}

/*
 * @func Randomly generate a new stock quote for a given company
 * @parm | companyIndex | the index of the company for which to generate the new quote
 * @rdesc the new stock quote
 */
char *
StockQuote::generateNextMessage (int companyIndex)
{
  const char *company = companies[companyIndex];
  short lastValue = quotes[companyIndex];

  // Randomly generate a new value and store it for future reference.
  int nextValue = rand () % 500;
  quotes[companyIndex] = (short) nextValue;

  // Construct the message.
  char lastStr[4];
  ACE_OS::sprintf (lastStr, ACE_TEXT ("%i"), lastValue);
  char nextStr[4];
  ACE_OS::sprintf (nextStr, ACE_TEXT ("%i"), nextValue);

  char message[60];
  ACE_OS::strcpy (message, company);
  if (nextValue < lastValue)
    {
      ACE_OS::strcat (message, ACE_TEXT (" dropped from "));
    }
  else
    {
      ACE_OS::strcat (message, ACE_TEXT (" increased from "));
    }
  ACE_OS::strcat (message, lastStr);
  ACE_OS::strcat (message, ACE_TEXT (" to "));
  ACE_OS::strcat (message, nextStr);

  return CORBA::string_dup (message);
}

int
main (int argc, char **argv)
{
  try
    {
      // Initialise the ORB instance.
      CORBA::ORB_var orbVar =
        CORBA::ORB_init (argc, argv, ACE_TEXT (""));

      // Cleaning guard.
      Service_Shutdown_Functor killer (orbVar.in ());
      Service_Shutdown kill_contractor (killer);

      // Create the stock quote.
      StockQuote *pStockQuote = 0;
      ACE_NEW_THROW_EX (pStockQuote,
                        StockQuote (orbVar.in ()),
                        CORBA::NO_MEMORY ());
      PortableServer::ServantBase_var holder (pStockQuote);

      // Connect to the event channel.
      pStockQuote->connectToChannel ();

      // Send events.
      pStockQuote->broadcast ();

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

  return 0;
}
