/* ----------------------------------------------------------------------------
fsm.c

a finite state machine within sics

M. Zolliker, Aug 2004
---------------------------------------------------------------------------- */

#include <stdlib.h>
#include <assert.h>
#include <time.h>
#include "fsm.h"

#define MAXSTACK 8

typedef struct {
  long pc;
  FsmFunc func;
} StackItem;

struct Fsm {
  void *obj;
  FsmHandler handler;
  long pc;
  FsmFunc func;
  time_t till;
  int pause;
  int sp;
  StackItem stack[MAXSTACK];
};


static Fsm *fsm = NULL;

void FsmWait(long delay) {
  assert(fsm);
  fsm->till = time(NULL) + delay;
}

int FsmTaskHandler(Fsm *this) {
  long line;
  
  if (this->pause) {
    this->handler(this->obj);
    return 1;
  }
  if (this->pc >= 0) { /* this->pc < 0 means stop current function */
    if (this->till != 0) {
      if (time(NULL) < this->till) return 1; /* wait */
      this->till = 0;
    }
    if (this->handler(this->obj) == 0) {
      return 1; /* wait for answer */
    }
    fsm = this;
    this->pc = this->func(this->pc, this->obj);
    fsm = NULL;
  }
  if (this->pc <= 0) {
    if (this->sp == 0) return 0;
    this->sp--;
    this->pc = this->stack[this->sp].pc;
    this->func = this->stack[this->sp].func;
  }
  return 1;
}
  
void FsmKill(void *data) {
  free(data);
}

Fsm *FsmStartTask(void *obj, FsmHandler handler, FsmFunc func) {
  Fsm *new;
  
  new=malloc(sizeof *new);
  new->obj = obj;
  new->pc = 0;
  new->func = func;
  new->handler = handler;
  new->sp = 0;
  new->pause = 0;
  new->till = 0;
  return new;
}

int FsmStop(Fsm *this, FsmFunc func) {
  int i;
  
  if (this == NULL) this = fsm;
  assert(this);
  for (i=0; i < this->sp; i++) {
    if (func == this->stack[i].func) {
      break;
    }
  }
  if (i == this->sp) {
    if (func != this->func) return 0;
  } else {
    this->sp = i;
  }
  this->pc = -1;
  return 1;
}

void FsmPause(Fsm *this, int pause) {
  this->pause = pause;
}

long FsmCall(long pc, FsmFunc func) {
  assert(fsm);
  assert(fsm->sp < MAXSTACK);
  fsm->stack[fsm->sp].pc = pc;
  fsm->stack[fsm->sp].func = fsm->func;
  fsm->sp++;
  fsm->func = func;
  fsm->pc = 0;
  return fsm->func(fsm->pc, fsm->obj);
}
