#include "Naming/video/VideoCompany.h"
#include "Naming/video/VideoServiceProvider.h"
#include "Naming/video/VideoSupplier.h"
#include "Naming/video/NamingVideoSupplier.h"

#include <tao/corba.h>

#include "Common/ObjectAdapter.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 naming service.
      CORBA::Object_var objVar =
        orbVar->resolve_initial_references (ACE_TEXT ("NameService"));
      CosNaming::NamingContext_var rootContextVar =
        CosNaming::NamingContext::_narrow (objVar.in ());
      if (CORBA::is_nil (rootContextVar.in ()))
        {
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("Error: Nil reference.\n")),
                            1);
        }

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

      // Assign a type and id to the service provider object.
      CosNaming::Name rootName;
      rootName.length (1);
      rootName[0].id = CORBA::string_dup (ACE_TEXT ("ServiceProvider"));
      rootName[0].kind = CORBA::string_dup (ACE_TEXT ("server"));

      // Bind the service provider into the root context.
      PortableServer::POA_var poaVar =
        VideoSupplier::pServiceProvider_->getObjectAdapter ()->getPOA ();
      objVar = poaVar->servant_to_reference (VideoSupplier::pServiceProvider_);
      rootContextVar->rebind (rootName, objVar.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
                           ACE_TEXT ("Wimbledon")};               //10

      // Create a few arbitary video suppliers and bind them into the naming service.
      NamingVideoSupplier 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.addVideo (cats[1], vids[10]);
      videosRUs.bind (rootContextVar.in ());

      NamingVideoSupplier videos4Sale (ACE_TEXT ("Videos for Sale"));
      videos4Sale.addCategory (cats[0]);
      videos4Sale.addCategory (cats[1]);
      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[1], vids[10]);
      videos4Sale.addVideo (cats[2], vids[6]);
      videos4Sale.addVideo (cats[2], vids[7]);
      videos4Sale.bind (rootContextVar.in ());

      NamingVideoSupplier 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.bind (rootContextVar.in ());

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

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

  return 0;
}
