#include "spinlock.h"
#include <stdio.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/stat.h>

int P, N, M, ID, *submatriz;
typedef struct {
  spinlock_t matriz_lock;
  int matriz_distancias[1];
} dados_partilhados;
dados_partilhados *mem_partilhada;

#define matriz_lock              mem_partilhada->matriz_lock
#define MATRIZ(LINHA,COLUNA)     mem_partilhada->matriz_distancias[(LINHA) * N + COLUNA]
#define SUBMATRIZ(LINHA,COLUNA)  submatriz[(LINHA) * N + COLUNA]
#define NONE -1

void floyd(void);
void ler_matriz(void);
void escrever_matriz(void);
void *mmap_init(char *file, void *start_addr, int size);
double current_time(void);



main(int argc, char **argv) {
  double execution_time;

  // parsing da linha de comandos
  if (argc == 2)
    P = atoi(argv[1]);
  else
    P = 1;
  fprintf(stderr, "All-Pairs Shortest Paths: %d agente(s)\n", P);

  // obtém o número de nós e testa se é uma configuração válida
  scanf("%d", &N);
  if (N % P) {
    fprintf(stderr, "Configuração inválida!\n");
    exit(0);
  }

  // inicializações
  M = N / P;
  submatriz = (int *) malloc(M * N * sizeof(int));
  mem_partilhada = (dados_partilhados *) mmap_init("dados", NULL, sizeof(dados_partilhados) + N * N * sizeof(int));
  spin_lock_init(&matriz_lock);
  ler_matriz();

  // criar os restantes agentes e executar
  execution_time = current_time();
  for (ID = 1; ID < P; ID++)
    if (fork() == 0) {
      memcpy(submatriz, &MATRIZ(ID * M, 0), M * N * sizeof(int));
      floyd();
      return;
    }
  ID = 0;
  memcpy(submatriz, &MATRIZ(0, 0), M * N * sizeof(int));
  floyd();

  // termina contagem do tempo e escreve resultado
  execution_time = current_time() - execution_time;
  escrever_matriz();
  fprintf(stderr, "tempo total de execução:   %f segundos\n", execution_time);
  return;
}


void floyd(void) {
  int d;
  for (d = 2; d < 2 * N; d *= 2) {
    // calcula matriz Di
    ...
  }
  return;
}


void ler_matriz(void) {
  int linha, coluna, item;
  for (linha = 0; linha < N; linha++)
    for (coluna = 0; coluna < N; coluna++) {
      scanf("%d", &item);
      if (linha == coluna)
	item = 0;
      else if (item == 0)
	item = NONE;
      MATRIZ(linha, coluna) = item;
    }
  return;
}


void escrever_matriz(void) {
  int linha, coluna;
  for (linha = 0; linha < N; linha++) {
    for (coluna = 0; coluna < N; coluna++) {
      if (MATRIZ(linha, coluna) == NONE)
      	printf("0");
      else
	printf("%d", MATRIZ(linha, coluna));
      if (coluna != N - 1)
	  printf(" ");
    }
    printf("\n");
  }
  return;
}


void *mmap_init(char *file, void *start_addr, int size) {
  int fd;
  if ((fd = open(file, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) == -1) {
    printf("erro no open!\n");
    exit(0);
  }
  if (lseek(fd, size, SEEK_SET) == -1) {
    printf("erro no lseek!\n");
    exit(0);
  }
  if (write(fd, "", 1) == -1) {
    printf("erro no write!\n");
    exit(0);
  }
  if ((start_addr = mmap(start_addr, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0)) == (void *)-1) {
    printf("erro no mmap!\n");
    exit(0);
  }
  return start_addr;
}


double current_time(void) {
  struct timeval tempo;
  gettimeofday(&tempo, NULL);
  return ((double)tempo.tv_sec + (double)tempo.tv_usec / 1000000);
}
