/*********************************************************/
/*    Autori:                                            */
/*            Pelosi Giovanni, 346787, pelosi            */
/*            Pesce  Agatino,  456868, pesce             */
/*                                                       */
/*  Progetto: Sistemi 2: biblioteca                      */
/*  Data Pr.: 9/2/96                                     */
/*      File: message.c                                  */
/*                                                       */
/*********************************************************/

#include <string.h>
#include <assert.h>
#include <signal.h>

#include "system.h"
#include "message.h"
#include "socket.h"

#ifdef __cplusplus
typedef void (*fptr)(int);
#else
typedef void (*fptr)();
#endif


static int _sigpipe = 0;

/*
************************************************************************
*
*   handler per sigpipe in caso di write su socket chiusa
*
************************************************************************
*/

void SigPipeHandler(int sig)
{
  sig ++; /* causa compilatore idiota*/
  signal(SIGPIPE,(fptr) SigPipeHandler);
  _sigpipe ++;
  return;
}


/*
************************************************************************
*
*   funzioni comuni per trattamento struttura libro in messaggi
*
************************************************************************
*/

int SizeRawLibro(void)
{
  int size=0;
  Libro_t Libro;  /* Orribile, casomai ripensarci */
  size += sizeof(Libro.Titolo);
  size  += sizeof(Libro.Autore);
  size  += sizeof(Libro.Anno);
  return size;
}

int PutRawLibro(char* t, Libro_t* Libro)
{
  char *p = t;
  strncpy(p,      Libro->Titolo,  sizeof(Libro->Titolo));         p += sizeof(Libro->Titolo);
  strncpy(p,      Libro->Autore,  sizeof(Libro->Autore));         p += sizeof(Libro->Autore);
  strncpy(p,      Libro->Anno,    sizeof(Libro->Anno));           p += sizeof(Libro->Anno);
  return (int)(p - t);
}

int GetRawLibro(Libro_t* Libro, char* t)
{
  char *p = t;
  strncpy(Libro->Titolo,  p,      sizeof(Libro->Titolo)); p += sizeof(Libro->Titolo);
  strncpy(Libro->Autore,  p,      sizeof(Libro->Autore)); p += sizeof(Libro->Autore);
  strncpy(Libro->Anno,    p,      sizeof(Libro->Anno));   p += sizeof(Libro->Anno);
  return (int)(p - t);
}

/*
************************************************************************
*
*   funzioni trattamento messaggi "control"
*
************************************************************************
*/

int SizeMsgControl(void)
{
  int size=0;
  size++; /* Type */
  size+=sizeof(struct in_addr);
  size+=sizeof(unsigned short);

  return size;
}

int GetMsgControl(MessageControl_t* Msg ,char* t)
{
  char *p = t;
  memcpy(&Msg->Sin.sin_addr,      p,      sizeof(struct in_addr));
  p+=sizeof(struct in_addr);
  memcpy(&Msg->Sin.sin_port,      p,      sizeof(unsigned short));
  p+=sizeof(unsigned short);
  return (int)(p - t);
}

int PutMsgControl(int Socket, struct sockaddr_in * Sin)
{
  SocketInfo_t* Info=RetrieveSockInfo(Socket);
  char *p = Info->Text;
  memset(Info->Text,0,sizeof(MessageText_t));

  *p = MsgTypeControl;                    p++;
  memcpy(p, &Sin->sin_addr, sizeof(struct in_addr));
  p+=sizeof(struct in_addr);
  memcpy(p, &Sin->sin_port, sizeof(unsigned short));
  p+=sizeof(unsigned short);

  Info->Len = Info->ToWrite = (int)(p - Info->Text);
  return Info->Len;
}

/*
************************************************************************
*
*   funzioni trattamento messaggi "query"
*
************************************************************************
*/

int SizeMsgQuery(void)
{
  int size=0;
  size++; /*Type*/
  size++;  /*NoForward*/
  size+=SizeRawLibro();
  return size;
}

int ClPutMsgQuery(MessageText_t Text, char NoForward, Libro_t* Libro)
{
  char *p = Text;
  memset(Text,0,sizeof(MessageText_t));

  *p =    MsgTypeQuery;                   p++;
  *p =    NoForward;                              p++;
  p       +=      PutRawLibro(p,Libro);

  return (int)(p - Text);
}

int PutMsgQuery(int Socket, char NoForward, Libro_t* Libro)
{
  SocketInfo_t* Info=RetrieveSockInfo(Socket);

  Info->Len = Info->ToWrite = ClPutMsgQuery(Info->Text, NoForward, Libro);
  return Info->Len;
}

int GetMsgQuery(MessageQuery_t* Msg, char* t)
{
  char *p = t;
  Msg->NoForward = *p;            p++;
  p += GetRawLibro(&Msg->Libro,p);
  return (int)(p - t);
}

/*
************************************************************************
*
*   funzioni trattamento messaggi "replay"
*
************************************************************************
*/

int SizeMsgReply(void)
{
  int size=0;
  size++; /*Type*/
  size += sizeof(Biblio_t);
  size += SizeRawLibro();
  return size;
}

int PutMsgReply(int Socket, Biblio_t Biblio, Libro_t* Libro)
{
  SocketInfo_t* Info=RetrieveSockInfo(Socket);
  char *p = Info->Text;
  memset(Info->Text,0,sizeof(MessageText_t));

  *p = MsgTypeReply;                      p++;
  strncpy(p,(char*) Biblio, sizeof(Biblio_t));    p += sizeof(Biblio_t);
  p += PutRawLibro(p,Libro);

  Info->Len = Info->ToWrite = (int)(p - Info->Text);
  return Info->Len;
}

int ClGetMsgReply(MessageReply_t* Msg, char* t)
{
  char *p = t;
  memset(Msg, 0, sizeof(MessageReply_t));
  strncpy(Msg->Biblio,p,          sizeof(Biblio_t));      p += sizeof(Biblio_t);
  p += GetRawLibro(&Msg->Libro,p);
  return (int)(p - t);
}

int GetMsgReply(MessageReply_t* Msg, char* t)
{
  return ClGetMsgReply(Msg, t);
}

/*
************************************************************************
*
*   funzioni per impostazione ciclo nullo di ritorno select()
*
************************************************************************
*/

int PutMsgNil(int Socket)
{
  SocketInfo_t* Info=RetrieveSockInfo(Socket);

  Info->Len= Info->ToWrite = 0;

  return 0;
}



/*
************************************************************************
*
*   funzioni generale per lettura messaggi da socket in buffer
*
************************************************************************
*/
/* restituisce:
 *  -1 su errore o eof
 *      0 su messaggio non ancora completato
 *   1 su messaggio completato
 */
int ReadMessage( int Socket, Message_t* Msg )
{
  int n;
  SocketInfo_t* Info              =  RetrieveSockInfo(Socket);
  char *p=Info->Text;             p++;
  memset(Msg,0,sizeof(Message_t));

  assert(Info->State==SockStateReading || Info->State==SockStateWaitRead);

  n  = read(Socket, &Info->Text[Info->Len - Info->ToRead], Info->ToRead );

  if ( n < 0 )                               /*   if (n < 0) --> (err) */
	 {
		Msg->Type = MsgTypeError;
		return -1;                               /*valutare utilizzo rc type/len*/
	 }

  if ( n == 0 )                             /*    if (n == 0) --> (eof) */
	 {
		if (Info->Len == Info->ToRead)
		  Msg->Type = MsgTypeEof;
		else
		  Msg->Type = MsgTypeError;
		return -1;                               /*valutare utilizzo rc type/len*/
	 }

  Info->ToRead -= n;
  switch(Info->Text[0])
	 {
	 case    MsgTypeQuery:
		Msg->Type = MsgTypeQuery;
		break;

	 case    MsgTypeReply:
		Msg->Type = MsgTypeReply;
		break;

	 case    MsgTypeControl:
		Msg->Type = MsgTypeControl;
		break;

	 default:
		Msg->Type = MsgTypeError;
		return -1;
	 }

  if(Info->State==SockStateWaitRead)
	 {
		Info->State = SockStateReading;
		Info->Len = MessageLen(Msg->Type);
		Info->ToRead = Info->Len - 1;
	 }

  if ( Info->ToRead > 0 )
	 return 0;

  switch (Msg->Type)
	 {
	 case MsgTypeQuery:
		GetMsgQuery(&Msg->Body.Query,p);
		return 1;

	 case MsgTypeReply:
		GetMsgReply(&Msg->Body.Reply,p);
		return 1;

	 case MsgTypeControl:
		GetMsgControl(&Msg->Body.Control,p);
		return 1;

	 default:                                                /* per casi strani tipo errore formato messaggio */
		Msg->Type = MsgTypeError;
		return -1;
	 }
}


/*
************************************************************************
*
*   funzioni generale per scrittura messaggi a socket da buffer
*
************************************************************************
*/
/*
 * restituisce:
 * -1 su errore o eof
 * 0 su messaggio non ancora completato
 * 1 su messaggio completato
 */
int WriteMessage( int Socket )
{
  int n;                                         /* n.bytes letti */
  SocketInfo_t * Info = RetrieveSockInfo(Socket);

  assert(Info->State==SockStateWriting);

  if( Info->ToWrite == 0 )        /* per i casi in cui la write serve solo a      */
	 return 1;                                       /* dare il via alla read                                                        */



  signal(SIGPIPE,(fptr)SigPipeHandler); _sigpipe = 0;


  n  = write(Socket, &Info->Text[Info->Len - Info->ToWrite], Info->ToWrite );


  signal(SIGPIPE,SIG_DFL);


  if ( _sigpipe !=0 )
	 return -1;
  if ( n <= 0 )              /*   if (n < 0) --> (err) */
	 return -1;              /* errore perche non EWOULDBLOCK da select*/

  Info->ToWrite -= n;

  if ( Info->ToWrite > 0 )
	 return 0;               /* incompleta -> continue */

  return 1;                  /**End of Write*/
}

/*
************************************************************************
*
*   funzioni generale per determinare lunghezza messaggio
*
************************************************************************
*/
int MessageLen(int Type)
{
  switch(Type)
    {
	 case    MsgTypeQuery:
		return SizeMsgQuery();

	 case    MsgTypeReply:
		return SizeMsgReply();

	 case    MsgTypeControl:
		return SizeMsgControl();

	 }
  return -1;
}