/*
 * Implement a solution to the bounded buffer problem
 *  spawn off num_prods producers and num_consumers, each
 *  producer tid produces num_items items, each consumer tid
 *  consumes num_items items
 *
 * (newhall, 2013)
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

// use global variables for shared buffer state
static char *buff;    // the buffer
static int  N;        // total capacity
static int  size;     // current num elements
static int  next_in;  // next insertion index
static int  next_out; // next remove index
static int  num_items; // number of items each tid should produce or consume

// add any synch primatives here

// the thread main routine must match this prototype:
void *producer(void *arg);
void *consumer(void *arg);

// these functions are implemented for you
static void *my_malloc(size_t size);
static void add_to_queue(char item);
static char remove_from_queue(void);
static void print_buffer(void);      // for debugging

/***************************************************************/
int main(int argc, char **argv) {

  pthread_t *ptids, *ctids;
  int i, num_prods, num_cons;
  // add any locals you need

  if(argc != 4) {
    printf("usage: ./prodcons n_prods_cons n_items buff_size\n");
    exit(1);
  }
  num_prods = num_cons = atoi(argv[1]);
  num_items = atoi(argv[2]);
  N = atoi(argv[3]);
  printf("%d Producer & Consumer tids, each producing %d items, buff size %d\n",
      num_prods, num_items, N);

  ptids = (pthread_t *)my_malloc(sizeof(pthread_t)*num_prods);
  ctids = (pthread_t *)my_malloc(sizeof(pthread_t)*num_cons);

  buff = (char *)my_malloc(sizeof(char)*N);
  size = 0;
  next_in = 0;
  next_out = 0;

  // TODO:  create the producer and consumer threads
  //        initialize any synchronization primitives prior to this
  for (i=0; i < num_prods; i++) {

  }

  for (i=0; i < num_cons; i++) {
  }

  // wait for threads to exit
  for (i=0; i < num_prods; i++) {
    pthread_join(ptids[i], 0);
  }
  for (i=0; i < num_cons; i++) {
    pthread_join(ctids[i], 0);
  }

  free ((char *)buff);
  free ((pthread_t *)ptids);
  free ((pthread_t *)ctids);
  exit(0);
}
/****************************************************************/
void *producer(void *arg){

  // TODO: implement this  (feel free to change the return value)
  return NULL;
}
/****************************************************************/
void *consumer(void *arg){

  // TODO: implement this
  return NULL;
}
/****************************************************************/
//
// Add an item to the circular buffer
// note: this function does no checking that there is enough
// space to add, the caller is responsible for that
//   item: the value to add
//
static void add_to_queue(char item) {

  buff[next_in] = item;
  next_in = (next_in + 1) % N;
  size += 1;
}
/****************************************************************/
//
// Remove an item to the circular buffer
// note: this function does no checking that there is something
// in the queue to remove, the caller is responsible for that
//   returns: the next item to remove
//
static char remove_from_queue() {
  char item;
  item = buff[next_out];
  next_out = (next_out + 1) % N;
  size -= 1;
  return item;
}
/****************************************************************/
// print out the contents of the buffer
static void print_buffer() {
  int i, index;
  printf("Buffer size %d ************************\n", size);
  for(i=0; i < size; i++) {
     index = (next_out + i) % N;
     printf("%2d:%2d ", index, buff[index]);
  }
  printf("\n next in = %d  next out %d\n", next_in, next_out);
  printf("***************************************\n");

}
/****************************************************************/
// a wrapper function around malloc that calls perror and exit
// on error
static void *my_malloc(size_t size){
  void *ret;
  ret = malloc(size);
  if(!ret) {
    perror("malloc array error");
    exit(1);
  }
  return ret;
}