//Editor-Info: -*- C++ -*-
//
//Subject: Scheduler Framework
//
//File: demo.cpp
//
//State: $State: Exp $
//
//Version: $Revision: 1.4 $
//
//Date: $Date: 1998/10/07 09:49:04 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Authors:
//      Juhana Räsänen
//
//Description:
//	Demonstration "application" of the Scheduler Framework.
//
//Copyright:
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//
//Licence:
//
//
//History:
//

#include <typeinfo>
#include <iostream.h>
#include <string.h>	// strerror
#include <errno.h>	// errno
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include "sf/fifoscheduler.h"
#include "sf/schedulerhandle.h"
#include "sf/task.h"
#include "sf/otime.h"
#include "pf/debug.h"

class timedTask;
class runnableTask;

class mainTask : public sfTask
{
    public:
        mainTask(sfScheduler *scheduler_, char *hostname_,
                 int timeout_, int counter_);
        virtual void readCallback(void);
        virtual void writeCallback(void);
        void waitForMessage(void);
        void sendMessage(void);

    private:
        static const int MAGIC_PORT = 9669;
        int _fd;
        timedTask *_timed;
        int _counter;
};

class timedTask : public sfTask
{
    public:
        timedTask(sfScheduler *scheduler_, mainTask *parent_,
                  int id_, int timeout_ = 1000);
        virtual void timeoutCallback(void);
        void start(int counter_);

    private:
        int _id;
        int _timeout;
        int _counter;
        mainTask *_parent;
        runnableTask *_runnable;
};

class runnableTask : public sfTask
{
    public:
        runnableTask(sfScheduler *scheduler_, int id_);
        virtual void runCallback(void);
        void start(void);

    private:
        int _id;
        int _counter;
};


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


mainTask :: mainTask(sfScheduler *scheduler_, char *hostname_,
                     int timeout_, int counter_)
    : sfTask(),
      _fd(0),
      _timed(0),
      _counter(counter_)
{
    if (scheduler_ != 0)
    {
        sfSchedulerHandle *handle = scheduler_->makeHandle(this);
        setHandle(handle);
    }

    _timed = new timedTask(scheduler_, this, 1, timeout_);

    struct sockaddr_in addr;
    _fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (_fd < 0)
    {
        debugString("mainTask: Error in constructor : socket",
                    strerror(errno));
        exit(0);
    }
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = MAGIC_PORT;
    if (::bind(_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
    {
        debugString("mainTask: Error in constructor : bind",
                    strerror(errno));
        ::close(_fd);
        exit(0);
    }
    memset(&addr, 0, sizeof(addr));
    struct hostent *host = gethostbyname(hostname_);
    if (host == 0)
    {
        debugString("mainTask: Error in constructor : gethostbyname",
                    strerror(errno));
        ::close(_fd);
        exit(0);
    }
    memcpy(&(addr.sin_addr), host->h_addr_list[0], sizeof(addr.sin_addr));
    addr.sin_family = AF_INET;
    addr.sin_port = MAGIC_PORT;
    if (::connect(_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
    {
        debugString("mainTask: Error in constructor : connect",
                    strerror(errno));
        ::close(_fd);
        exit(0);
    }
    return;
}

void mainTask :: readCallback(void)
{
    char buffer[256];
    int nchars = ::read(_fd, buffer, 256);
    if (nchars > 0)
    {
        int count = atoi(buffer);
        _timed->start(count);
        debugPfUlong("mainTask: started tasks, iterations requested", count);
    }
    else
    {
        debugString("mainTask: Error reading socket : ??",
                    strerror(errno));
        exit(0);
    }
    return;
}

void mainTask :: writeCallback(void)
{
    char buffer[256];
    sprintf(buffer, "%d", _counter);
    int nchars = ::write(_fd, buffer, strlen(buffer)+1);
    if (nchars < 0)
    {
        debugString("mainTask: Error writing to socket : write",
                    strerror(errno));
        exit(0);
    }
    debugPfUlong("mainTask: iterations requested", _counter);
    waitForMessage();
    return;
}

void mainTask :: sendMessage(void)
{
    OTime timeout(0);
    requestWrite(_fd, timeout);
    return;
}

void mainTask :: waitForMessage(void)
{
    OTime timeout(0);
    requestRead(_fd, timeout);
    return;
}


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


timedTask :: timedTask(sfScheduler *scheduler_, mainTask *parent_,
                       int id_, int timeout_)
    : sfTask(),
      _id(id_),
      _timeout(timeout_),
      _counter(0),
      _parent(parent_),
      _runnable(0)
{
    if (scheduler_ != 0)
    {
        sfSchedulerHandle *handle = scheduler_->makeHandle(this);
        setHandle(handle);
    }
    _runnable = new runnableTask(scheduler_, id_);
    return;
}

void timedTask :: timeoutCallback(void)
{
    debugPfUlong("Timeout expired for timed task ", _id);
    _runnable->start();
    _counter--;
    if (_counter == 0)
    {
        _parent->sendMessage();
    }
    else
    {
        OTime timeout(0, _timeout);
        requestTimeout(timeout);
    }

    return;
}

void timedTask :: start(int counter_)
{
    _counter = counter_;
    OTime timeout(0, _timeout);
    requestTimeout(timeout);
    return;
}


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


runnableTask :: runnableTask(sfScheduler *scheduler_, int id_)
    : sfTask(),
      _id(id_),
      _counter(0)
{
    if (scheduler_ != 0)
    {
        sfSchedulerHandle *handle = scheduler_->makeHandle(this);
        setHandle(handle);
    }
    return;
}

void runnableTask :: runCallback(void)
{
    debugPfUlong("Runnable task", _id);
    debugPfUlong("running step", _counter);
    if (_counter > 0)
    {
        _counter--;
        requestCPU();
    }
    return;
}

void runnableTask :: start(void)
{
    if (_counter == 0)
    {
        requestCPU();
    }
    _counter += 10;
    return;
}


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


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

    debugOutputCout();
    if ((argc < 3) || (argc > 5))
    {
        cerr << "Usage: " << argv[0] << " s|w hostname [counter] [timeout]" << endl;
        exit(0);
    }
    else if (argc > 4)
    {
        counter = atoi(argv[3]);
        timeout = atoi(argv[4]);
    }
    else if (argc > 3)
    {
        counter = atoi(argv[3]);
    }

    sfFIFOScheduler *scheduler = new sfFIFOScheduler;
    mainTask *parent = new mainTask(scheduler, argv[2], timeout, counter);

    if (argv[1][0] == 's')
    {
        parent->sendMessage();
    }
    else
    {
        parent->waitForMessage();
    }

    scheduler->run();
}

