#include "queue.h"
#include <stdio.h>
#include "declarations.h"

/* Log Management */
#define FATAL_QUEUE
#define ERROR_QUEUE
#define INFO_QUEUE
//#define WARN_QUEUE
//#define DEBUG_QUEUE

T_Queue_ptr Q_create() {
  T_Queue_ptr queue;

  queue = (T_Queue_ptr)malloc(sizeof(struct T_Queue));
  queue->first = queue->last = NULL;
  return queue;
};


void Q_printR(T_Queue_ptr queue) {
  Q_item_ptr current;

#ifdef INFO_QUEUE
  printf("printR: ");
#endif

  current = queue->last;
  while (current != NULL) {
#ifdef INFO_QUEUE
    printf("%d,", current->item->transaction.tid);
#endif

    current = current->prev;
  }

#ifdef INFO_QUEUE
  printf("\n");
#endif
};

void printT_TransactionCompact(T_Transaction *trans){
  int i = 0;

  printf("T_Transaction trans:\n");
  printf("local <%d>;", trans->local);
  printf("executedByAll <%d>;", trans->executedByAll);
  printf("localNode <%d>;", trans->localNode);
  printf("finished <%d>;", trans->finished);
  printf("committable <%d>;", trans->committable);
  printf("executing <%d>;", trans->executing);
  printf("aborting <%d>;", trans->aborting);
  printf("committing <%d>;", trans->committing);
  printf("commitreceived <%d>;", trans->commitreceived);
  printf("writeset_length <%ld>;", trans->writeset_length);
  printf("qmId <%d>;", trans->qmId);
  printf("mclientId <%d>;", trans->mclientId);
  printf("idConnection <%d>;", trans->idConnection);
  printf("idPetition <%d>;", trans->idPetition);
  printf("recordsUpdates <%d>;", trans->recordsUpdates);
  printf("error <%d>;", trans->error);

  printf("\nT_Trans transaction:\n");
  printf("noCC <%d>;", trans->transaction.noCC);
  printf("hot_uint32 *transCC;\n");
  for (i = 0; i < trans->transaction.noCC; i++) {
    printf("transCC[%d] = %d;", i, trans->transaction.transCC[i]);
  }
  printf("sqlcode <%s>;", trans->transaction.sqlcode);
  printf("tid <%d>;", trans->transaction.tid);
  printf("endpoint;");
  printf("isQuery <%d>;", trans->transaction.isQuery);
  printf("typeSQL <%c>;\n", trans->transaction.typeSQL);
}

void Q_printFirstAndNumElements(T_Queue_ptr queue) {
  Q_item_ptr current;
  T_Transaction *t = NULL;//TODO Quitar
  int num_elements = 0;

#ifdef INFO_QUEUE
  //printf("print: ");
#endif

  current = queue->first;
#ifdef INFO_QUEUE
  if (current != NULL){
    t = current->item;//TODO Quitar
  }
#endif

  while (current != NULL) {
/*
#ifdef INFO_QUEUE
    printf("%d(%d),", current->item->transaction.tid, current->item->executing);
#endif
*/
    num_elements++;
    current = current->next;
  }

#ifdef INFO_QUEUE
  //printf("\n");
#endif

#ifdef INFO_QUEUE
  printf("Number of elements <%d>\n", num_elements);
  if (t != NULL){
    printf("First Transaction\n");
    printT_TransactionCompact(t);//TODO Quitar
  }
  printf("\n");
#endif
};

//TODO function is duplicated, resolve this
void printT_Transaction(T_Transaction *trans){
  int i = 0;

  printf("\nT_Transaction trans:\n");
  printf("bool local <%d>;\n", trans->local);
  printf("bool executedByAll <%d>;\n", trans->executedByAll);
  printf("bool localNode <%d>;\n", trans->localNode);
  printf("bool finished <%d>;\n", trans->finished);
  printf("bool committable <%d>;\n", trans->committable);
  printf("bool executing <%d>;\n", trans->executing);
  printf("bool aborting <%d>;\n", trans->aborting);
  printf("bool committing <%d>;\n", trans->committing);
  printf("bool commitreceived <%d>;\n", trans->commitreceived);
  printf("long writeset_length <%ld>;\n", trans->writeset_length);
  printf("hot_uint32 qmId <%d>;\n", trans->qmId);
  printf("hot_uint32 mclientId <%d>;\n", trans->mclientId);
  printf("hot_uint32 idConnection <%d>;\n", trans->idConnection);
  printf("hot_uint32 idPetition <%d>;\n", trans->idPetition);
  printf("int recordsUpdates <%d>;\n", trans->recordsUpdates);
  printf("int error <%d>;\n", trans->error);

  printf("\nT_Trans transaction:\n");
  printf("hot_uint32 noCC <%d>;\n", trans->transaction.noCC);
  printf("hot_uint32 *transCC;\n");
  for (i = 0; i < trans->transaction.noCC; i++) {
    printf("transCC[%d] = %d;\n", i, trans->transaction.transCC[i]);
  }
  printf("char sqlcode <%s>;\n", trans->transaction.sqlcode);
  printf("T_Tid tid <%d>;\n", trans->transaction.tid);
  printf("hot_endpt_t endpoint;\n");
  printf("hot_uint32 isQuery <%d>;\n", trans->transaction.isQuery);
  printf("char typeSQL <%c>;\n", trans->transaction.typeSQL);
}

void Q_print(T_Queue_ptr queue) {
  Q_item_ptr current;
  T_Transaction *t = NULL;//TODO Quitar

#ifdef INFO_QUEUE
  printf("print: ");
#endif

  current = queue->first;
#ifdef INFO_QUEUE
  if (current != NULL){
    t = current->item;//TODO Quitar
  }
#endif

  while (current != NULL) {
#ifdef INFO_QUEUE
    printf("%d(%d),", current->item->transaction.tid, current->item->executing);
#endif

    current = current->next;
  }

#ifdef INFO_QUEUE
  printf("\n");
#endif

#ifdef INFO_QUEUE
  printf("FIRST TRANSACTION\n");
  if (t != NULL){
    printT_Transaction(t);//TODO Quitar
  }
#endif
};

void Q_insert(T_Queue_ptr queue, T_Transaction * trans) {
  Q_item_ptr item;

  item = (Q_item_ptr)malloc(sizeof(struct Q_item));
  item->item = trans;
  item->prev = item->next = NULL;
  if (queue->first == NULL) {
    queue->first = item;
#ifdef DEBUG_QUEUE
    printf("insert in empty queue\n");
#endif
  } else {
    queue->last->next = item;
    item->prev = queue->last;
#ifdef DEBUG_QUEUE
    printf("insert in non empty queue\n");
#endif
  }
  queue->last = item;
};

void Q_insertFirst(T_Queue_ptr queue, T_Transaction * trans) {
  Q_item_ptr item;

  item = (Q_item_ptr)malloc(sizeof(struct Q_item));
  item->item = trans;
  item->prev = NULL;
  if (queue->last == NULL) {
    queue->last = item;
    item->next = NULL;
#ifdef DEBUG_QUEUE
    printf("insert at the begining of  empty queue\n");
#endif
  } else {
    queue->first->prev = item;
    item->next = queue->first;
#ifdef DEBUG_QUEUE
    printf("insert at the beginning of non empty queue\n");
#endif
  }
  queue->first = item;
};

T_Transaction * Q_getFirst(T_Queue_ptr queue) {
  if (queue->first == NULL) {
#ifdef ERROR_QUEUE
    printf("ERROR getFirst on empty queue \n");
#endif
  }

  return queue->first->item;
};

void Q_remove(T_Queue_ptr queue) {
  if (queue->first == NULL){
#ifdef ERROR_QUEUE
    printf("ERROR remove from an empty queue\n");
#endif
    return;
  }

  if (queue->first->next == NULL) {
    free(queue->first);
    queue->first = NULL;
    queue->last = NULL;
#ifdef DEBUG_QUEUE
    printf("removing the only element of a queue\n");
#endif
  } else {
    queue->first = queue->first->next;
    free(queue->first->prev);
    queue->first->prev = NULL;
#ifdef DEBUG_QUEUE
    printf("removing an element of a queue\n");
#endif
  }
};

/*T_Transaction* Q_extractByCC(T_Queue_ptr queue, C_set  confClass) {

C_item* current; C_mess* res;

if (first == NULL) printf("ERROR extract from an empty queue\n"); current = first;
while ((current != NULL) && !confClass.issubset(current -> item -> myTrans -> myConflictClass)){ current = current -> next; }
if (current == NULL) printf("ERROR extract a non-existing CC\n"); // now current points to the searched item
// unlink it from the list if (current -> prev != NULL) current -> prev -> next = current -> next; else
first = current -> next; if (current -> next != NULL) current -> next -> prev = current -> prev; else last = current -> prev;
res = current -> item; free(current); return res; }; */

bool Q_isEmpty(T_Queue_ptr queue) {
#ifdef DEBUG_QUEUE
  if (queue->first != NULL) {
    printf(" the queue is not empty %d \n", queue->first->item->transaction.tid);
  }
#endif
  return queue->first == NULL;
};

void Q_reschedule(T_Queue_ptr queue, T_Tid tid) {
  Q_item_ptr current;
  Q_item_ptr item;
  T_Transaction * trans;

  // remove the transaction to be rescheduled
  if (queue->first == NULL){
#ifdef ERROR_QUEUE
    printf("ERROR reschedule from an empty queue \n");
#endif
  }

#ifdef DEBUG_QUEUE
  Q_print(queue);
  Q_printR(queue);
#endif
  trans = Q_getAndRemove(queue, tid);
  // traverse the queue in reverse order until a pending transaction is found
  // or the head of the queue is reached
  current = queue->last;
  while ((current != NULL) && !(current->item->committable)) {
    current = current->prev;
  } // while
  // if the head of list has been reached insert the transaction at the beginning
  if (current == NULL) {
    Q_insertFirst(queue, trans);
#ifdef DEBUG_QUEUE
    printf("rescheduling at the beginning of the queue\n");
#endif
  } else {
    // current is committable, insert after it
    item = (Q_item_ptr)malloc(sizeof(struct Q_item));
    item->item = trans;
    item->prev = current;
    item->next = current->next;
    if (current->next == NULL) {
      queue->last = item;
#ifdef DEBUG_QUEUE
      printf("rescheduling at the end of the queue \n");
#endif
    } else {
      current->next->prev = item;
#ifdef DEBUG_QUEUE
      printf("rescheduling somewhere inthe queue\n");
#endif
    } // if current->next == NULL
    current->next = item;
  } // if current == NULL
#ifdef DEBUG_QUEUE
  Q_print(queue);
  Q_printR(queue);
#endif
};

void Q_insertQuery(T_Queue_ptr queue, T_Transaction * trans) {
  Q_item_ptr current;
  Q_item_ptr item;

#ifdef DEBUG_QUEUE
  Q_print(queue);
  Q_printR(queue);
#endif

  // traverse the queue in reverse order until a pending transaction is found
  // or the head of the queue is reached
  current = queue->last;
  while ((current != NULL) && !(current->item->committable) && !(current->item->executing)) {
    current = current->prev;
  } // while
  // if the head of list has been reached insert the transaction at the beginning
  if (current == NULL) {
#ifdef DEBUG_QUEUE
    printf("rescheduling at the beginning of the queue\n");
#endif
    Q_insertFirst(queue, trans);
  } else {
    // current is committable or executing, insert after it
#ifdef DEBUG_QUEUE
    printf("rescheduling at the end of the queue \n");
#endif
    item = (Q_item_ptr)malloc(sizeof(struct Q_item));
    item->item = trans;
    item->prev = current;
    item->next = current->next;
    if (current->next == NULL) {
      queue->last = item;
    } else {
#ifdef DEBUG_QUEUE
      printf("rescheduling somewhere in the queue\n");
#endif
      current->next->prev = item;
    } // if current->next == NULL
    current->next = item;
  } // if current == NULL
#ifdef DEBUG_QUEUE
  Q_print(queue);
  Q_printR(queue);
#endif
};

T_Transaction * Q_getAndRemove(T_Queue_ptr queue, T_Tid tid) {
  Q_item_ptr current;
  T_Transaction * res;

  current = queue->first;
  if (current == NULL){
    #ifdef ERROR_QUEUE
      printf("ERROR getAndRemove from an empty queue\n");
    #endif
  }

  while ((current != NULL) && !isEqualId(current->item->transaction.tid, tid))
    current = current->next;

  if (current == NULL){
#ifdef ERROR_QUEUE
    printf("Error: Transaction not found in queue\n");
#endif
  }

#ifdef DEBUG_QUEUE
  printf("found %d\n", current->item->transaction.tid);
#endif
  // now current points to the searched item
  // unlink it from the list
  if (current->prev != NULL)
    current->prev->next = current->next; else
    queue->first = current->next;
  if (current->next != NULL)
    current->next->prev = current->prev; else
    queue->last = current->prev;
  res = current->item;
#ifdef DEBUG_QUEUE
  printf("getAndRemove found the transaction in the queue\n");
#endif
  free(current);
  return res;
};



