#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>

/*
 * Main thread creates a a queue of ready processes according to priorities
 * The "scheduler" thread is activated by a signal and always picks the
 * highest priority process
 *
 * Jose Rogado, November 2008
 */

#define NPRI 10

typedef struct proc {
  int pid;
  int pri;
  int slice;
  time_t time;
  struct proc *next;
  struct proc *prev;
} proc_t;

typedef struct queue_head {
  struct proc *next;
  struct proc *last;
  int nprocs;
} queue_t;


queue_t array_queue[NPRI];

void selecao(void);

typedef void (*sighandler_t)(int);
sighandler_t handler();

pthread_mutex_t mutex;
pthread_cond_t pick;
int active  = 0;

int main()
{
  int pri, ret, nprocs=0, pid;
  proc_t *p,*r;
  queue_t *q;
  pthread_t thread;
  sighandler_t res;

  pthread_mutex_init(&mutex, NULL);
  pthread_cond_init (&pick, NULL);

  memset(array_queue, 0, NPRI *sizeof(queue_t));
  active = 1;

  res = signal(SIGINT, (sighandler_t)handler);
  if (res == SIG_ERR)
    perror("signal");

  ret = pthread_create(&thread, NULL, (void *)(&selecao), (void *)NULL);

  while(1) {

    printf("Insira a prioridade do Processo %d: ", nprocs);
    ret = scanf("%d",&pri);
    if(ret == EOF) {
      active = 0;
      pthread_mutex_lock(&mutex);
      pthread_cond_signal(&pick);
      pthread_mutex_unlock(&mutex);
      break;
    }
    if (pri>9) pri=9;

    if (pri<0) pri=0;

    p = (proc_t *) calloc(1, sizeof( struct proc ) );
    p->pri = pri;
    p->pid = nprocs++;

    q = &array_queue[pri];

    // Inicio Seccao Critica
    pthread_mutex_lock(&mutex);

    if(q->next == 0) {
      q->next = p;
      q->last = p;
    } else {
      r=q->last;
      r->next = p;
      q->last = p;
    }
    p->next = 0;

    // Fim Seccao Critica
    pthread_mutex_unlock(&mutex);
	
    //printf("Prioridade do Processo Inserido: %d\n",p->pri);
    //printf("Numero do Processo: %d\n",p->pid);
  }
  // Espera pela concusão da thread
  pthread_join(thread, NULL);
}

void selecao(){

  int k = 0;
  proc_t *p;
  queue_t *q;

  while (active) {
    pthread_mutex_lock(&mutex);
    // Espera pelo evento
    pthread_cond_wait(&pick, &mutex);
    /*
     * Procurar o processo de maior prioridade nas ready queues
     */
    for (k = 0; k < NPRI; k++) {
      q = &array_queue[k];
      if (q->next != NULL) {
        /*
        * Retirar o processo de maior prioridade da lista
        */
        p = q->next;
    	q->next = p->next;
        q->nprocs--;
        printf ("\nSeleccao pid: %d  pri: %d\n", p->pid , p->pri);
        free(p);
        break;
      }
      if (k == NPRI - 1)
        printf("No more procs\n");
    }
    pthread_mutex_unlock(&mutex);
  }
  pthread_exit(NULL);
}

sighandler_t handler()
{
  // Sinaliza o evento	
  pthread_mutex_lock(&mutex);
  pthread_cond_signal(&pick);
  pthread_mutex_unlock(&mutex);
}
