#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <errno.h>

void 
unexpected_eof () {
  fprintf (stderr, "Unexpected end of file.\n");
  exit (EXIT_FAILURE);
}

unsigned char 
xget_char (FILE* f) {
  int read_c = fgetc (f);

  if (read_c == EOF) 
    unexpected_eof ();

  return (unsigned char)read_c;
}

void
unexpected_char (unsigned char c) {
  fprintf (stderr, "Unexpected char: %c\n", c);
  exit (EXIT_FAILURE);
}

void 
accept_char (FILE* f, unsigned char c) {
  unsigned char read_c = xget_char (f);

  if (read_c != c) 
    unexpected_char (read_c);

  return;
}

void 
accept_whitespace (FILE* f) {
  unsigned char read_c = xget_char (f);

  switch (read_c) {

  case ' ': case '\t': case '\n':
    return;

  default:
    unexpected_char (read_c);
  }
  return;
}

int 
read_int (FILE* f) {
  int read_i = 0;
  if (fscanf (f, "%d", &read_i) == EOF) 
    unexpected_eof ();
  return read_i;
}

int 
read_point_as_byte (FILE* f) {
  unsigned char r = xget_char (f);
  unsigned char g = xget_char (f);
  unsigned char b = xget_char (f);
  return /*((int)r << 16) + ((int)g << 8) +*/ (int)b;
}

void 
charge_image_ppm (const char* filename, int width, int height, int* image) {
  FILE* f = fopen (filename, "r");
  int i, rwidth, rheight, max_val;

  // L'ouverture du fichier a pu échouer, dans ce cas, f == NULL.
  // On utilise les fonctions standards du système pour afficher
  // l'erreur correspondante.
  if (f == NULL) {
    perror ((const char*)strerror (errno));
    exit (EXIT_FAILURE);
  }
  
  // Sinon, le fichier a bien été ouvert. On va pouvoir l'analyser.
  // On suit le format décrit dans "man ppm".
  // Il commence par une séquence qui caractérise les fichiers ppm.
  // (magic number).
  accept_char (f, 'P');
  accept_char (f, '6');
  // Suivi d'un espace.
  accept_whitespace (f);
  // La largeur.
  rwidth = read_int (f);
  accept_whitespace (f);
  // La hauteur.
  rheight = read_int (f);
  accept_whitespace (f);

  // On verifie la correspondance des tailles.
  if (rwidth != width || rheight != height) {
    fprintf (stderr, "Dimensions invalides (%d, %d) <> (%d, %d).\n",
	     rwidth, rheight, width, height);
    exit (EXIT_FAILURE);
  }

  // La valeur maximale des couleurs. 
  max_val = read_int (f);
  accept_whitespace (f);

  // Determine le nombre d'octets pour chaque couleur.
  // Cette fonction ne gère que les max_val <= 255. 
  if (max_val > 255) {
    fprintf (stderr, "This format is not handled.\n");
    exit (EXIT_FAILURE);
  }

  // Lit les donnees.
  for (i = 0; i < rwidth * rheight; ++i) 
    image[i] = read_point_as_byte (f);

  // Ferme le fichier.
  fclose (f);
}

void
enregistre_image_ppm (const char* filename, int width, int height, const int* image) {
  int i, j, color;
  FILE* f;

  f = fopen (filename, "w");

  if (f == NULL) {
    perror ((const char*)strerror (errno));
    exit (EXIT_FAILURE);
  }

  // Tout d'abord, on stocke les informations descriptives du fichier.
  fputs ("P6\n", f);
  fprintf (f, "%d %d\n255\n", width, height);

  for (j = 0; j < height; ++j) 
    for (i = 0; i < width; ++i) {
      color = image[j * width + i];
      fputc (color /*>> 16*/, f);
      fputc (/*(*/color /*>> 8) % 256*/, f);
      fputc(color /*% 256*/, f);
    }

  fclose (f);
}

