#include <stdlib.h>
#include <stdio.h>
#include "waitq.h"
#define type int
#define int_null -1
#include "array.h"

/*
  Resource watching

  Implements a mechanism for clients to watch a set of resources until
  any of them has a specified value.

  For example, for DIOs we want to be able to block waiting for a DIO 
  to be set to a certain value, or any of a group of DIOs to be a
  specific value for that DIO.
 */
typedef struct watch watch;
struct watch{
  int clientId;
  int value;
};
static watch **watches; // array of watches for each resource
static int *num_watches; // number of watches for each resource
static int resources; // total number of resources
static int *value;    // current value for each resource

static int *runq;
static int runq_len;

// for testing/debugging only
void dump_watch(int n) {
  int i,j;

  printf("wa[%d] = List[",n);
  for (i=0;i<resources;i++) {
    printf("List[%d,",value[i]);
    printf("List[");
    for (j=0;j<num_watches[i];j++) {
      printf("List[%d,%d]",watches[i][j].clientId,watches[i][j].value);
      if (j+1<num_watches[i]) printf(",");
    }
    printf("]]");
    if (i+1<resources) printf(",");
  }
  printf("];\n");
  printf("rq[%d] = List[",n);
  for (i=0;i<runq_len;i++) {
    printf("%d",runq[i]);
    if (i+1 < runq_len) printf(",");
  }
  printf("];\n");  
}

#define type watch
#define watch_null {-1,0}
#include "array.h"

void init_watch(int num_resources) {
  int i;

  if (watches) free(watches);
  watches = malloc(sizeof(watch *) * num_resources);
  num_watches = malloc(sizeof(int) * num_resources);
  value = malloc(sizeof(int) * num_resources);
  
  for (i=0;i<num_resources;i++) {
    watches[i] = 0;
    num_watches[i] = 0;
    value[i] = -1;
  }
  resources = num_resources;
}

/*
  We store the resource ids that the client wants to watch in one queue,
  and the values for each resource in another queue.
  returns true if succeeded, or false if request was denied
 */
int request_watch(int clientid,int id,int value) {
  int i;

  if (id < 0 || id >= resources) return 0;
  for (i=0;i<num_watches[id];i++) if (watches[id][i].clientId == clientid) {
      // client is already watching. has value changed?
      watches[id][i].value = value;
      return 1;
  }
  // append client to list of watchers for this id
  watchQueue(watches+id,num_watches+id,(watch){clientid,value});
  /*
  num_watches[id]++;
  watches[id] = realloc(watches[id],num_watches[i] * sizeof(watch));
  watches[id][num_watches[id]-1].clientId = clientid;
  watches[id][num_watches[id]-1].value = value;
  */
  return 1;
}

// release all change watches
int release_watch(int clientid) {
  int i,j,k,n=0;

  for (j=0;j<resources;j++) {
    for (i=0;i<num_watches[j];i++) if (watches[j][i].clientId == clientid) {
      watchDelete(watches+j,num_watches+j,i,1);
      n++;
      break;
    }
  }
  return n;
}

// the server must call this function whenever the value
// of a resource changes.  it must not call this function
// if the value has not changed
int watch_update_resource(int id,int val) {
  int i,j;

  if (id < 0 || id >= resources) return 0;
  value[id] = val;
  for (i=0;i<num_watches[id];i++) if (watches[id][i].value == val) {
    for (j=0;j<runq_len;j++) if (runq[j]==watches[id][i].clientId) {
      break;
    }
    if (j==runq_len) intQueue(&runq,&runq_len,watches[id][i].clientId);
  }
  return 1;
}

// get the next client that has a watch that changed since it
// last saw it.  we assume that the client is blocking waiting
// for the change, so it last saw it the last time this function
// return that it had a changed watch, or when the watch was
// created, whichever came later.
// there is no guarantee that the value the client wanted will
// be the same by the time the client sees it.
// returns -1 if no client runnable
int watch_get_next_client() {
  return intPop(&runq,&runq_len);
}
// what if a value changes to the value desired,
// but before the server process for the client has a chance to check
// the value, it changes again to something different?
// we could be stuck in a case where the client knows something changed
// to a value it wanted, but it cannot tell which value it was!
