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

#include <OB/CORBA.h>
#include <OB/Timer.h>
#include <OB/TemplateI.h>

#include <Factory_impl.h>
#include <Database.h>
#include <Reaper.h>

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

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

#ifndef HAVE_NO_EXPLICIT_TEMPLATES
template class OBFixSeq< DBRecord_impl* >;
#else
#ifdef HAVE_PRAGMA_DEFINE
#pragma define(OBFixSeq< DBRecord_impl* >)
#endif
#endif

// ----------------------------------------------------------------------
// DBRecord private member implementation
// ----------------------------------------------------------------------

//
// Create a new record.  Add the record to the set of active objects
// and return a pointer to the CORBA proxy.
//
DBRecord_ptr
DBFactory_impl::makeRecord(DBRecordData* data)
{
    //
    // Create the new record.
    //
    DBRecord_impl* record = new DBRecord_impl(data);

    //
    // Remember records_ is a sequence of DBRecord_impl*.
    // Add the implementation to the records_ sequence.
    //
    records_.append(record);

    return record;
}

// ----------------------------------------------------------------------
// DBFactory constructor and destructor
// ----------------------------------------------------------------------
DBFactory_impl::DBFactory_impl(CORBA_ORB_ptr orb)
    : nextKey_(0), database_(new Database),
      reaper_(new DBReaper(orb, 2, records_)), orb_(CORBA_ORB::_duplicate(orb))
{
}

DBFactory_impl::~DBFactory_impl()
{
    //
    // Release all outstanding active objects.
    //
    for(CORBA_ULong i = 0; i < records_.length(); ++i)
    {
	    //
	    // Ask the database to destroy the object.
	    //
	    database_ -> destroy(records_[i] -> internalKey());

	    //
	    // Disconnect the current record from the ORB.
	    //
	    orb_ -> disconnect(records_[i]);
    }

    delete reaper_;
    delete database_;
}

// ----------------------------------------------------------------------
// DBFactory public member implementation
// ----------------------------------------------------------------------

//
// Create a new record.
// To create a new key we have an constantly incrementing nextKey
// member variable.  For a real application this isn't good enough,
// but for this demonstration this will suffice.
//
DBRecord_ptr
DBFactory_impl::createRecord()
{
    return makeRecord(database_ -> add(nextKey_++));
}

//
// Destroy the record with `key'.  If the record isn't found
// then throw a NoRecord exception.
//
void
DBFactory_impl::destroyByKey(CORBA_Long key)
{
    //
    // First try to find the record in the `active object' list.
    //
    for(CORBA_ULong i = 0; i < records_.length(); ++i)
    {
	//
	// Is this the record?
	//
	if(records_[i] -> internalKey() == key)
	{
	    //
	    // Ask the database to destroy the object.
	    //
	    database_ -> destroy(key);

	    //
	    // Disconnect the record from the ORB.
	    //
	    orb_ -> disconnect(records_[i]);

            records_.remove(i);
            return;
	}
    }

    //
    // Ask the database to destroy the record.
    //
    if(!database_ -> destroy(key))
	throw DBFactory::NoRecord();
}

//
// Retrieve the record identified by `key'.  If the record isn't present
// in the database then throw a NoRecord exception.
//
DBRecord_ptr
DBFactory_impl::getByKey(CORBA_Long key)
{
    //
    // Find look the record among active records.
    //
    for(CORBA_ULong i = 0; i < records_.length(); ++i)
    {
	if(records_[i] -> internalKey() == key)
	{
	    return DBRecord::_duplicate(records_[i]);
	}
    }

    //
    // Next look in the database.
    //
    DBRecordData* data = database_ -> find(key);
    if(data != 0)
    {
	return makeRecord(data);
    }

    throw DBFactory::NoRecord();
    return DBRecord::_nil(); // Some compilers need this
}

//
// Return the keys of all records in the database.
//
DBFactory::DBRecordKeySeq*
DBFactory_impl::getAllKeys()
{
    DBRecordKeySeq_var r = new DBRecordKeySeq(database_ -> size());
    r -> length(database_ -> size());
    for(CORBA_ULong i = 0; i < database_ -> size(); ++i)
    {
	r[i] = database_ -> recordAt(i) -> key;
    }

    return r._retn();
}

// ----------------------------------------------------------------------
// DBRecord_impl constructor and destructor
// ----------------------------------------------------------------------
DBRecord_impl::DBRecord_impl(DBRecordData* data)
    : data_(data), access_(time(0))
{
}

// ----------------------------------------------------------------------
// DBRecord_impl public member implementation
// ----------------------------------------------------------------------

//
// Retrieve the key of the record.
// Updates the access time.
//
CORBA_Long
DBRecord_impl::key()
{
    access_ = time(0);
    return data_ -> key;
}

//
// This methord retrieves the key without updating the access time.
//
CORBA_Long
DBRecord_impl::internalKey()
{
    return data_ -> key;
}

//
// Retrieve the record data.
// Updates the access time.
//
char*
DBRecord_impl::data()
{
    access_ = time(0);
    return CORBA_string_dup(data_ -> data);
}

//
// Updates the record data.
// Updates the access time.
//
void
DBRecord_impl::data(const char* d)
{
    access_ = time(0);
    data_ -> data = CORBA_string_dup(d);
}
