//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / OVOPS++
//
//File: conduittest.cpp
//
//Version: $Revision: 1.1 $
//
//State: $State: Exp $
//
//Date: $Date: 1997/07/14 12:43:33 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Author:
//      Juhana Räsänen
//
//Description:
//      A program to test the conduits
//
//Copyright:
//
//
//Licence:
//
//
//History: 

#include <typeinfo>
#include <iostream.h>
#include "system.h"
#include "protocol.h"
#include "state.h"
#include "messenge.h"
#include "timers.h"

static const int NUMBER_OF_TEST_ROUNDS = 1000;
static const int DEFAULT_TIMEOUT = 100;
static const char * const TIMERNAME = "TEST_TIMER";

class pfTestMessage;
class pfTestState;


//------------------------------------------------------------------------

class pfTestProtocol : public pfProtocol, public pfTimers
{
    public:
        pfTestProtocol(int nTimeouts_, pfUlong timeout_);
        void decrementN(void) {_nTimeouts--;};
        int getN(void) {return _nTimeouts;};

    private:
        int _nTimeouts;
};

class pfTestState : public pfState
{
    public:
        virtual void testMessageAct(pfTestMessage *messenger_,
                                    pfTestProtocol *protocol_);
        virtual void testTimeoutAct(pfTestProtocol *protocol_);
};

class pfTestMessage : public pfMessenger
{
    public:
        pfTestMessage(int id_, int counter_ = NUMBER_OF_TEST_ROUNDS)
            {_id = id_; _counter = counter_;};
        pfTestMessage(const pfTestMessage &other_)
            {_id = other_._id; _counter = other_._counter;};
        virtual void apply(pfState *state_,
                           pfProtocol *protocol_);
        void decrementCounter(void) {_counter--;};
        int getId(void) {return _id;};
        int getCounter(void) {return _counter;};

    private:
        int _id;
        int _counter;
};

class pfTestTimeout : public pfTimerMessenger
{
    public:
        pfTestTimeout(void);
        pfTestTimeout(const pfTestTimeout &other_);
        ~pfTestTimeout(void);

        virtual void apply(pfState *state_,
                           pfProtocol *protocol_);
        virtual pfTimerMessenger *clone(void) const;
};

//------------------------------------------------------------------------

pfTestProtocol :: pfTestProtocol(int nTimeouts_, pfUlong timeout_)
    : pfProtocol(),
      pfTimers(this),
      _nTimeouts(nTimeouts_)
{
    pfTestTimeout *messenger = new pfTestTimeout;
    defineTimer(TIMERNAME, messenger, timeout_);
    return;
}

//------------------------------------------------------------------------

void pfTestState :: testMessageAct(
    pfTestMessage *messenger_,
    pfTestProtocol *protocol_)
{
    if (messenger_->getCounter() > 0)
    {
//        cout << "pfConduitTest: Received message "
//             << messenger_->getId() << ", round "
//             << messenger_->getCounter() << endl;
        pfTestMessage *newMessage = new pfTestMessage(*messenger_);
        newMessage->decrementCounter();
        protocol_->toB(newMessage);
    }
    else
    { 
        cout << "pfConduitTest: Message "
             << messenger_->getId() << " done!" << endl;
    }
    return;
}

void pfTestState :: testTimeoutAct(pfTestProtocol *protocol_)
{
    pfTestMessage *newMessage = new pfTestMessage(protocol_->getN());
    protocol_->toB(newMessage);
    cout << "pfConduitTest: Sent message " << protocol_->getN() << endl;
    
    if (protocol_->getN() > 0)
    {
        protocol_->decrementN();
        protocol_->startTimer(TIMERNAME);
    }
    return;
}

//------------------------------------------------------------------------

void pfTestMessage :: apply(
    pfState *state_,
    pfProtocol *protocol_)
{
    (dynamic_cast<pfTestState *> (state_))->
        testMessageAct(this, (pfTestProtocol *) protocol_);
    return;
}

//------------------------------------------------------------------------

pfTestTimeout :: pfTestTimeout(void)
    : pfTimerMessenger()
{
    return;
}

pfTestTimeout :: pfTestTimeout(const pfTestTimeout &)
    : pfTimerMessenger()
{
    return;
}

pfTestTimeout :: ~pfTestTimeout(void)
{
    return;
}

pfTimerMessenger *pfTestTimeout :: clone(void) const
{
    pfTimerMessenger *messenger = new pfTestTimeout(*this);
    return messenger;
}

void pfTestTimeout :: apply(
    pfState *state_,
    pfProtocol *protocol_)
{
    (dynamic_cast<pfTestState *> (state_))->
        testTimeoutAct((pfTestProtocol *) protocol_);
    return;
}

//------------------------------------------------------------------------

int main(int argc, char *argv[])
{
    int counter = NUMBER_OF_TEST_ROUNDS;
    int timeout = DEFAULT_TIMEOUT;

    if (argc > 3)
    {
        cerr << "Usage: " << argv[0]
             << " [rounds] [timeout in msec]" << endl;
        exit(0);
    }
    else if (argc > 2)
    {
        counter = atoi(argv[1]);
        timeout = atoi(argv[2]);
    }
    else if (argc > 1)
    {
        counter = atoi(argv[1]);
    }

    pfTestState *state = new pfTestState;
    pfTestProtocol *proto1 = new pfTestProtocol(counter, timeout);
    pfTestProtocol *proto2 = new pfTestProtocol(0, 0);

    proto1->changeState(state);
    proto2->changeState(state);

    pfConduit p1(proto1);
    pfConduit p2(proto2);

    p1.connectToB(p2);
    p1.connectToA(p2);
    p2.connectToB(p1);
    p2.connectToA(p1);

    proto1->startTimer(TIMERNAME);

    pfSystem::instance()->run();

    return 0;
}
