/*****************************************************************************
* 	Project 	: Network Based Message Queue System		    **
*	Course		: CIS 650 - Software Engineering		    **
*	Location 	: ~sameer/cis650/proj/uai               	    **
*	File		: uai.cc                                            **
*	Description 	: user application interface                        **
*****************************************************************************/

#include "uai.h"

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <stream.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

// includes from router

#include <rtmsg.h>
/* defines common with the router */
#define SERV_TCP_PORT   7123
#define SERV_HOST_ADDR  "kaliber" /* kaliber.cs.uoregon.edu */

#define MAX_MESSAGE_SIZE        1024

#define DATA_MESSAGE            100
#define REQUEST_MESSAGE         101
#define CONTROL_MESSAGE         102
#define QUERY_MESSAGE           103

/* error dump macro */
/* To display an error message */
#define err_dump(ret,str)                               \
if (ret < 0)    {                                       \
        printf("ERROR message from %s\n", processName); \
        perror(str);                                    \
        printf("Failure at line %d, file \"%s\"\n", __LINE__, __FILE__); \
        exit(1); \
        }






char processName[256];
// UAI functions

int nmsgget(key_t key, int msgflag)
/* The parameters are not used at the moment */
{
  FILE               *fp;
  char               serverName[64];
  int	             sockFd;
  struct sockaddr_in servAddr;
  struct hostent     *hp;

  /* read the hostname from the configuration file */
  fp = popen("grep HOSTNAME msgqconf", "r");
  fscanf(fp,"HOSTNAME %s", serverName);
  /*
  printf("server = %s\n", serverName);
  */
  pclose(fp);

  hp = gethostbyname(serverName);
  err_dump(hp, "uai : Host name not found");

  /* initialize servAddr structure */
  memset((char *) &servAddr, 0, sizeof(servAddr));
  memcpy((char *) &servAddr.sin_addr,
         (char *) hp->h_addr_list[0],
	 hp->h_length);
  
  servAddr.sin_family	= AF_INET;
  servAddr.sin_port	= htons(SERV_TCP_PORT);
  
  /* open a tcp socket */
  sockFd = socket(AF_INET, SOCK_STREAM, 0);
  err_dump(sockFd,"uai : can't open stream socket");
  
  /* connect to the server */
  err_dump(connect(sockFd, (struct sockaddr *) &servAddr, sizeof(servAddr)),
	   "uai : can't connect to server");

  return(sockFd);
}

int nmsgsnd(int nmsqid, struct nmsgbuf *ptr, int length, int flag)
{
  MsgHeader    *messageHeader = new MsgHeader();
  struct iovec  iov[2];
  int           bytesSent;

/* initialize message header for a data message*/
  messageHeader->opcode    = DATA_MESSAGE;
  messageHeader->msgLength = length;
  messageHeader->msgType   = ptr->mtype;
  messageHeader->uniqueId  = ngetownid();

/*
  printf("lib : sending clId = %d\n",  messageHeader-> uniqueId);
*/
/* initialize data structure for transfer */
  iov[0].iov_base = (char *)messageHeader;
  iov[0].iov_len  = sizeof(*messageHeader);
  iov[1].iov_base = (char *)&(ptr->mtext);
  iov[1].iov_len  = length;

/* send message to router */
  bytesSent = writev(nmsqid, iov, 2);
/*
  printf("lib : sent %d bytes \n", bytesSent);
*/

/* user doesn't care about the header */
  if (bytesSent > 0) bytesSent -= sizeof(messageHeader);
  return(bytesSent);
}

int nmsgrcv(int nmsqid, struct nmsgbuf *ptr, int length, long msgtype)
{
  MsgHeader    *messageHeader = new MsgHeader();
  struct iovec  iov[2];
  int           bytesReceived;

/* initialize message header for a request message*/
  messageHeader->opcode    = REQUEST_MESSAGE;
  messageHeader->msgLength = 0;
  messageHeader->msgType   = int(msgtype);
  messageHeader->uniqueId  = ngetownid();

/* initialize data structure for transfer */
  iov[0].iov_base = (char *)messageHeader;
  iov[0].iov_len  = sizeof(*messageHeader);
  iov[1].iov_base = ptr->mtext;
  iov[1].iov_len  = 0;

  memset(ptr->mtext,0,length);
/* request a message from the router */
  writev(nmsqid, iov, 2);
/*
  printf("sent request to the server asking for receive\n");
*/


/* initialize data structure for transfer */
  iov[0].iov_base = (char *)messageHeader;
  iov[0].iov_len  = sizeof(*messageHeader);
  iov[1].iov_base = ptr->mtext;
  iov[1].iov_len  = length;

/* get the requested message */
  bytesReceived = readv(nmsqid, iov, 2);
/*
  printf("lib read bytes %d\n",bytesReceived);
  printf("lib Got opcode = %d, len = %d, type = %d, id = %d,\ndata = %s\n",
messageHeader->opcode,messageHeader->msgLength,messageHeader->msgType,
messageHeader->uniqueId,ptr->mtext);
  printf("lib : received clId = %d\n", messageHeader->uniqueId);   
*/

/* user doesn't care about the header */
  if (bytesReceived > 0) bytesReceived -= sizeof(messageHeader);

/* return the unique ID of the message */
  ptr->mtype = messageHeader->uniqueId;
  return(bytesReceived);
}

int nmsgctl(int nmsqid, int cmd, nmsqid_ds *buff)
{
  MsgHeader    *messageHeader = new MsgHeader();
  struct iovec  iov[2];

/* initialize message header for a control message */
  messageHeader->opcode    = CONTROL_MESSAGE;
  messageHeader->msgLength = 0;
  messageHeader->msgType   = cmd;
  messageHeader->uniqueId  = (gethostid() << 16) | ((getpid() << 16) >> 16);
  
/* initialize data structure for transfer */
  iov[0].iov_base = (char *)messageHeader;
  iov[0].iov_len  = sizeof(*messageHeader);
  iov[1].iov_base = (char *)buff;
  if (cmd == IPC_STAT)
    iov[1].iov_len  = sizeof(*buff);
  else
    iov[1].iov_len = 0;

/* send the control command to the message queue */  
  writev(nmsqid, iov, 2);

  if (cmd == IPC_RMID)
/* remove own socket */
    close(nmsqid);
  else if (cmd == IPC_STAT)
/* get the statistical information */
    readv(nmsqid, iov, 2);
  else
  {
    cerr << "Message Queue Command " << cmd << " not implemented!\n";
    return(-1);
  }

  return(1);
}
	
int ngetownid(void)
{
  return((gethostid() << 16) | ((getpid() << 16) >> 16));
}


