// **********************************************************************
//
// 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/OBEventChannelFactory.h>
#include <OB/CosEventComm_skel.h>
#include <OB/Reactor.h>

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

#include <stdlib.h>

class PushConsumer_impl : public CosEventComm_PushConsumer_skel
{
    CosEventComm_PushSupplier_var supplier_;
    bool slow_;

public:

    PushConsumer_impl(CosEventComm_PushSupplier*, bool);
    ~PushConsumer_impl();

    void disconnect_push_consumer();

    void push(const CORBA_Any&);
};

PushConsumer_impl::PushConsumer_impl(
    CosEventComm_PushSupplier_ptr supplier, bool slow)
    : supplier_(CosEventComm_PushSupplier::_duplicate(supplier)),
      slow_(slow)
{
}

PushConsumer_impl::~PushConsumer_impl()
{
}

void
PushConsumer_impl::disconnect_push_consumer()
{
    cout << "disconnect_push_consumer" << endl;
}

void
PushConsumer_impl::push(const CORBA_Any& any)
{
    char* s;

    //
    // Display event data if Any is a string
    //
    if(any >>= s)
		cout << s << endl;
    if(slow_)
    {
#ifdef WIN32
		Sleep(2000);
#else
		sleep(2);
#endif        
    }
}

static void
usage(const char* progName)
{
    cerr << "\nUsage:\n";
    cerr << progName << " [options] event-channel-name\n"
        "\n"
        "Options:\n"
        "-h, --help             Show this message.\n"
        "-s, --slow             Run in `slow' mode.\n"
        "-t sec, --time 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;
        const char* ecName = 0;
        for(int i = 1; i < argc; ++i)
        {
            if(*argv[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;
                }
            }
            else if(!ecName)
                ecName = argv[i];
            else
            {
                usage(argv[0]);
                return 0;
            }
        }

        if (!ecName)
        {
            usage(argv[0]);
            return 0;
        }
	
        //
        // Get event channel factory.
        //
        CORBA_Object_var objF;
	
        try
        {
            objF = orb -> resolve_initial_references("EventChannelFactory");
        }
        catch(const CORBA_ORB::InvalidName&)
        {
            cerr << argv[0] << ": can't resolve `EventChannelFactory'" << endl;
            return 1;
        }
	
        if(CORBA_is_nil(objF))
        {
            cerr << argv[0]
                 << ": `EventChannelFactory' is a nil object reference"
                 << endl;
            return 1;
        }
	
        OBEventChannelFactory_EventChannelFactory_var f =
            OBEventChannelFactory_EventChannelFactory::_narrow(objF);
	
        if(CORBA_is_nil(f))
        {
            cerr << argv[0]
                 << ": `EventChannelFactory' is not an "
                 << "EventChannelFactory object reference"
                 << endl;
            return 1;
        }
	
        //
        // Get event channel.
        //
        CORBA_Object_var obj;

        try
        {
            obj = f -> get_channel_by_id(CORBA_string_dup(ecName));
        }
        catch(OBEventChannelFactory_ChannelNotAvailable&)
        {
            try
            {
                obj = f -> create_channel(CORBA_string_dup(ecName));
                cerr << argv[0] << ": `Channel " << ecName << "' created"
                     << endl;
            }
            catch(OBEventChannelFactory_ChannelAlreadyExists&)
            {
            }
        }
            
        if(CORBA_is_nil(obj))
        {
            cerr << argv[0] << ": `" << ecName << "' is a nil object reference"
                 << endl;
            return 1;
        }
	
        CosEventChannelAdmin_EventChannel_var e =
            CosEventChannelAdmin_EventChannel::_narrow(obj);
	
        if(CORBA_is_nil(e))
        {
            cerr << argv[0]
                 << ": `" << ecName
                 << "' is not an EventChannel object reference"
                 << endl;
            return 1;
        }
	
        //
        // Get ProxyPushSupplier.
        //
        CosEventChannelAdmin_ConsumerAdmin_var consumerAdmin =
            e -> for_consumers();
        CosEventChannelAdmin_ProxyPushSupplier_var supplier =
            consumerAdmin -> obtain_push_supplier();
	
	//
	// Connect implementation to ProxyPushSupplier.
	//
        CosEventComm_PushConsumer_var consumer =
            new PushConsumer_impl(supplier, slow);

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

        boa -> init_servers();

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