/**CFile***********************************************************************

  FileName    [def.c]

  Synopsis    [Files creates a message queue and read from the input of two clients]

  Description [These functions support the implementation of the client program.]

  Authors     [Ralf Seepold]

******************************************************************************/

#define _FILENAME "def.c: "

#include "def.h"

#ifdef SEMAPHORE
  #include "my_semaphore.h"
#endif

/********************************************************************
  Function: 	createQueue
  Description:
  This function creates a new message queue or it connects to an existing queue.
  The parameter is the one received by the user or set in def.h (as SENDKEY/RETURNKEY)
  The return value is the queue id.
  In the case of error, -1 is returned.
  
********************************************************************/
    int createQueue (long queue_number) {
      int queue_id;
   
      if ((queue_id = msgget(queue_number, IPC_CREAT | 0660)) < 0 ) { 
	                       // permission for user(=6) - group (=6) - others (=0)
         perror("msgget");
         return(-1);
      }
      return queue_id;
   }
      
/********************************************************************
  Function: 	removeQueue
  Description:
  This function removes a queue that is identified by the queue
  identifier. In case of successful execution the return
  value is 0, otherwise -1 is returned
  
********************************************************************/
      	
    int removeQueue (long queue_id) { 
      if (msgctl(queue_id, IPC_RMID, NULL) != 0) {
         perror("msgctl");
         return -1;
      }
      else 
         return 0; 
   }

/********************************************************************
  Function: 	writeQueue
  Description:
  The function will write into an existing message queue. The queue
  is defined by the queue id number (first parameter),
  the pointer points to the data (second parameter)
  and size_t defines the size of the data where the pointer 
  *data points to.
  writeQueue is used for the client to server queue
  The return value is 0 in case of successful execution and -1 else.
  
********************************************************************/

    int writeQueue (int queue_id, mymesg *data, size_t size) {
      
      if (msgsnd(queue_id, data, size, IPC_STAT) != 0 ) {
         perror("msgsnd");
         return -1;
      }
   
      return 0;
   }			

/********************************************************************
  Function: 	readQueueRet
  Description:
  The function readQueueRet performs a read from a message queue. The queue is 
  identified by the queue id (first parameter) and the extracted
  data is written into the second paramter that is of type pointer
  to myretmesg.
  
  Internally, the function performs a blocking read (in case the option
  SEMAPHORE is not defined)
  (last parameter in msgrcv == 0)
  on the queue identified by the queue id (first parameter). 
  In case the option SEMAPHORE is defined, the read is non-blocking
  (i.e. IPC_NOWAIT is set) and thus the access must be synchronized 
  by a semaphore before reading the contents of the queue.
  
  The data is transfered into the pointer data that must be set by 
  the caller.
  The return value is the length of the data read, otherwise
  -1 is returned.
  readQueueRet is used for the server to client information. 
  
********************************************************************/

    int readQueueRet (int queue_id, myretmesg *rdata) {
      int read_length;
   
      #ifndef SEMAPHORE
        if ((read_length = msgrcv(queue_id, rdata, RETURN_SIZE, 0, 0 )) != -1) {
      #endif
      #ifdef SEMAPHORE
        if ((read_length = msgrcv(queue_id, rdata, RETURN_SIZE, 0, IPC_NOWAIT )) != -1) {
      #endif
      
         return read_length;
      }
         
      printf (_FILENAME "ERROR: readQueueRet\n");
      perror("msgrcv");
      return -1;
   	
   }

	
/********************************************************************
  Function: 	passfile
  Description:
  Open, read and pass contents of a file (given by a pointer to a file)
  to the message queue with id queue_id.
  Returns the total number of even lines (="total number of lines" / 2)
  that have been passed from the file to the queue and -1; in case of
  error. The EOF token is not counted as a line.
  In case of an error (e.g. total number of lines is odd)
  in the input file, the EOF token is send to the server
  in order to terminate the waiting server process. 

  In case that the SEMAPHORE option is defined:
  After passing one element to the server, the semaphore of the server
  needs to be released and thus the server starts a non-blokcking reading 
  from the queue.
  The client needs to wait on his semaphore until the server has read the 
  data out of the message queue. The server will release the semaphore of
  the client after reading the queue input.
  
  In all cases, the return codes are:
  -1 => file has odd line number
  -2 => line number is no integer
  -3 => other error (e.g. open of file failed).
  
********************************************************************/
    int passfile (const char *file, int queue_id) {
      FILE   *fp;
      mymesg *data;
      char   line[LINE_LENGTH];
      int    passed_lines;
      int    error;
   	
      error = 0;
      passed_lines = 0;
   	   
   	/*
   		Open the file for reading
   	*/
      if ( (fp = fopen(file, PERMISSION)) == NULL ) { 
         printf(_FILENAME "Error when opening file\n");   
         exit(-1);
      }
   	
      if ( (data = (mymesg *) malloc (sizeof(mymesg)) ) == NULL) {
         perror("malloc");
         return -1;
      }
   	
      data->mtype = 100; // give an initial value to data.mtype
   	
   	/* 
   		Read the input from the file. It is expected that the odd line contains one
   		integer number and even line line is a string.
   	*/
            	
      while (fgets(line, sizeof(line), fp) != NULL) { // read the odd line
         if ( (data->mnumber = atol(line)) == 0) {	
             /* convert integer to LONG
                invalid line number in case result == 0
       	     */
         
            //printf(_FILENAME "Error: No valid line number detected\n");
            error = -2;
            break;
         }
         
         if (fgets(line, sizeof(line), fp) != NULL) // read the even line
            strcpy(data->mtext, line);
         else {					// code part missing
            //printf(_FILENAME "Error: code line after line number is missing\n");
            error = -1;
            break;
         }
      	
         if (writeQueue (queue_id, data, SEND_SIZE ) != 0) {
            /* write the data from the
               file into the queue to the server
            */
            printf (_FILENAME "ERROR: in writing data in queue to client\n");
            error = -3;
            break;
         }
      	
         passed_lines++;
      	
      	#ifdef SEMAPHORE
      	  releaseSem(getSemId(RETURNKEY)); 	// server's semaphore is released
          waitSem(getSemId(SENDKEY));  		// client starts waiting on own semaphore
      	#endif
      	      
      }	// end of while
   	
   
   	#ifdef DEBUG
   		printf(_FILENAME "File passed to server.\n");
   	#endif
   	
   	/* Close the file after reading
   	*/
      if (fclose(fp) == EOF) {
         printf(_FILENAME "Error when closing file\n");
         error = -3;
      }
   	
   	/* 
   	 After passing the file to the server, the EOF token
   	 need to be passed tp the server.
   	*/ 
   	
      data->mtype = EOF_TYPE;	// set EOF token
      data->mnumber = 0;
      
      if (writeQueue (queue_id, data, SEND_SIZE) != 0) {
         printf (_FILENAME "ERROR: in writing data\n");
         error = -3;
      }
      
   	#ifdef SEMAPHORE
   	  releaseSem(getSemId(RETURNKEY));
   	  waitSem(getSemId(SENDKEY));
   	#endif
   	
      free(data);
   	
      if (error < 0)
         return(error);
      	
      return(passed_lines);
   } 	// end of passfile()

