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

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

#include <OB/CosEventChannelAdmin.h>

#include <PropagatorBaseThread.h>

// ----------------------------------------------------------------------
// Template instantiations
// ----------------------------------------------------------------------

#ifndef HAVE_NO_EXPLICIT_TEMPLATES
template class JTCHandleT<PropagatorBaseThread>;
#else
#ifdef HAVE_PRAGMA_DEFINE
#pragma define(JTCHandleT<PropagatorBaseThread>)
#endif
#endif

// ----------------------------------------------------------------------
// PropagatorBaseThread protected member implementation
// ----------------------------------------------------------------------

//
// Return true if we should try sending the event again.
//
bool
PropagatorBaseThread::retry()
{
    JTCSynchronized sync(*this);

    //
    // Have we exceeded the max number of retries?
    //
    if(++retries_ > maxRetries_)
        return false;

    //
    // Provide some debugging information.
    //
#ifdef OB_TRACE
    if(OBMessageViewer::instance() -> getTraceLevel() >= 2)
    {
        CORBA_String_var msg = CORBA_string_dup("retry(");
        msg += id_;
        msg += ") ";
        msg += retries_;
        msg += '/';
        msg += maxRetries_;
        msg += " in ";
        msg += retry_;
        msg += " ms";
        OBMessageViewer::instance() -> trace(2, msg);
    }
#endif

    delay(retry_);

    //
    // Calculate the next timeout interval.
    //
    retry_ = (CORBA_ULong)((CORBA_Double)retry_ * retryMultiplier_);

    return !done_;
}

void
PropagatorBaseThread::delay(CORBA_ULong msec)
{
    JTCSynchronized sync(*this);

    //
    // Calculate the current timeout interval in timeval format.
    //
    timeval len;
    len.tv_sec = msec/1000;
    len.tv_usec = (msec % 1000) * 1000;

    //
    // When are we done?
    //
    timeval timeDone = OBTimerList::timeAdd(OBTimerList::timeNow(), len);
    while(!done_)
    {
        //
        // Are we done yet?
        //
        timeval now = OBTimerList::timeNow();
        if(OBTimerList::timeCmp(timeDone, now) <= 0)
            break;
        
        //
        // No, calculate how long we have to wait now.
        //
        timeval diff = OBTimerList::timeSub(timeDone, now);
        CORBA_ULong interval = diff.tv_sec * 1000 + diff.tv_usec / 1000;
        
        //
        // If the time is less than one msec, then we're also done.
        //
        if(interval == 0)
            break;
        
        //
        // Block for this long. If we are interrupted before this
        // interval then we are either done, or we have to
        // recalculate.
        //
        try
        {
            wait(interval);
        }
        catch(const JTCInterruptedException&)
        {
        }
    }
}

void
PropagatorBaseThread::resetRetry()
{
    retries_ = 0;
    retry_ = retryTimeout_;
}

// ----------------------------------------------------------------------
// PropagatorBaseThread constructor/destructor
// ----------------------------------------------------------------------

PropagatorBaseThread::PropagatorBaseThread(
    CORBA_ULong id,
    CORBA_ULong maxRetries,
    CORBA_ULong retryTimeout,
    CORBA_Double retryMultiplier)
    : maxRetries_(maxRetries),
      retryTimeout_(retryTimeout),
      retryMultiplier_(retryMultiplier),
      retry_(retryTimeout),
      retries_(0),
      done_(false),
      id_(id)
{
}

PropagatorBaseThread::~PropagatorBaseThread()
{
}

// ----------------------------------------------------------------------
// PropagatorBaseThread public member implementation
// ----------------------------------------------------------------------

//
// Stop the thread.
//
void
PropagatorBaseThread::halt()
{
    JTCSynchronized sync(*this);
    done_ = true;
    notify();
}
