/***************************************************************************
                                   main.c
                             -------------------
   This program illustrates the use of IPC message queue mechanism. It employs
   a set of processes, organised into 4 classes: producers, sequencer,
   distributor and consumers.

    begin                : Mon May 12 15:14:52 BRT 2003
    copyright            : (C) 2003 by Marinho Pilla Barcellos
    email                : marinho@exatas.unisinos.br
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
// #define NR_PRODUCERS 1
// #define NR_CONSUMERS 1
// #define NR_PROCS (NR_PRODUCERS + 1 + 1 + NR_CONSUMERS)
#define NR_PROCS 100

#define MSG_LENGHT 64
#define NR_MSGS  5

/* MESSAGE TYPES */
#define PRODUCER_TO_SEQUENCER    1L
#define SEQUENCER_TO_DISTRIBUTOR 2L

/* PROTO */
void producer(int id, key_t, int);
void sequencer(key_t, key_t, int);
void distributor(key_t, key_t, int, int); // #
void consumer(int id, key_t, int);
void fatal_error(char *msg);

/* TYPES */
struct msgbuf {
   long mtype;
   char mtext[MSG_LENGHT];
};

/* VARS */
extern int errno;

int main(int argc, char *argv[])
{
   int result = 0;
   int i;
   key_t key_p2s, key_s2d, key_d2c;
   int msgflag = IPC_CREAT;
   int status;
   int pid_array[NR_PROCS], procs = 0;
	int nr_producers = 6;
	int nr_consumers = 3;
	int probability = 50;

	// interpretação dos parametros passados
	for (i=0; i<argc; i++) {

		if (i<6) {
			if ((!strcmp(argv[i],"-np")) && (strcmp(argv[i+1],"6")) ){
				nr_producers = atoi(argv[i+1]);
			}
			else if ( (!strcmp(argv[i],"-nc")) && (strcmp(argv[i+1],"3")) ) {
						nr_consumers = atoi(argv[i+1]);
			}
			else if ( (!strcmp(argv[i],"-p")) && (strcmp(argv[i+1],"50")) ) {
						probability = atoi(argv[i+1]);
			}
		}
		printf("Argumento numero: %d: %s \n",i, argv[i]);
	}

	// printf("nr_producers: %d\n",nr_producers);
	// printf("nr_consumers: %d\n",nr_consumers);
	// printf("probability: %d\n",probability);

	printf("master: Hello, world!\n");
	for (i = 0; i < NR_PROCS; i++) pid_array[i] = -1;

   // this is the master process, which must first create all processes involved:
   // producer, sequencer, distributor, consumer

   // generate keys (identifiers)
   key_p2s = ftok(argv[0], 111); // producer - sequencer
   key_s2d = ftok(argv[0], 112); // sequencer - distributor
   key_d2c = ftok(argv[0], 113); // distributor - consumer
   msgflag |= S_IRWXU | S_IRWXG | S_IRWXO; /* rwx for all */

////////////////////////////////////
/* FAZER UM FOR PARA N PRODUTORES */
////////////////////////////////////
	for (i=1; i<= nr_producers; i++) {
		result = fork();
   	if (result < 0) fatal_error("master: could not fork");
   	else if (result == 0) { // child, producer
      	producer(i, key_p2s, msgflag);
      	exit(0);
   	}
   	pid_array[procs++] = result;
	}

//////////////////
/* SEQUENCIADOR */
//////////////////
   result = fork();
   if (result < 0) fatal_error("master: could not fork");
   else if (result == 0) { /* child, sequencer */
      sequencer(key_p2s, key_s2d, msgflag);
      exit(0);
   }
   pid_array[procs++] = result;

//////////////////
/* DISTRIBUIDOR */
//////////////////
   result = fork();
   if (result < 0) fatal_error("master: could not fork");
   else if (result == 0) { /* child, distributor */
      distributor(key_s2d, key_d2c, msgflag, nr_consumers);
      exit(0);
   }
   pid_array[procs++] = result;

////////////////////////////////////
/* FAZER UM FOR PARA M CONSUMIDORES */
////////////////////////////////////

	//for (i=1; i<= nr_consumers; i++){
		result = fork();
   	if (result < 0) fatal_error("master: could not fork");
   	else if (result == 0) { /* child, consumer */
      	consumer(1, key_d2c, msgflag);
      	exit(0);
   	}
   	pid_array[procs++] = result;
	//}

   sleep(20);
   while (--procs >= 0)
      kill(9, pid_array[procs]);

   printf("master: done, I have killed all my children.\n");

  return EXIT_SUCCESS;
}

void producer(int id, key_t key_p2s, int msgflag) {
   struct msgbuf message;
   int counter = 0;
   int queue;

   printf("producer %d: running\n", id);
   queue = msgget(key_p2s, msgflag);
   if (queue < 0)
      fatal_error("producer: error in get");
   msgflag = 0;

   sleep(3);
   message.mtype = PRODUCER_TO_SEQUENCER;
   for (counter = 1; counter <= NR_MSGS; counter++) {
      sprintf(message.mtext, "ITEM SEQ %d PRODUCED BY %d", counter, id); // escreve na mensagem
      if (msgsnd(queue, &message, MSG_LENGHT, msgflag) < 0) // envia mensagem
         fatal_error("producer: error in snd");
      printf("producer: sent item sequence %d\n", counter);
      sleep(1);
   }
}

void sequencer(key_t key_p2s, key_t key_s2d, int msgflag) {
   struct msgbuf message;
   int queue1, queue2;

   printf("sequencer: running\n");
   if ((queue1 = msgget(key_p2s, msgflag)) < 0)
      fatal_error("sequencer: error in get");
   if ((queue2 = msgget(key_s2d, msgflag)) < 0)
      fatal_error("sequencer: error in get");
   msgflag = 0;

   while (1) {
      if (msgrcv(queue1, &message, MSG_LENGHT, PRODUCER_TO_SEQUENCER, msgflag) < 0)
         fatal_error("sequencer: error in recv");
      message.mtype = SEQUENCER_TO_DISTRIBUTOR;
      if (msgsnd(queue2, &message, MSG_LENGHT, msgflag) < 0)
         fatal_error("sequencer: error in snd");
   }
   sleep(5);
   msgctl(queue1, IPC_RMID, NULL);  /* one removes the resource, others will abort with error if still in revc */
}

void distributor(key_t key_s2d, key_t key_d2c, int msgflag, int nr_consumers) {
   struct msgbuf message;
   int i = 0;
   int queue1, queue2;

   printf("distributor: running\n");

   queue1 = msgget(key_s2d, msgflag);
   if (queue1 < 0)
      fatal_error("distributor: error in get");
   queue2 = msgget(key_d2c, msgflag);
   if (queue2 < 0)
      fatal_error("distributor: error in get");
   msgflag = 0;

   i =  1;
   while (1) {
      if (msgrcv(queue1, &message, MSG_LENGHT, SEQUENCER_TO_DISTRIBUTOR, msgflag) < 0)
         fatal_error("distributor: error in recv");

      	/* distribute item */
      	while (strncmp(message.mtext, "ACCEPT", strlen("ACCEPT")) != 0) {
         	// send to receiver and wait reply
         	message.mtype = i; /* message type tells which consumer */
         	printf("distributor: sending item to consumer %d\n", i);
         	if (msgsnd(queue2, &message, MSG_LENGHT, msgflag) < 0) // send to consumer i
            	fatal_error("distributor: error in snd");
         	if (msgrcv(queue2, &message, MSG_LENGHT, i + 100, msgflag) < 0) // wait for positive accept
            	fatal_error("distributor error in recv");
         	i++;
         	if (i > nr_consumers) i = 1;
      	}
      // printf("distributor: consumer %d accepted message\n", i);
      message.mtext[0] = 0;
   }
   sleep(5);
   msgctl(queue1, IPC_RMID, NULL);  /* one removes the resource, others will abort with error if still in revc */
}

void consumer(int id, key_t key_d2c, int msgflag) {
   struct msgbuf message;
   int queue;
   int item, producer;

   printf("consumer %d: running\n", id);
   srand(id * 1000); /* initialize random generator with seed based on my id */
   queue = msgget(key_d2c, msgflag);
   if (queue < 0)
      fatal_error("consumer: error in get");
   msgflag = 0;

   sleep(3);

   while (1) {
      if (msgrcv(queue, &message, MSG_LENGHT, id, msgflag) < 0) // try to recv a message sent to my id
         fatal_error("consumer: error in rcv");
      sscanf(message.mtext, "ITEM SEQ %d PRODUCED BY %d", &item, &producer);
      if (rand() < RAND_MAX / 2L) { /* 50% of chances */
         /* accept item */
         printf("consumer %d: item %d of %d received and accepted\n", id, item, producer);
         sprintf(message.mtext, "ACCEPT (sent by consumer %d)", id);
      } else {
         /* reject item */
         printf("consumer %d: item %d of %d received and rejected\n", id, item, producer);
         sprintf(message.mtext, "REJECT (sent by consumer %d)", id);
      }
      message.mtype = id + 100; // message type will indicate which recvr is the destination of the message
      if (msgsnd(queue, &message, MSG_LENGHT, msgflag) < 0)
         fatal_error("consumer: error in snd");
   }
   sleep(5);
   msgctl(queue, IPC_RMID, NULL);  /* one removes the resource, others will abort with error if still in revc */
}

void fatal_error(char *msg) {
   perror(msg);
   exit(1);
}





