/*
 * This CORBA server delegates to a (singleton) VideoServiceProvider object
 * that manages VideoSupplier objects using the Trading Service. The program
 * registers a Service Type describing a VideoSupplier offer. Each
 * VideoSupplier object exports its offer to the service.
 */

#include <tao/AnyTypeCode/TypeCode.h>

#include "Trading/video/VideoCompany.h"
#include "Trading/video/VideoServiceProvider.h"
#include "Trading/video/TradingVideoSupplier.h"
#include "Trading/video/VideoSupplier.h"

#include "Common/ObjectAdapter.h"

#include <tao/corba.h>
#include <tao/PortableServer/PortableServer.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_;
};

int main (int argc, char **argv)
{
  try
    {
      // Initialise the ORB.
      CORBA::ORB_var orbVar = CORBA::ORB_init (argc, argv);
      ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("INFO: Initialised ORB\n")));

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

      // Get a reference to the trading service.
      CORBA::Object_var objVar =
        orbVar->resolve_initial_references (ACE_TEXT ("TradingService"));
      CosTrading::Lookup_var lookupVar =
        CosTrading::Lookup::_narrow (objVar.in ());

      if (CORBA::is_nil (lookupVar.in ()))
        {
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("Error: nil reference.\n")),
                            1);
        }

      // Get a reference to the service type repository.
      objVar = lookupVar->type_repos ();
      CosTradingRepos::ServiceTypeRepository_var reposVar =
        CosTradingRepos::ServiceTypeRepository::_narrow (objVar.in ());

      if (CORBA::is_nil (lookupVar.in ()))
        {
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("Error: nil reference.\n")),
                            1);
        }

      // Create the service provider.
      ACE_NEW_RETURN (VideoSupplier::pServiceProvider_,
                      VideoServiceProvider (orbVar.in ()),
                      1);
      PortableServer::ServantBase_var holder (VideoSupplier::pServiceProvider_);

      // Register the service type.
      CosTradingRepos::ServiceTypeRepository::PropStructSeq properties;
      properties.length (3);

      properties[0].name = CORBA::string_dup (ACE_TEXT ("type"));
      properties[0].value_type = CORBA::_tc_string;
      properties[0].mode = CosTradingRepos::ServiceTypeRepository::PROP_MANDATORY_READONLY;

      properties[1].name = CORBA::string_dup (ACE_TEXT ("title"));
      properties[1].value_type = CORBA::_tc_string;
      properties[1].mode = CosTradingRepos::ServiceTypeRepository::PROP_MANDATORY_READONLY;

      properties[2].name = CORBA::string_dup (ACE_TEXT ("cost"));
      properties[2].value_type = CORBA::_tc_float;
      properties[2].mode = CosTradingRepos::ServiceTypeRepository::PROP_MANDATORY;

      CosTradingRepos::ServiceTypeRepository::ServiceTypeNameSeq defaultSupers;

      try
        {
          reposVar->add_type (ACE_TEXT ("IDL:video_supplier:1.0"),
                              ACE_TEXT ("IDL:video_supplier_if:1.0"),
                              properties,
                              defaultSupers);
        }
      catch (const CosTradingRepos::ServiceTypeRepository::ServiceTypeExists &)
        {
          TradingVideoSupplier::cleanAllOffers (lookupVar.in ());
        }

      // Create a few arbitrary categories.
      const char *cats[] = {ACE_TEXT ("Films"),           //0
                            ACE_TEXT ("Sporting Events"), //1
                            ACE_TEXT ("News Broadcasts"), //2
                            ACE_TEXT ("Soap Operas")};    //3

      // Create a few arbitrary videos.
      const char *vids[] = {ACE_TEXT ("Bridge On The River Kwai"), //0
                            ACE_TEXT ("Mutiny On The Bounty"),     //1
                            ACE_TEXT ("Lawrence Of Arabia"),       //2
                            ACE_TEXT ("Star Wars"),                //3
                            ACE_TEXT ("Great Expectations"),       //4
                            ACE_TEXT ("FA Cup Final"),             //5
                            ACE_TEXT ("BBC"),                      //6
                            ACE_TEXT ("CNN"),                      //7
                            ACE_TEXT ("Coronation Street"),        //8
                            ACE_TEXT ("Neighbours")};              //9

      // Create a few arbitary video suppliers and add their offers into the trading service.

      TradingVideoSupplier videosRUs (ACE_TEXT ("Videos R us"));
      videosRUs.addCategory (cats[0]);
      videosRUs.addCategory (cats[1]);
      videosRUs.addVideo (cats[0], vids[0]);
      videosRUs.addVideo (cats[0], vids[1]);
      videosRUs.addVideo (cats[1], vids[5]);
      videosRUs.exportOffer (lookupVar.in ());

      TradingVideoSupplier videos4Sale (ACE_TEXT ("Videos for Sale"));
      videos4Sale.addCategory (cats[0]);
      videos4Sale.addCategory (cats[2]);
      videos4Sale.addVideo (cats[0], vids[2]);
      videos4Sale.addVideo (cats[0], vids[3]);
      videos4Sale.addVideo (cats[0], vids[4]);
      videos4Sale.addVideo (cats[2], vids[6]);
      videos4Sale.addVideo (cats[2], vids[7]);
      videos4Sale.exportOffer (lookupVar.in ());

      TradingVideoSupplier videosOnDemand (ACE_TEXT ("Videos on Demand"));
      videosOnDemand.addCategory (cats[2]);
      videosOnDemand.addCategory (cats[3]);
      videosOnDemand.addVideo (cats[2], vids[6]);
      videosOnDemand.addVideo (cats[3], vids[8]);
      videosOnDemand.addVideo (cats[3], vids[9]);
      videosOnDemand.exportOffer (lookupVar.in ());

      // Accept incoming requests on this server.
      VideoSupplier::pServiceProvider_->activateManager ();
      ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ready...\n")));
      orbVar->run ();

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

  return 0;
}
