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

#include <OB/CORBA.h>
#include <OB/Util.h>

#include <Factory.h>

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

#ifndef WIN32
#include <unistd.h>
#endif // !WIN32

#ifdef HAVE_FSTREAM
#   include <fstream>
#else
#   include <fstream.h>
#endif

int
main(int argc, char* argv[], char*[])
{
    try
    {
	//
	// Create ORB
	//
	CORBA_ORB_var orb = CORBA_ORB_init(argc, argv);
	
	//
	// Get "factory" object
	//
	const char* refFile = "factory.ref";
	ifstream in; // Must use open(name), not ifstream in(name) (VC++ bug)
	in.open(refFile);
	if(in.fail())
	{
	    cerr << argv[0] << ": can't open `" << refFile << "': "
		 << strerror(errno) << endl;
	    return EXIT_FAILURE;
	}
	
	char s[1000];
	in >> s;
	
	CORBA_Object_var obj = orb -> string_to_object(s);
	assert(!CORBA_is_nil(obj));
	
	DBFactory_var factory = DBFactory::_narrow(obj);
	assert(!CORBA_is_nil(factory));

	//
	// Purge any records left in the database.
	//
	{
	    DBFactory::DBRecordKeySeq_var records = factory -> getAllKeys();
	    cout << "Purging " << records -> length() << " records." << endl;
	    for(CORBA_ULong i = 0; i < records -> length(); ++i)
	    {
		try
		{
		    factory -> destroyByKey(records[i]);
		}
		catch(DBFactory::NoRecord&)
		{
		    cout << "Record " << records[i]
		    	 << " doesn't exist anymore." << endl;
		}
	    }
	}

	//
	// Create a record.
	//
	CORBA_Long key;
	{
	    DBRecord_var record = factory -> createRecord();
	    key = record -> key();
	    cout << "Created record: " << key << endl;
	    record -> data("This is some data");
	    CORBA_String_var data(record -> data());
	    cout << "Added data: " << data << endl;
	}

	//
	// Verify that getByKey works.
	//
	{
	    cout << "Retrieving record again." << endl;
	    DBRecord_var record = factory -> getByKey(key);
	    cout << "Got record: " << record -> key() << endl;
	    CORBA_String_var data(record -> data());
	    cout << "Read data: " << data << endl;
	}

	//
	// Destroy the record.
	//
	{
	    DBRecord_var record = factory -> getByKey(key);

	    cout << "Destroying record." << endl;
	    factory -> destroyByKey(key);

	    cout << "Expecting an OBJECT_NOT_EXIST exception..." << endl;
	    try
	    {
		record -> key();
		cout << "FAILED!" << endl;
	    }
	    catch(const CORBA_OBJECT_NOT_EXIST&)
	    {
		cout << "OK!" << endl;
	    }

            cout << "Verifying that the record is destroyed." << endl;
            try
            {
                DBRecord_var checkRecord = factory -> getByKey(key);
                cout << "FAILED!" << endl;
            }
            catch(DBFactory::NoRecord&)
            {
                cout << "OK!" << endl;
            }
	}

	//
	// Test that reaper works.  This assumes that the timeout
	// and reaping periods on the server are extremely fast.
	//
	{
	    cout << "Creating another record." << endl;
	    DBRecord_var record = factory -> createRecord();
	    key = record -> key();
	    cout << "Created record: " << key << endl;
	    record -> data("This is some more data");
	    CORBA_String_var data(record -> data());
	    cout << "Added data: " << data << endl;

	    cout << "Sleeping to wait for the record to be reaped." << endl;
#if defined(WIN32)
	    Sleep(1000*5);
#else
	    sleep(5);
#endif
	    cout << "Expecting an OBJECT_NOT_EXIST exception..." << endl;
	    try
	    {
		record -> key();
		cout << "FAILED!" << endl;
	    }
	    catch(const CORBA_OBJECT_NOT_EXIST&)
	    {
		cout << "OK!" << endl;
	    }

	    cout << "Regetting object" << endl;
	    record = factory -> getByKey(key);

	    data = record -> data();
	    cout << "Data is: " << data << endl;

	    factory -> destroyByKey(key);
	}

	//
	// Create 10 records.  Demonstrate that the client application
	// must be prepared to deal with an OBJECT_NOT_EXIST exception
	// at any time when dealing with DBRecord objects.
	//
	{
	    cout << "Creating 10 records" << endl;
	    CORBA_ULong i;
	    for(i = 0; i < 10; ++i)
	    {
		DBRecord_var rec = factory -> createRecord();
	    }

	    DBFactory::DBRecordKeySeq_var records = factory -> getAllKeys();

	    cout << records -> length() << " records now in database." << endl;

	    //
	    // We want all 10 records as _var types.  Now we demonstrate
	    // setting of data, handling OBJECT_NOT_EXIST errors.
	    //
	    DBRecord_var recs[10];
	    for(i = 0; i < 10; ++i)
	    {
		recs[i] = factory -> getByKey(records[i]);
	    }

	    cout << "Sleeping five seconds." << endl;
#if defined(WIN32)
	    Sleep(1000*5);
#else
	    sleep(5);
#endif
	    cout << "Setting data" << endl;
	    i = 0;
	    while(i < 10)
	    {
		char buf[100];
		sprintf(buf, "This is record: %lu", (unsigned long)i);
		try
		{
		    recs[i] -> data(buf);
		}
		catch(CORBA_OBJECT_NOT_EXIST&)
		{
		    cout << "Reconnecting record: " << records[i] << endl;
		    recs[i] = factory -> getByKey(records[i]);
		    continue;
		}
		++i;
	    }

	    cout << "Sleeping five seconds." << endl;
#if defined(WIN32)
	    Sleep(1000*5);
#else
	    sleep(5);
#endif
	    cout << "Getting data" << endl;
	    i = 0;
	    while(i < 10)
	    {
		try
		{
		    CORBA_String_var data = recs[i] -> data();
		    cout << "Data: " << data << endl;
		}
		catch(CORBA_OBJECT_NOT_EXIST&)
		{
		    cout << "Reconnecting record: " << records[i] << endl;
		    recs[i] = factory -> getByKey(records[i]);
		    continue;
		}
		++i;
	    }

	    //
	    // Purge these records.
	    //
	    cout << "Purging " << records -> length() << " records." << endl;
	    for(i = 0; i < records -> length(); ++i)
	    {
		try
		{
		    factory -> destroyByKey(records[i]);
		}
		catch(DBFactory::NoRecord&)
		{
		    cout << "Record " << records[i]
		    	 << " doesn't exist anymore." << endl;
		}
	    }
	}
    }
    catch(CORBA_SystemException& ex)
    {
	OBPrintException(ex);
	return EXIT_FAILURE;
    }
    
    return EXIT_SUCCESS;
}

