#include <stdlib.h>
#include <string.h>
#include "UCTTupleMgmt.h"
#include "UCTPacketMgmt.h"
#include "UCTGlobal.h"

/* "library" is an array of List classes, used to represent the Tuple space.
   each List class is a linked list of UCTuples.  This array is used to 
   segregate UCTuples according to their key sizes.  For instance, UCTuples
   with keysizes of 5 will be inserted into the linked list at library[5].
   This was done to speed up accesses to the UCTuple space.  Once the bucket
   is determined, a sequential search through the linked list is performed
   to locate UCTuple matches. */
List library[MAX_TUPLE_LENGTH];

Tuple::~Tuple() {
  if (key) delete [] key;
  if (data) delete [] data;
  if (info) delete [] info;
}

Tuple::Tuple() {
  key = NULL;
  data = NULL;
  info = NULL;
}

/* List() is a List constructor which sets up the dummy header node
   for the linked list and inits the iterator index to the head. */
List::List() {
  head = new node;
  head->next = NULL;
  index = head;
}

/* ~List() is a List destructor which deletes a list */
List::~List() {
  node * temp;

  while (head->next) {
    temp = head->next;
    head->next = temp->next;
    if (temp->next)
      temp->next->prev = head;

    delete temp;
  }
  delete head;
}

/* Insert is a member function for the List class which inserts a new
   tuple at the head of the linked list.  It will first look to see if an
   identical UCTuple exists in the respective library bucket.  If a
   duplicate exists (which should not happen), then this UCTuple will not
   be inserted into the UCTuple space. */
void List::Insert (Tuple * t) {
  node * n;

  /* Check to see if a duplicate tuple exists. */
  if (!FindTuple(t, RD)) {
    n = new node;
    n->tuple = t;
    n->next = head->next;
    n->prev = head;
    head->next = n;
    if (n->next)
      n->next->prev = n;
  }
}

/* Remove() is member function for the List class which will remove the
   UCTuple which is currently being iterated on (from the member 
   function Iterate). */
void List::Remove () {
  node * temp = index;

  index->prev->next = index->next;
  if (index->next)
    index->next->prev = index->prev;

  index = head;

  delete temp;
}

/* Iterate() is a member function for the List class which will iterate
   through the List linked list and return a UCTuple pointer at each
   iteration.  It will return NULL if there are no more nodes to
   iterate. */
Tuple * List::Iterate() {
  if (index->next) {
    index = index->next;
    return index->tuple;
  }
  else return NULL;
}

/* Reset() is a member function for the List class which will reset
   the iterator to the head of the linked list. */
void List::Reset() {
  index = head;
}

/* FindTuple() is a member function for the List class which will
   search through the library and attempt to find a match on an
   incoming UCTuple.  If no match exists, it will return NULL. */
Tuple* List::FindTuple(Tuple * tester, int op) {
  int match;	/* Var to flag when a match has been found. */
  Tuple * testee;  /* Iterated UCTuple */

  Reset();	//Reset the iterator for this List.
  while (testee = Iterate()) {
    match = 1;

    /* First, test to see if the keys all match.  This is done first since it is
       the easiest (fastest) way to immediately remove a UCTuple from consideration. */
    for (int i=1; i < testee->keysize; i++) {

      /* Test the "type" field of the UCTuple info structure to see if types match */
      if ((tester->info[i][0] != testee->info[i][0]) && (tester->info[i][0] != PTR)) {
        match = 0;
        break;
      }
    }

    /* Loop to next iteration if there wasn't a match. */
    if (!match) continue;

    /* If we get here, the types for all the keys match, now compare
       sizes of elements, and then compare the data. Again, sizes are
       compared first so that we can remove a UCTuple from consideration
       before a possibly expensive compare. */
    for (int j=1; j < testee->keysize; j++) {

      /* Only compare if the incoming UCTuple element type is non-pointer */
      if (tester->info[j][0] != PTR) {
	/* Compare the size field of the UCTuple info structure. */
        if (tester->info[j][1] != testee->info[j][1]) {
          match = 0;
          break;
        }
        else {
          /* Now, compare the data in the field */
          match = !memcmp(tester->data + tester->info[j][2],
                         testee->data + testee->info[j][2],
                         tester->info[j][1]);
          if (!match) break;
        }
      }
    }

    /* If match == 1 here, we must have found a match, so return it.  Also,
       delete the matching UCTuple from the library if this is a in() op. */
    if (match) {
      if (op == IN)  Remove();
      return testee;
    }
  }

  /* No match found, so return NULL */
  return NULL;
}


/* BuildTuple() is called by the manager to decode incoming request packets
   from workers. It decodes the data packet and builds a corresponding
   UCTuple, which is returned. */

Tuple * BuildTuple(char * data, int op, int datasize) {
  int offset,  //used to track offset info for Tuple->info data structure
      lastint, //tracks the last int for array sizing info.
      type;    //stores type info
  Tuple * NewTuple = new Tuple;  //new UCTuple to build

  /* Fill op, datasize, keysize, key string, and data fields */
  NewTuple->op = op;
  NewTuple->datasize = datasize;
  
  /* Just copy the incoming data buffer pointer to the data field */
  NewTuple->data = data;

  if (op != EXEC) {
    NewTuple->keysize = strlen(data);
    NewTuple->key = new char[NewTuple->keysize + 1];
    strcpy(NewTuple->key, data);
  
    /* Set up the info structure for this UCTuple. */
    NewTuple->info = new int[NewTuple->keysize][3];

    /* init offset var to keysize + NULL char */
    offset = NewTuple->keysize + 1;

    /* Build the info structure for this UCTuple. By looping through each
       key and recording the data for that key. */
    for (int i=1; i < NewTuple->keysize; i++) {
      type = NewTuple->key[i];		//set the type
      NewTuple->info[i][2] = offset;	//set the offset into the data buffer

      /* info[i][0] is the type field
         info[i][1] is the size field
         info[i][2] is the offset field */
      switch(type) {
      case 'i':
        NewTuple->info[i][0] = INT;
        NewTuple->info[i][1] = intsize;
        memcpy(&lastint, data + offset, intsize);
        offset += intsize;
        break;
      case 'f':
        NewTuple->info[i][0] = FLOAT;
        NewTuple->info[i][1] = floatsize;
        offset += floatsize;
        break;
      case 's':
        NewTuple->info[i][0] = STRING;
        NewTuple->info[i][1] = strlen(data + offset) + 1;
        offset += NewTuple->info[i][1];;
        break;
      case 'I':
        NewTuple->info[i][0] = INTA;
        NewTuple->info[i][1] = (intsize * lastint);
        offset += NewTuple->info[i][1];
        break;
      case 'F':
        NewTuple->info[i][0] = FLOATA;
        NewTuple->info[i][1] = (floatsize * lastint);
        offset += NewTuple->info[i][1];
        break;
      case 'p':
        if (op == OUT) 
          Error("BuildTuple failed: Trying to send a pointer in an OUT call\n");
        NewTuple->info[i][0] = PTR;
        NewTuple->info[i][1] = addrsize;
        offset += addrsize;
        break;
      }
    }
  }
  return NewTuple;
}

/* UpdatePendingList() is used by the manager to manage blocked in()
   and rd() requests.  If a in() or rd() requests cannot be filled
   because a matching UCTuple could not be found, that workers index
   into the PendingList array will point to the UCTuple that the worker
   originally sent.  After all the UCTuples have been considered in the
   main manager loop, this list will be traversed to see if any of the
   requests can now be satisfied.  This function performs this check. */

void UpdatePendingList(Tuple ** list, int * clients) {
  Tuple * OutTuple;

  for (int i=0; i < UCTNWorkNodes; i++) {

    /* If the ith workers index into this list is not NULL, they have a
       pending in() or rd() request.  See if it can now be resolved. */
    if (list[i]) {
      OutTuple = library[list[i]->keysize].FindTuple(list[i], list[i]->op);

      /* If OutTuple is not NULL, a match has been found. */
      if (OutTuple) {
        EncodePacketSrvr(clients[i], list[i], OutTuple);
        list[i] = NULL;
      }
    }
  }
}

/* TestForEndMsg() is called by the manager following a receipt of
   an out() buffer from a worker.  If the UCTuple received a
   "UCTEnd" message, then we return 1 to indicate to the manager that
   they should decrement their active worker count. */
int TestForEndMsg(Tuple * InTuple) {
  if ((InTuple->keysize == 2) && (InTuple->info[1][0] == STRING))
    return !strcmp("UCTEnd", InTuple->data + InTuple->info[1][2]);
  return 0;
}

