/****************************************************************************/
/* This file is part of FreeFEM.                                            */
/*                                                                          */
/* FreeFEM is free software: you can redistribute it and/or modify          */
/* it under the terms of the GNU Lesser General Public License as           */
/* published by the Free Software Foundation, either version 3 of           */
/* the License, or (at your option) any later version.                      */
/*                                                                          */
/* FreeFEM is distributed in the hope that it will be useful,               */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of           */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
/* GNU Lesser General Public License for more details.                      */
/*                                                                          */
/* You should have received a copy of the GNU Lesser General Public License */
/* along with FreeFEM. If not, see <http://www.gnu.org/licenses/>.          */
/****************************************************************************/
/* SUMMARY : ...                                                            */
/* LICENSE : LGPLv3                                                         */
/* ORG     : LJLL Universite Pierre et Marie Curie, Paris, FRANCE           */
/* AUTHORS : Pascal Frey                                                    */
/* E-MAIL  : pascal.frey@sorbonne-universite.fr                             */

#ifdef __cplusplus
extern "C" {
#endif

#include "medit.h"
#include "sproto.h"
#include "extern.h"

PPMimage *loadPPM(const char *imgname, int *type) {
  pPPMimage result;
  FILE *fp;
  int i, k, typimg, ret, r, g, b, s, maxval, bitsize;
  char *ptr, c, buff[1024];

  /* search for image */
  ptr = strstr(imgname, ".ppm");
  if (!ptr) {
    char data[256];

    strcpy(data, imgname);
    strcat(data, ".ppm");
    fp = fopen(data, "rb");
  } else {
    fp = fopen(imgname, "rb");
  }

  if (!fp) {
    fprintf(stderr, "  ## Unable to open file %s.\n", imgname);
    return (0);
  }

  if (!fgets(buff, sizeof(buff), fp)) {
    fprintf(stderr, "  ## Invalid file header.\n");
    fclose(fp);
    return (0);
  }

  /* check header file */
  if (buff[0] != 'P') {
    fprintf(stderr, "  ## Invalid image format.\n");
    fclose(fp);
    return (0);
  }

  switch (buff[1]) {
    case '2':
      typimg = P2;
      break;
    case '3':
      typimg = P3;
      break;
    case '5':
      typimg = P5;
      break;
    case '6':
      typimg = P6;
      break;
    default:
      fprintf(stderr, "  ## Invalid image format.\n");
      fclose(fp);
      return (0);
  }

  /* allocate memory to store imagee */
  result = malloc(sizeof(PPMimage));
  if (!result) {
    fprintf(stderr, "  ## Unable to load image.\n");
    fclose(fp);
    return (0);
  }

  do {
    ret = fscanf(fp, "%1023s", buff);
    if (ret == EOF) break;

    /* check and strip comments */
    if (buff[0] == '#') do {
        c = getc(fp);
      } while (c != '\n');

    else
      break;
  } while (1);

  /* read columns + lines */
  ret = sscanf(buff, "%d", &s);
  result->sizeX = (short)s;
  ret += fscanf(fp, "%d", &s);
  result->sizeY = (short)s;
  if (ret != 2) {
    fprintf(stderr, "  ## Error loading image.\n");
    free(result);
    fclose(fp);
    return (0);
  }

  if (!quiet) fprintf(stdout, "  image size:   %d x %d\n", result->sizeX, result->sizeY);

  if (fscanf(fp, "%d", &maxval) != 1) {
    fprintf(stderr, "  ## Invalid image size.\n");
    free(result);
    fclose(fp);
    return (0);
  }

  /* strip line */
  while (fgetc(fp) != '\n') {
    ;
  }

  /* size based on type */
  if (typimg == P2 || typimg == P5)
    bitsize = result->sizeX * result->sizeY;
  else
    bitsize = 3 * result->sizeX * result->sizeY;

  result->data = (ubyte *)malloc(bitsize * sizeof(ubyte));
  if (!result->data) {
    fprintf(stderr, "  ## Unable to load image.\n");
    free(result);
    fclose(fp);
    return (0);
  }

  /* read data file */
  switch (typimg) {
    case P2: /* ascii file (grey)  */
    case P3: /* ascii file (color) */

      for (i = 0; i < bitsize; i++) {
        int res = fscanf(fp, "%d", &r);
        if (res == EOF) printf("fscanf error\n");
        result->data[i] = (ubyte)r;
      }

      break;

    case P5: /* binary file (grey) */
    case P6: /* binary file (color) */
      ret = fread(result->data, sizeof(ubyte), bitsize, fp);
      if (ret != bitsize) {
        fprintf(stderr, "  ## Error loading image.\n");
        free(result->data);
        free(result);
        fclose(fp);
        return (0);
      }

      break;
  }

  fclose(fp);

  if (*type == DEFAULT) {
    switch (typimg) {
      case P2:
      case P5:
        *type = GREY;
        break;
      case P3:
      case P6:
        *type = RGB;
        break;
    }
  }
  /* convert to grey levels */
  else if (*type == GREY && (typimg == P3 || typimg == P6)) {
    fprintf(stdout, "  converting to grey levels\n");

    for (i = 0, k = 0; i < bitsize; i += 3, k++) {
      r = (int)result->data[i];
      g = (int)result->data[i + 1];
      b = (int)result->data[i + 2];
      result->data[k] = (ubyte)(0.3 * r + 0.59 * g + 0.11 * b);
    }

    result->data = (ubyte *)realloc(result->data, sizeof(ubyte) * bitsize / 3);
  }

  return (result);
}

int savePPM(const char *imgname, pPPMimage img, int typimg) {
  FILE *out;
  int i, c, bitsize;
  char *ptr, data[512];

  strcpy(data, imgname);
  ptr = (char *)strstr(data, ".ppm");
  if (!ptr) strcat(data, ".ppm");

  out = fopen(data, "w");
  if (!out) {
    fprintf(stderr, "  ## Unable to open file %s.\n", data);
    exit(1);
  }

  bitsize = img->sizeX * img->sizeY;

  switch (typimg) {
    case P2:
      fprintf(out, "P2\n");
      fprintf(out, "# Created using medit %s %s, (c) INRIA\n", ME_VER, ME_REL);
      fprintf(out, "%d %d\n", img->sizeX, img->sizeY);
      fprintf(out, "255\n");
      c = 0;

      for (i = 0; i < img->sizeX * img->sizeY; i++) {
        fprintf(out, "%3d ", (int)img->data[i]);
        if (++c == 17) {
          c = 0;
          fprintf(out, "\n");
        }
      }

      fprintf(out, "\n");
      break;
    case P5:
      fprintf(out, "P5\n");
      fprintf(out, "# Created using medit %s %s, (c) INRIA\n", ME_VER, ME_REL);
      fprintf(out, "%d %d\n", img->sizeX, img->sizeY);
      fprintf(out, "255\n");
      fwrite(img->data, sizeof(ubyte), bitsize, out);
      break;
    case P6:
      fprintf(out, "P6\n");
      fprintf(out, "# Created using medit %s %s, (c) INRIA\n", ME_VER, ME_REL);
      fprintf(out, "%d %d\n", img->sizeX, img->sizeY);
      fprintf(out, "255\n");
      bitsize = (img->sizeX * 24 + 7) / 8 * img->sizeY;
      if (fwrite(img->data, sizeof(ubyte), bitsize, out) < bitsize)
        fprintf(stderr, "  ## Data file corrupted.\n");

      break;
  }

  fclose(out);

  return (1);
}

int saveTGA(const char *imgname, GLubyte *img, int w, int h) {
  TGAheader tga;
  int i;
  char *ptr, data[256];
  FILE *out;

  strcpy(data, imgname);
  ptr = (char *)strstr(data, ".tga");
  if (!ptr) strcat(data, ".tga");

  out = fopen(data, "wb");
  if (!out) {
    fprintf(stderr, "  ## UNABLE TO OPEN FILE %s.\n", data);
    exit(1);
  }

  tga.idfield_len = 0;
  tga.cmap_type = 0;
  tga.image_type = 2;

  for (i = 0; i < 5; i++) {
    tga.cmap_spec[i] = 0;
  }

  for (i = 0; i < 2; i++) {
    tga.x_orig[i] = 0;
    tga.y_orig[i] = 0;
  }

  /* Lo bits */
  tga.width[0] = w & 0xFF;
  /* Hi bits */
  tga.width[1] = (w >> 8) & 0xFF;
  tga.height[0] = h & 0xFF;
  tga.height[1] = (h >> 8) & 0xFF;
  tga.pixel_size = 24;
  tga.image_desc = 0;
  /* Output header */
  fwrite(&tga, sizeof(TGAheader), 1, out);

  /* Output image */
  fwrite(img, sizeof(unsigned char), w * h * 3, out);

  fclose(out);
  return (1);
}

void swapPixels(PPMimage *pixels) {
  GLubyte *row;
  int i, bits;

  bits = 3 * pixels->sizeX;
  row = (GLubyte *)malloc(bits * sizeof(GLubyte));
  if (!row) return;

  /* exchange rows */
  for (i = 0; i < pixels->sizeY / 2; i++) {
    int k, ck;

    k = 3 * i * pixels->sizeX;
    ck = 3 * (pixels->sizeY - i - 1) * pixels->sizeX;
    memcpy(row, &pixels->data[k], bits);
    memcpy(&pixels->data[k], &pixels->data[ck], bits);
    memcpy(&pixels->data[ck], row, bits);
  }

  free(row);
}

int imgHard(pScene sc, char *data, char key) {
  PPMimage *pixels;
  GLint viewport[4];
  pPersp p;
  int xx0, yy0, ww, hh;

  pixels = (PPMimage *)M_malloc(sizeof(PPMimage), "imgHard");
  if (!pixels) {
    fprintf(stderr, "  ## UNABLE TO ALLOCATE MEMORY FOR IMAGE.\n");
    return (0);
  }

  p = sc->persp;
  if (abs(p->rubfx - p->rubix) > 0) {
    ww = abs(p->rubfx - p->rubix) - 2;
    hh = abs(p->rubfy - p->rubiy) - 2;
    xx0 = min(p->rubix, p->rubfx) + 1;
    yy0 = min(p->rubiy, p->rubfy) + 1;
  } else {
    glGetIntegerv(GL_VIEWPORT, viewport);
    ww = viewport[2];
    hh = viewport[3];
    xx0 = 0;
    yy0 = 0;
  }

  /* align to 8 bytes */
  ww = ww & ~7;
  hh = hh & ~7;

  pixels->sizeX = (short)ww;
  pixels->sizeY = (short)hh;
  pixels->data = (ubyte *)M_calloc(3 * ww * hh, sizeof(ubyte), "imgHard.data");
  if (!pixels->data) {
    fprintf(stderr, "  ## Not enough memory to save image.\n");
    M_free(pixels);
    return (0);
  }

  if (ddebug) fprintf(stdout, "size %d x %d\n", ww, hh);

  glFinish( );
  if (saveimg)
    glReadBuffer(GL_BACK_LEFT);
  else
    glReadBuffer(GL_FRONT);

  glPixelStorei(GL_PACK_ALIGNMENT, 4);
  glPixelStorei(GL_PACK_ROW_LENGTH, 0);
  glPixelStorei(GL_PACK_SKIP_ROWS, 0);
  glPixelStorei(GL_PACK_SKIP_PIXELS, 0);

  glReadPixels(xx0, yy0, ww, hh, GL_RGB, GL_UNSIGNED_BYTE, pixels->data);
  if (glGetError( ) != GL_NO_ERROR) {
    fprintf(stderr, "  ## Unable to save image\n");
    M_free(pixels->data);
    M_free(pixels);
    return (0);
  }

  if (key == 'H') {
    swapPixels(pixels);
    savePPM(data, pixels, P6);
  } else if (key == 'T') {
    saveTGA(data, pixels->data, ww, hh);
  }

  M_free(pixels->data);
  M_free(pixels);
  return (1);
}

#ifdef __cplusplus
}
#endif
