/*
 * admin.c
 * 
 * receives messages from other computers, informing it about who is 
 * currently logged to them. It solves the problem of big messages by 
 * puting them in a linked list and waiting for all of the to arrive
 * before displaying anything.
 *
 * By Kamran Karimi
 */

#include "admin.h"

struct infolink
{
 struct message mess;
 struct infolink *next, *prev;
 struct infolink *nextsame;
};
struct infolink FirstInfo;
struct infolink *PrevInfo;

int msgid = -1;

static void sig_handler(int signo)
{
 fprintf(stderr,"Admin: Quiting\n");
 if(msgid > -1)
  msgctl(msgid, IPC_RMID, 0);
 exit(5);
}


void show_info(struct infolink *info)
{
 struct infolink *tempinfo = info;

 printf("Users On Machine %s\n", info->mess.body.spec);
 while(tempinfo)
 {
  printf("%s", tempinfo->mess.body.mtext);
  tempinfo = tempinfo->nextsame;
 }
}

 
void add_info(struct message *mess, struct infolink *current)
{
 struct infolink *tempinfo, *tempinfo2;
 
 /* prepare the newly arrived information to be put in the linked list */
 tempinfo = malloc(sizeof(struct infolink));
 if(tempinfo == NULL) 
 {
  fprintf(stderr,"Admin: Out of Memory\n");
  raise(SIGINT);
 }
 tempinfo->nextsame = NULL; 
 tempinfo->next = NULL;
 tempinfo->prev = NULL;
 tempinfo->mess = *mess;

 /* see if this information follows others */
 if(current == NULL)
 { /* place it in the main linked list */
  tempinfo->prev = PrevInfo;
  PrevInfo->next = tempinfo;
  PrevInfo = tempinfo;
  current = tempinfo;
 }
 else
 { /* find the end of the nextsame linked list */
  tempinfo2 = current;
  while(tempinfo2->nextsame != NULL)
   tempinfo2 = tempinfo2->nextsame;
  tempinfo2->nextsame = tempinfo;
 }
 /* display the information, if all of it has arrived */
 if(mess->body.done == 1)
  show_info(current);
}
 
  
void process_message(struct message *mess)
{
 struct infolink *tempinfo, *tempinfo2, *tempinfo3;

 /* use the network address of the computers to identify them */ 
 tempinfo = FirstInfo.next;
 while(tempinfo)
 {
  if(!strcmp(tempinfo->mess.body.spec, mess->body.spec))
   break;
  tempinfo = tempinfo->next;
 }
 if(tempinfo)
 { /* found some pre-existing info about that computer */
  if(mess->body.type == FIRST)
  { /* remove the previous entry */
   tempinfo->prev->next = tempinfo->next;
   if(tempinfo->next)
    tempinfo->next->prev = tempinfo->prev;
   /* free it. don't forget the information that follow in the nextsame */ 
   tempinfo2 = tempinfo->nextsame;
   while(tempinfo2)
   {
    tempinfo3 = tempinfo2->nextsame;
    free(tempinfo2);
    tempinfo2 = tempinfo3;
   }
   if(PrevInfo == tempinfo)
    PrevInfo = PrevInfo->prev;
   free(tempinfo);
   add_info(mess, NULL);
  }
  else /* this is not the first message */
   add_info(mess, tempinfo);
 }
 else /* there is no previous info about that computer */
  add_info(mess, NULL);
}


void main()
{
 struct message mess;
 struct sigaction sact;
 
 FirstInfo.next = FirstInfo.prev = FirstInfo.nextsame = NULL;
 PrevInfo = &FirstInfo;
 sact.sa_handler = sig_handler;
 sigemptyset(&sact.sa_mask);
 sact.sa_flags = 0;

 if(sigaction(SIGINT, &sact, NULL) < 0)
 {
  fprintf(stderr,"Admin: Can't Catch Signal");
  fprintf(stderr,"  BECAUSE: %s\n",strerror(errno));
 } 
 msgid = msgget(MSG_KEY, MSG_MODE | IPC_CREAT);
 if(msgid < 0) 
 {
  fprintf(stderr,"Admin: msgget() failed BECAUSE %s\n", strerror(errno));
  exit(20);
 }
 while(1) /* wait till an interrupt arrives */
 {
  printf("\nAdmin: waiting to receive a message...\n");
  if(msgrcv(msgid, (struct msgbuf *)&mess, sizeof(struct body), 0, 0) < 0)
  {
   fprintf(stderr,"Admin: msgrcv() failed BECAUSE %s\n", strerror(errno));
   raise(SIGINT);
  }
  else
  {
   if(mess.body.architecture != MY_DIPC_ARCH)
    fprintf(stderr,"Different Architecture. Needs Conversion\n"); 
   process_message(&mess);
  }
 }
}
