#include <stdio.h>
#include <stdlib.h> /* para chamar a função system */
#include <string.h> /* para chamar a função strcmp */
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

int killpg(pid_t pgrp, int signal);
int setpgrp(void);

#define FALSE        0
#define TRUE         1
#define MAX_NAME     256   /* tamanho maximo para nome de arquivo */
#define MAX_DIM      512   /* dimensao maxima para matrizes */
#define MAX_LINE     256   /* comprimento maximo de linha da entrada */
#define TMPFILE_NAME "temp.pgm"

int read_image_file(int *m, int *n, int *maxval, int a[][MAX_DIM]);
int write_tmpfile(int m, int n, int maxval, int a[][MAX_DIM]);
void horizontal_flip(int m, int n, int a[][MAX_DIM]);
int run_viewer_and_get_option(char command[], char message[]);
int get_option(char message[]);
void copy(int m, int n, int a[][MAX_DIM], int b[][MAX_DIM]);

int main() 
{
    char menu[] = 
        "\n"
        "Opcões:\n"
        "  0 - ler nova imagem\n"
        "  1 - inversão horizontal\n"
        "  ... (demais opções não implementadas) \n"
        "  9 - sair\n"
        "\n"
        "Escolha uma opção: ";

    /* Comando para visualização de imagem. Este comando usa o visualizador 
       eog ("Eye of Gnome"), para Linux. Outra alternativa é o kview, também
       para Linux. */  
    char image_viewer_cmd[] = "eog " TMPFILE_NAME; 

    int option;
    int  m, n, maxval, a[MAX_DIM][MAX_DIM];

    while (!(read_image_file(&m, &n, &maxval, a) 
             && write_tmpfile(m, n, maxval, a))) 
        ;
    option = run_viewer_and_get_option(image_viewer_cmd, menu);
    do {
        switch (option) {
            
        case 0:
            if (read_image_file(&m, &n, &maxval, a) 
                && write_tmpfile(m, n, maxval, a))
                option = run_viewer_and_get_option(image_viewer_cmd, menu); 
            else
                option = get_option(menu);
            break;
        case 1:
            horizontal_flip(m, n, a);
            if (write_tmpfile(m, n, maxval, a))
                option = run_viewer_and_get_option(image_viewer_cmd, menu); 
            else
                option = get_option(menu);
            break;
        case 9:
            break;
        default:
            printf("Opção inválida\n");
            option = get_option(menu);
            break;
        }
    } while (option != 9);
    remove(TMPFILE_NAME);
    return 0;
}

int read_image_file(int *m, int *n, int *maxval, int a[][MAX_DIM])
{
    char line[MAX_LINE];
    char fname[MAX_NAME];
    FILE *entrada;
    int i, j, aux;
    char key[128];

    do {
        printf("\nDigite o nome do arquivo de entrada: ");
        fgets(line, MAX_LINE, stdin);
    } while (sscanf(line, "%s", fname) != 1);
    
    /* abre arquivo para leitura */
    entrada = fopen(fname, "r");
    if (entrada == NULL) {
        printf("Erro na abertura do arquivo %s\n", fname);
        return FALSE;
    }
    /* le dados do cabecalho */
    aux = fscanf(entrada, "%s", key);
    if (aux != 1) {
        printf("Erro na leitura do arquivo %s\n", fname);
        fclose(entrada);
        return FALSE;
    }
    if (strcmp(key, "P2") != 0) {
        printf("Formato desconhecido\n");
        fclose(entrada);
        return FALSE;
    }
    aux = fscanf(entrada, "%d %d %d ", n, m, maxval);
    if (aux != 3) {
        printf("Formato incorreto\n");
        fclose(entrada);
        return FALSE;
    }
    /* le a matriz (imagem) que se segue */
    for (i = 0; i < *m; i++) {
        for (j = 0; j < *n; j++) {
            fscanf(entrada, "%d ", &a[i][j]);
        }
    }
    fclose(entrada);   /* fecha arquivo */
    return TRUE;
}

int write_tmpfile(int m, int n, int maxval, int a[][MAX_DIM])
{
    FILE *saida;
    int i, j;

    /* abre arquivo para escrita */
    saida = fopen(TMPFILE_NAME, "w");
    if (saida == NULL) {
        printf("Erro na abertura do arquivo %s\n", TMPFILE_NAME);
        return FALSE;
    }
    
    fprintf(saida, "P2\n");
    fprintf(saida, "%d %d\n", n, m);
    fprintf(saida, "%d\n", maxval);
    
    for (i = 0; i < m; i++) {
        for (j = 0; j < n; j++) {
            fprintf(saida, "%d\n", a[i][j]);
        }
    }
    if (fclose(saida) != 0)
        return FALSE;
    return TRUE;
}
    
void horizontal_flip(int m, int n, int a[][MAX_DIM])
{
    int i, j;
    int b[MAX_DIM][MAX_DIM];

    for (i = 0; i < m; i++) {
        for (j = 0; j < n; j++) {
            b[i][j] = a[i][n - j - 1];
        }
    }
    copy(m, n, b, a);
}

int run_viewer_and_get_option(char command[], char message[])
{
    pid_t pid;
    int retval;

    if ((pid = fork()) == 0) {
        setpgrp();
        system(command);
        exit(0);
    }
    retval = get_option(message);
    killpg(pid, SIGQUIT);
    return retval;
}

int get_option(char message[])
{
    char line[MAX_LINE];
    int retval;

    do {
        printf(message);
        fgets(line, MAX_LINE, stdin);
    } while (sscanf(line, "%d", &retval) != 1);
    return retval;
}

void copy(int m, int n, int a[][MAX_DIM], int b[][MAX_DIM])
{       
    int i, j;

    for (i = 0; i < m; i++) {
        for (j = 0; j < n; j++) {
            b[i][j] = a[i][j];
        }
    }
}
