//#
//# Time Client in C++
//# Copyright 2004 by Eric Y. Theriault
//# All Rights Reserved.
//# http://www.eyt.ca/CORBA
//#
#ifdef EYT_MICO
#include "Mico/Time.h"
#include <coss/CosNaming.h>
#else
#ifdef EYT_TAO
#include "TAO/TimeC.h"
#include <orbsvcs/CosNamingC.h>
#else
#error Undefined ORB type.
#endif
#endif
#include "MulticastSocket.h"
#include <string>
#include <iostream>
#include <fstream>
#include <errno.h>

namespace {
   //
   // Port to multicast on.  This number is random.
   //
   const int MulticastPort = 7007;

   //
   // Address to multicast to.  The default is link-local, which is
   // generally OK, but in the presence of a segmented network with
   // routers, this address and the TTL will have to be changed.
   //
   const char *MulticastAddress = "224.0.0.1";

   //
   // This is the message that is multicasted to discover the IOR.
   //
   const char *MulticastDiscoverServer = "DiscoverIOR";
};

int main( int argc, char ** argv )
{
   try {
      // Create and Initialize the ORB
      CORBA::ORB_var orb = CORBA::ORB_init( argc, argv );

      // Multicast for the Nameservice IOR 
      std::string ior;
      {
         // Please note that a real implementation would provide more
         // options, and possibly share this with the server.  This is
         // only meant to show the functionality.
         MulticastSocket mcast( MulticastPort );
         mcast.setSoTimeout( 1000 );
         mcast.send( MulticastAddress, MulticastDiscoverServer );
         try {
            while ( true ) {
               // Acquire a packet
               struct sockaddr_in address;
               std::string buffer = mcast.receive( address );

               // Check the message
               if ( strcmp( buffer.c_str(), MulticastDiscoverServer ) != 0 ) {
                  ior = buffer;
                  break;
               }
            }
         } catch ( std::exception & e ) {
            std::cerr << "Unable to discover the IOR via Multicast; "
                      << "Is the Multicast server running?  "
                      << e.what() << std::endl;
            return 1;
         }
      }

      // Get the root naming context.
      CORBA::Object_var objectReference = orb->string_to_object( ior.c_str() );
      CosNaming::NamingContext_var ncRef = 
                   CosNaming::NamingContext::_narrow( objectReference );

      // Resolve a reference into an object.
      std::string timeName = "Time"; 
      CosNaming::Name name;
      name.length( 1 );
      name[ 0 ].id = CORBA::string_dup( timeName.c_str() ); 
      name[ 0 ].kind = CORBA::string_dup( "" );
      CORBA::Object_var object;
      object = ncRef->resolve( name );
      TimeTools::Time_var timeImpl = TimeTools::Time::_narrow( object );
      if ( CORBA::is_nil( timeImpl ) ) {
         std::cerr << "Argument is not a Time reference." << std::endl;
         return 1;
      }

      // OK.  Let's call the Time interface's methods.
      TimeTools::TimeOfDay time = timeImpl->getTime();
      std::cout << "Time server's time is: "
                << time.hour << ":" << time.minute << ":"
                << time.second << std::endl;
      return 0;
   } catch ( const CORBA::Exception & e ) {
      std::cerr << "Caught a CORBA exception." << std::endl;
#ifdef EYT_MICO
      e._print( std::cerr );
      std::cerr << std::endl;
#else
#ifdef EYT_TAO
      std::cerr << e._name() << std::endl;
#else
#error More verbose error here required.
#endif
#endif
      return 1;
   } catch ( const std::exception & e ) {
      std::cerr << "Caught Exception: " << e.what() << std::endl;
      return 1;
   } catch ( ... ) {
      std::cerr << "Unknown exception caught." << std::endl;
      return 1;
   }
}

