#include "socku.h"
#include "utilities.h"
#include "tools.h"

/* Log Management */
#define FATAL_SOCKU
#define ERROR_SOCKU
//#define INFO_SOCKU
//#define WARN_SOCKU
//#define DEBUG_SOCKU
//#define DEBUG_SOCKU_ARRAY
#define DEBUG_SOCKU_QMMESSAGE

/* sockcreate -- Create socket from which to read. return socket descriptor if ok, -1 if not ok. */
int sockcreate(char path[]) {
  int sockfd;
  struct sockaddr_un serv_addr;

#ifdef DEBUG_SOCKU
  printf("Trying to create socket at path %s...\n", path);
#endif

  remove(path);
  sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
  if (sockfd == -1)
    return -1;

  /* bind our local address so client can connect to us */
  bzero((char *) & serv_addr, sizeof(serv_addr));
  serv_addr.sun_family = AF_UNIX;
  strncpy(serv_addr.sun_path, path, sizeof(serv_addr.sun_path));
  if (bind(sockfd, (struct sockaddr *) & serv_addr, sizeof(serv_addr)) < 0)
    return -1;

#ifdef DEBUG_SOCKU
  printf("Socket created!!!\n");
#endif

  /* listen on the socket for connection */
  if (listen(sockfd, 5) == -1)
    return -1;

#ifdef DEBUG_SOCKU
  printf("Accepting connections.\n");
#endif

  return sockfd;

}

/* sockcreate -- Create socket from which to read. return socket descriptor if ok, -1 if not ok. */
int sockcreateINET(int port) {
  int sockfd;
  struct sockaddr_in serv_addr;

#ifdef DEBUG_SOCKU
  printf("Trying to create socket in port %d...\n", port);
#endif

  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd == -1)
    return -1;

  /* bind our local address so client can connect to us */
  bzero((char *) & serv_addr, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  serv_addr.sin_port = htons(port);

  if (bind(sockfd, (struct sockaddr *) & serv_addr, sizeof(serv_addr)) < 0)
    return -1;

#ifdef DEBUG_SOCKU
  printf("Socket created!!!\n");
#endif

  /* listen on the socket for connection */
  if (listen(sockfd, 5) == -1)
    return -1;

#ifdef DEBUG_SOCKU
  printf("Accepting connections...\n");
#endif

  return sockfd;
}

/* sockaccept -- accept connection on a socket. Return new socket descriptor if ok, -1 if not ok. */
int sockaccept(int sock) {
  int clilen;
  int newsockfd;
  struct sockaddr_in cli_addr;

  clilen = sizeof(cli_addr);
  newsockfd = accept(sock, (struct sockaddr *) & cli_addr, & clilen);
  if (newsockfd < 0)
    return -1;
  return newsockfd;
}

/* sockconnect -- create socket to host at port. Return socket if ok, -1 if no ok. */
int sockconnectINET(int port) {
  unsigned long int inaddr;
  struct sockaddr_in serv_addr;

  int sockfd;
  struct hostent * hp;

  /* * First try to convert the host name as a dotted-decimal number. * Only if that fails do we call gethostbyname(). */
  bzero((void *) & serv_addr, sizeof(serv_addr));

  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(port);

  /* open a TCP socket (an Unix stream socket). */
#ifdef DEBUG_SOCKU
  puts("Opening socket...");
#endif

  if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    return -1;

#ifdef DEBUG_SOCKU
  puts("Open. Trying connection to server...");
#endif

  /* socket open, so connect to the server */
  if (connect(sockfd, (struct sockaddr *) & serv_addr, sizeof(serv_addr)) < 0)
    return -1;

#ifdef DEBUG_SOCKU
  printf("Connection established!\n");
#endif

  return sockfd;
}

/* sockconnect -- create socket to host at port. Return socket if ok, -1 if no ok. */
int sockconnect(char path[]) {
  unsigned long int inaddr;
  struct sockaddr_un serv_addr;
  int sockfd;
  struct hostent * hp;


  /* * First try to convert the host name as a dotted-decimal number. * Only if that fails do we call gethostbyname(). */
  bzero((void *) & serv_addr, sizeof(serv_addr));
#ifdef DEBUG_SOCKU
  printf("Looking up %s...\n", path);
#endif

#ifdef DEBUG_SOCKU
  printf("Found it.  Setting port connection to %s...\n", path);
#endif

  serv_addr.sun_family = AF_UNIX;
  strncpy(serv_addr.sun_path, path, sizeof(serv_addr.sun_path));

#ifdef DEBUG_SOCKU
  puts("Done. Opening socket...");
#endif

  /* open a TCP socket (an Unix stream socket). */
  if ((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
    return -1;

#ifdef DEBUG_SOCKU
  puts("Open. Trying connection to server...");
#endif
  /* socket open, so connect to the server */
  if (connect(sockfd, (struct sockaddr *) & serv_addr, sizeof(serv_addr)) < 0)
    return -1;

#ifdef DEBUG_SOCKU
  printf("Connection established!\n");
#endif

  return sockfd;
}

int recvWithSize(int socket, char * buffer, int sizebuffer) {
  int rval = 0, cread = 0, size = 0;
  char straux[sizeof(int) + EOF_SIZE];

  cread = 0;
  while (cread < sizeof(int)) {
    if ((rval = recv(socket, straux, sizeof(int), 0)) < 0) {
      return -1;
    }
    cread = cread + rval;
  }

  /* Get size of message */
  convertToInt (straux, sizeof(int), &size, 0);

  //JSalas
  if (sizebuffer < size){
#ifdef FATAL_SOCKU
     printf ("NOT Possible store message<%d> in buffer<%d> (FATAL ERROR)\n", size, sizeof(buffer));
#endif
     exit(0);
  }
  bzero(buffer, sizebuffer);

  /* Get data of message */
  cread = 0;
  while (cread < size) {
    if ((rval = recv(socket, buffer + cread, size - cread, 0)) < 0) {
      return -1;

    }
    cread = cread + rval;
  }

#ifdef DEBUG_SOCKU
  printf("Received Bytes: %d\n", size);
  printf("Received Message: %s\n", buffer);
#endif

  return size;
}

int sendWithSize(int socket, char * men, int size) {
  int rval, i;
  char buffer[size + sizeof(int) + EOF_SIZE /*+ EXTRASIZE - RMEXTRASIZE*/];

  bzero(buffer, sizeof(buffer));

  /* Add size to message */
  convertToChar(buffer, sizeof(int), 0, size);

  /* Add data to message */
  memcpy(buffer + sizeof(int), men, size);

  /* Send message */
  if ((rval = send(socket, buffer, size + sizeof(int), 0)) < 0) {
    return -1;
  }
//printf("Send Bytes: %d\n", size + sizeof(int));
#ifdef DEBUG_SOCKU
  printf("Send Bytes: %d\n", size + sizeof(int));
  printf("Send Message: %s\n", buffer + sizeof(int));
#endif

  return size + sizeof(int);
}

int recvWithSizeMem(int socket, char ** buffer, int timeout) {
  int rval = 0, cread = 0, size = 0;
  char straux[sizeof(int) + EOF_SIZE];

  *buffer = NULL;
//printf("recvWithSizeMem 1\n");
  cread = 0;
  while (cread < sizeof(int)) {
    if ((rval = recv(socket, straux + cread, sizeof(int) - cread, timeout)) < 0) {
      return -1;
    }
    cread = cread + rval;
  }
//printf("recvWithSizeMem 2\n");
  /* Get size of message */
  convertToInt (straux, sizeof(int), &size, 0);
//printf("Bytes SEnd: %d\n", size);
  if (!(* buffer = (char *) calloc(sizeof(char), (size + EOF_SIZE)))) {
#ifdef FATAL_SOCKU
    printf("%s# %d # Out of memory.\n", __FILE__, __LINE__);
#endif
    exit(-1);
  }
  //bzero(*buffer, sizeof(*buffer));
//printf("Bytes SEnd: \n");
  /* Get data of message */
  cread = 0;
  while (cread < size) {
    if ((rval = recv(socket, (*buffer) + cread, size - cread, timeout)) < 0) {
    	printf("Error: %d\n", cread);
      return -1;
    }
    cread = cread + rval;
    //printf(": %d\n", cread);
  }
//printf("Exit: %d\n", cread);
  buffer[cread] = '\0';
//printf("Exit: %d\n", cread);
  //printf("Received Bytes: %d\n", size);
#ifdef DEBUG_SOCKU
  printf("Received Bytes: %d\n", size);
  printf("Received Message: %s\n", *buffer);
#endif

  return size;
}
