// pgserver

#include "pgserver.h"

#include "declarations.h"

#include "groupcomm.h"

#include "hashtypes.h"
#include "tools.h"
// system imports
#include <sys/utsname.h>

/* Log Management */
#define FATAL_PGSERVER
#define ERROR_PGSERVER
//#define INFO_PGSERVER
//#define WARN_PGSERVER
//#define DEBUG_PGSERVER
//#define DEBUG_PGSERVER_RECV
//#define DEBUG_PGSERVER_ARRAY
//#define DEBUG_PGSERVER_CLOSE

int getFreeThread() {
  int i;

  for (i = 0; i < MAXCLIENTS; i++) {
#ifdef DEBUG_PGSERVER_ARRAY
    printf("FreeThread Iterator %d, SizeArray %d\n", i, sizeof(freeThreads) / sizeof(int));
    if (i >= (sizeof(freeThreads) / sizeof(int))) {
      printf("Array Index Outbounds (FATAL ERROR) -> File: %s Line: %d\n", __FILE__, __LINE__);
      exit(0);
    }
#endif
    if (freeThreads[i]) {
      freeThreads[i] = 0;
      return i;
    } // if
  }
  return -1; // no free Thread has been found
}


// Free the Thread thId of pool of Threads
void freeThread(int thId, int options) {

  if (freeThreads[thId]) {
#ifdef DEBUG
    printf("(pgserver) Thread%d that is already free\n", thId);
#endif
    //exit(1);
  }

  freeThreads[thId] = 1;
  if (options == 1){
#ifdef DEBUG_PGSERVER_CLOSE
     printf("(pgserver) Close Socket <%d> File: %s Line: %d\n", sockConnectionDriver[thId], __FILE__, __LINE__);
#endif
     close(sockConnectionDriver[thId]);
  }

#ifdef DEBUG_PGSERVER
  printf("Close connection of thread%d\n", thId);
#endif
  // Free socket of connection
}

void initFreeThreads() {
  int i;

  // initially of Threads are free
  for (i = 0; i < MAXCLIENTS; i++) {
#ifdef DEBUG_PGSERVER_ARRAY
    printf("Iterator %d, SizeArray %d\n", i, sizeof(freeThreads) / sizeof(int));
    if (i >= (sizeof(freeThreads) / sizeof(int))) {
      printf("Array Index Outbounds (FATAL ERROR) -> File: %s Line: %d\n", __FILE__, __LINE__);
      exit(-1);
    }
#endif
    freeThreads[i] = 1;

#ifdef DEBUG_PGSERVER_ARRAY
    printf("Iterator %d, SizeArray %d\n", i, sizeof(threadIdConnection) / sizeof(int));
    if (i >= (sizeof(threadIdConnection) / sizeof(int))) {
      printf("Array Index Outbounds (FATAL ERROR) -> File: %s Line: %d\n", __FILE__, __LINE__);
      exit(-1);
    }
#endif
    threadIdConnection[i] = 0;
    createSemaphore(0, & semaThreads[i]);
  }
}

void connection_thread(int thId) {
  char query[MAX_TXT /*+ EXTRASIZE - RMEXTRASIZE */];
  hot_uint32 transCC[MAX_CC];
  int end = 0;
  int rval = 0;
  int last_petition = 0;
  struct itemhPetition *foundPet = NULL;
  char *bufrecv = NULL;
  
#ifdef DEBUG_PGSERVER
  printf("Iniciado thread %d\n", thId);
#endif

  while (!end) {
#ifdef DEBUG_PGSERVER_ARRAY
    printf("Iterator %d, SizeArray %d\n", thId, sizeof(semaThreads) / sizeof(semaphore));
    if (thId >= (sizeof(semaThreads) / sizeof(semaphore))) {
      printf("Array Index Outbounds (FATAL ERROR) -> File: %s Line: %d\n", __FILE__, __LINE__);
      exit(0);
    }
#endif

    decSemaphore(& semaThreads[thId]);
    /* End Critical section. Unblock Thread id = thId */

#ifdef DEBUG_PGSERVER
    printf("(pgserver) Thread %d Start Work ...\n", thId);
#endif

#ifdef DEBUG_PGSERVER_ARRAY
    printf("Iterator %d, SizeArray %d\n", thId, sizeof(sockConnectionDriver) / sizeof(int));
    if ((thId >= (sizeof(sockConnectionDriver) / sizeof(int)))
       || (thId >= (sizeof(threadIdConnection) / sizeof(int)))
       || (thId >= (sizeof(threadInformation) / sizeof(int)))) {
      printf("Array Index Outbounds (FATAL ERROR) -> File: %s Line: %d\n", __FILE__, __LINE__);
      exit(0);
    }
#endif

    last_petition = 0;
    //TODO Que ocurre si se cae el driver se sigue acordando de last_petition o reinicializa
    if ((rval = pgconnectionStart(sockConnectionDriver[thId], thId, &threadIdConnection[thId], threadInformation[thId], &last_petition)) < 0) {
      if (rval == -1) {
        perror("(PGSERVER) StartUpPacketError -> ");
      } else if (rval == -2) {
        perror("(PGSERVER) Error Received Datas in PGConnection -> ");
      }
    }

    // Send stored procedure disconnection to Middleware
    if (rval != 0) {
      bzero(query, strlen(query));
      if (rval < 0){
      	int petition = (threadIdConnection[thId] * MAX_PETITION_PER_CLIENT) + 0 /*last_petition*/; 
        sprintf(query, "%2d ", EARLY_DISCON);
        decSemaphore(& semaConnections[0/*TODO thId*/]);
        if (((foundPet = (struct itemhPetition *) get_hashtable(&hPetition, & petition)) != NULL) && (foundPet->last.id_petition == last_petition)){
        	foundPet->last.is_change_nodelocal_new = 0;
        	foundPet->last.is_change_nodelocal_old = 1;
        	/* TODO recibir informacion con temporizador x, 
        	 * para evitar dejar en el sockets informacion
        	 * de una posible contestacion al thread,
        	 * al no haber advertido con tiempo que no debia responder. 
        	 */
        	 if ((recvWithSizeMem(sockResTh[thId], & bufrecv, 1)) < 0) {
                printf("(PGSERVER) NOT FOUND TRASH\n");
             }else{
             	printf("(PGSERVER) FOUND TRASH\n");
        	 }
        	 Free(bufrecv);
        } else {
        	printf("ERROR Failover Last Petition Not Found.\n");
        }
        incSemaphore(& semaConnections[0/*TODO thId*/]);
      } else if (rval > 0){
        sprintf(query, "%2d ", DISCON_RESPONSE);
      }
      transCC[0] = 0;
      //printf("(pgserver) threadIdConnection <%d> id_petition <%d> ...\n", threadIdConnection[thId], last_petition + 1);
      SendQuery(query, 1, transCC, thId, 2, threadIdConnection[thId], last_petition + 1, threadInformation[thId]);

#ifdef DEBUG_PGSERVER
      printf("(Thread%d) Send DISCON query=<%s>\n", thId, query);
#endif
    } else {
#ifdef DEBUG_PGSERVER    	
      printf("(PGSERVER) Driver Close Socket -> Nothing TO DO\n");
#endif
      //close(sockConnectionDriver[thId]); //TODO no se si es necesario o se ha cerrado ya, si se hace aqui puede ocasionar problemas de conurrencia
    }
#ifdef DEBUG_PGSERVER
    printf("(pgserver) Thread %d Stop Working ...\n", thId);
#endif
    //freeThread (thId);
    //close(sockConnectionDriver[thId]); //JSalas No descomentar si no se puede cerrar el sockets antes que el driver recoja los datos
  }
  //close (sockThread[thId]);
//#ifdef DEBUG_PGSERVER
    printf("(pgserver) Thread %d finished ...\n", thId);
//#endif
}

int StartServer() {

  int i = 0;
  int end = 0;
  int socketJDBC = 0;
  char buf[MAXDBNAME /*+ EXTRASIZE - RMEXTRASIZE*/];
  hot_uint32 idConnection = 0;
  char databaseName[MAXDBNAME /*+ EXTRASIZE - RMEXTRASIZE*/];
  char server[MAXSERVERPATH /*+ EXTRASIZE - RMEXTRASIZE*/];
  char * nodename = NULL;
  static struct utsname node_name;
  int sock_server;

#ifdef WARN_PGCONNECTION
  printf("(pgserver) Starting pgserver in port %d\n", PORTPGSERVER);
#endif

  /* create socket */
  sock_server = sockcreateINET(PORTPGSERVER);
  if (sock_server == -1) {
    perror("(pgserver) sockcreate");
    return -1;
  }

  initFreeThreads();

  for (i = 0; i < MAXCLIENTS; i++) {
  	//last_update_petition[i] = 0;//PNF
    createSemaphore(1, & semaConnections[i]);
  }

#ifdef DEBUG_PGSERVER
  printf("(pgserver) Init threads of connection \n");
#endif

  createSemaphore(1, & mutexSequence);

  // Waiting the socket server is created and accepting connections
  sleepThread(10000);

  uname(& node_name);
  nodename = strtok(node_name.nodename, ".");
  strcpy(server, getcwd(NULL, MAXSERVERPATH - (1 + strlen(nodename) + 1 + EOF_SIZE)));
  strcat(server, "/");
  strcat(server, nodename);
  strcat(server, "b");

#ifdef DEBUG_PGSERVER
  printf("(pgserver) Connect Threads to server %s\n", server);
#endif

  for (i = 0; i < MAXCLIENTS; i++) {
#ifdef DEBUG_PGSERVER_ARRAY
    printf("Iterator %d, SizeArray %d\n", i, sizeof(sockResTh) / sizeof(int));
    if (i >= (sizeof(sockResTh) / sizeof(int))) {
      printf("Array Index Outbounds (FATAL ERROR) -> File: %s Line: %d\n", __FILE__, __LINE__);
      exit(0);
    }
#endif

    while ((sockResTh[i] = sockconnect(server)) == -1) {
      sleepThread(100000);
    }

    /* JSalas No es posible if (sockResTh[i] == -1) { perror("Error in QM sockconnect\n"); exit(1); } */

#ifdef DEBUG_PGSERVER
    printf("(pgserver) Thread(%d) connected to socket %d\n", i, sockResTh[i]);
#endif
    createThread((void *) connection_thread, (int *) i, NULL);
  }

  /**  */
  /* Init process of Fill table hash with datas of */
  /* conflict_classes */
  /**  */
  
  //printf("INIT\n");
  sendMsgInit();
  //printf("INIT OK\n");
  
  // Infinite loop
  while (!end) {
    if ((socketJDBC = sockaccept(sock_server)) == -1) {
#ifdef ERROR_PGSERVER
      printf("(pgserver) ERROR accepting connection from Driver JDBC <%d>\n", sock_server);
      perror("(pgserver) Error");
#endif

#ifdef DEBUG_PGSERVER_CLOSE
      printf("(pgserver) Close Socket <%d> File: %s Line: %d\n", sock_server, __FILE__, __LINE__);
#endif
      close(sock_server);
      return -1;
    }

#ifdef DEBUG_PGSERVER
    printf("(pgserver) Connection request received from Driver JDBC <%d>\n", sock_server);
#endif

    // receive the id connection
    bzero(buf, sizeof(buf));
    if ((i = recv(socketJDBC, buf, 2, 0)) < 0) {
      return -1;
    }
    convertToInt(buf, 2, (int *) & idConnection, 0);

#ifdef DEBUG_PGSERVER_RECV
    printf("(PGSERVER) 2 socketDriver = %d\n", socketJDBC);
    printf("(PGSERVER) (RECV 4) Bytes <%d> Message <%d> (IDCONNECTION)\n", i, idConnection);
#endif

    // receive the bdname
    bzero(buf, sizeof(buf));
    if ((i = recv(socketJDBC, buf, sizeof(buf), 0)) < 0) {
      return -1;
    }
    bzero(databaseName, sizeof(databaseName));
    strcpy(databaseName, buf);

#ifdef DEBUG_PGSERVER_RECV
    printf("(PGSERVER) (RECV 5) Bytes <%d> Message <%s> (DBNAME)\n", i, databaseName);
    printf("(PGSERVER) databaseName = %s\n", databaseName);
    printf("(PGSERVER) socketDriver = %d\n", socketJDBC);
    printf("(PGSERVER) idConn = %d\n", idConnection);
#endif

    sendMsgConnection(databaseName, socketJDBC, idConnection);
    //printf("SEND MSG CONNECTION");
  }

#ifdef DEBUG_PGSERVER_CLOSE
  printf("(pgserver) Close Socket <%d> File: %s Line: %d\n", sock_server, __FILE__, __LINE__);
#endif

  close(sock_server);
  return 0;
}

/* int main(int argc, char *argv[]) { StartServer();  } */
