// **********************************************************************
//
// Copyright (c) 1999
// Object Oriented Concepts, Inc.
// Billerica, MA, USA
//
// All Rights Reserved
//
// **********************************************************************

#include <OB/CORBA.h>
#include <OB/Util.h>
#include <OB/CosEventChannelAdmin.h>
#include <OB/Reactor.h>
#include <OB/CosEventComm_skel.h>

#include <time.h>
#include <stdlib.h>
#include <stdio.h>

#ifdef HAVE_UNISTD_H
#   include <unistd.h>
#endif

class PullSupplier_impl : public CosEventComm_PullSupplier_skel
{
    CosEventComm_PullConsumer_var consumer_;
    bool slow_;

public:

    PullSupplier_impl(CosEventComm_PullConsumer_ptr, bool);
    ~PullSupplier_impl();

    void disconnect_pull_supplier();

    CORBA_Any* pull();

    CORBA_Any* try_pull(CORBA_Boolean&);
};

PullSupplier_impl::PullSupplier_impl(
    CosEventComm_PullConsumer_ptr consumer,
    bool slow)
    : consumer_(CosEventComm_PullConsumer::_duplicate(consumer)),
      slow_(slow)
{
}

PullSupplier_impl::~PullSupplier_impl()
{
}

void
PullSupplier_impl::disconnect_pull_supplier()
{
    consumer_ = CosEventComm_PullConsumer::_nil();
}

//
// Provide date and time
//
CORBA_Any*
PullSupplier_impl::pull()
{
    char str[256];
    time_t clock;

    clock = time(0);
    sprintf(str, "PullSupplier says: %s", ctime(&clock));
    str[strlen(str) - 1] = 0;

    CORBA_Any* any = new CORBA_Any;
    *any <<= CORBA_string_dup(str);

    if(slow_)
    {
#ifdef WIN32
	Sleep(2000);
#else
	sleep(2);
#endif        
    }

    return any;
}

CORBA_Any*
PullSupplier_impl::try_pull(CORBA_Boolean& hasEvent)
{
    hasEvent = true;

    return pull();
}

static void
usage(const char* progName)
{
    cerr << "Usage:\n";
    cerr << progName << " [options]\n"
	"\n"
	"Options:\n"
	"-h, --help             Show this message.\n"
	"-s, --slow             Run in `slow' mode.\n"
        "-t sec, --timeout sec  Run for [sec] seconds, -1 forever.\n"
        "-n, --no-register      Don't register with the event service.\n"
	;
}

int
main(int argc, char* argv[], char*[])
{
    try
    {
        //
        // Create ORB.
        //
        CORBA_ORB_var orb = CORBA_ORB_init(argc, argv);
	
	//
	// Create BOA.
	//
	CORBA_BOA_var boa = orb -> BOA_init(argc, argv);
	
#if defined(HAVE_JTC)
	orb -> conc_model(CORBA_ORB::ConcModelThreaded);
	boa -> conc_model(CORBA_BOA::ConcModelThreadPerClient);
#else
	orb -> conc_model(CORBA_ORB::ConcModelBlocking);
	boa -> conc_model(CORBA_BOA::ConcModelReactive);
#endif
	
	//
	// Parse command line arguments.
	//
	bool slow = false;
        CORBA_Long timeout = 5;
	bool reg = true;
	for(int i = 1 ; i < argc && *argv[i] == '-' ; i++)
	{
	    if(strcmp(argv[i], "--help") == 0 ||
	       strcmp(argv[i], "-h") == 0)
	    {
		usage(argv[0]);
		return 0;
	    }
	    else if(strcmp(argv[i], "-s") == 0 ||
		    strcmp(argv[i], "--slow") == 0)
	    {
		slow = true;
	    }
            else if(strcmp(argv[i], "-n") == 0 ||
		    strcmp(argv[i], "--no-register") == 0)
	    {
		reg = false;
	    }
	    else if(strcmp(argv[i], "-t") == 0 ||
		    strcmp(argv[i], "--timeout") == 0)
	    {
                if(i + 1 >= argc)
                {
                    cerr << argv[0] << ": argument expected for " << argv[i]
                         << endl;
                    exit(1);
                }
                timeout = atoi(argv[i+1]);
                ++i;
            }
	    else
	    {
		cerr << argv[0] << ": unknown option `"
		     << argv[i] << "'\n" << endl;
		usage(argv[0]);
		return 1;
	    }
	}
	
	//
	// Get event channel.
	//
	CORBA_Object_var obj;
	
	try
	{
	    obj = orb -> resolve_initial_references("EventService");
	}
	catch(const CORBA_ORB::InvalidName&)
	{
	    cerr << argv[0] << ": can't resolve `EventService'" << endl;
	    return 1;
	}
	
	if(CORBA_is_nil(obj))
	{
	    cerr << argv[0] << ": `EventService' is a nil object reference"
		 << endl;
	    return 1;
	}
	
	CosEventChannelAdmin_EventChannel_var e =
	    CosEventChannelAdmin_EventChannel::_narrow(obj);
	if(CORBA_is_nil(e))
	{
	    cerr << argv[0]
		 << ": `EventService' is not an EventChannel object reference"
		 << endl;
	    return 1;
	}
	
	//
	// Get ProxyPullConsumer.
	//
	CosEventChannelAdmin_SupplierAdmin_var supplierAdmin =
	    e -> for_suppliers();
	CosEventChannelAdmin_ProxyPullConsumer_var consumer =
	    supplierAdmin -> obtain_pull_consumer();
	
	//
	// Connect implementation to ProxyPullConsumer.
	//
	CosEventComm_PullSupplier_var supplier =
	    new PullSupplier_impl(consumer, slow);

        //
	// Connect the supplier to the ORB with the same name each
	// time.
	//
	orb -> connect(supplier, "DefaultSupplier");

        boa -> init_servers();
        
        if(reg)
            consumer -> connect_pull_supplier(supplier);
	
	//
	// Run implementation.
	//
        if(timeout < 0)
            boa -> impl_is_ready(CORBA_ImplementationDef::_nil());
        else
        {
#ifdef WIN32
            Sleep(timeout * 1000);
#else
            sleep(timeout);
#endif
        }
                
	//
	// Disconnect the pull consumer here to stop the event service
	// from pulling further events.
	//
	consumer -> disconnect_pull_consumer();
    }
    catch(CORBA_SystemException& ex)
    {
        OBPrintException(ex);
        return 1;
    }

    return 0;
}
