/****************************************************************************/
/* 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 <stdlib.h>
#include <stdio.h>

#ifndef ubyte
typedef unsigned char ubyte;
#endif

#define CM2IN 0.3937

void writeEPSheader(FILE *out, char *data, char key, int ww, int hh, float cm, float dpi) {
  fprintf(out, "%%!PS-Adobe-2.0 EPSF-2.0\n");
  fprintf(out, "%%LanguageLevel: 1\n");
  fprintf(out, "%%%%Title: %s\n", data);
  fprintf(out, "%%%%Creator: medit -  (C) INRIA-Rocquencourt, 1999-2001\n");
  fprintf(out, "%%%%BoundingBox: 50 50 %d %d\n", (int)(ww * 72. / dpi + 50.5),
          (int)(hh * 72.0 / dpi + 50.5));
  fprintf(out, "%%%%Pages: (atend)\n");
  fprintf(out, "%%DocumentFonts:\n");
  fprintf(out, "%%%%EndComments\n");
  fprintf(out, "%%%%EndProlog\n");
  fprintf(out, "\n%%%%Page: 1 1\n");
  fprintf(out, "\n%% remember original state\n");
  fprintf(out, "/origstate save def\n");
  fprintf(out, "\n%% build a temporary dictionary\n");
  fprintf(out, "20 dict begin\n");

  fprintf(out, "\n%% define space for color conversions\n");
  fprintf(out, "/grays %d string def  %% space for gray scale line\n", ww);
  fprintf(out, "/npixls 0 def\n");
  fprintf(out, "/rgbindx 0 def\n");
  fprintf(out, "\n%% lower left corner\n");
  fprintf(out, "50 50 translate\n");

  fprintf(out, "\n%% size of image (on paper, in 1/72inch coords)\n");
  fprintf(out, "%g %g scale\n", 72.0 * CM2IN * cm, 72.0 * CM2IN * cm * (float)hh / ww);
  fprintf(out, "\n%% define string to hold a scanline's worth of data\n");

  /* write BW image */
  if (key == 'B') {
    int wpad;

    wpad = (ww + 7) & ~7;
    fprintf(out, "/pix %d string def\n", wpad / 8);
    fprintf(out, "\n%% dimensions of data\n");
    fprintf(out, "%d %d 1\n", ww, hh);
    fprintf(out, "\n%% mapping matrix\n");
    fprintf(out, "[%d 0 0 %d 0 %d]\n", ww, -hh, hh);
    fprintf(out, "\n{currentfile pix readhexstring pop}\n");
    fprintf(out, "image\n");
  }
  /* write greyscale image */
  else if (key == 'G') {
    fprintf(out, "/pix %d string def\n", ww);
    fprintf(out, "\n%% dimensions of data\n");
    fprintf(out, "%d %d 8\n", ww, hh);
    fprintf(out, "\n%% mapping matrix\n");
    fprintf(out, "[%d 0 0 %d 0 %d]\n", ww, -hh, hh);
    fprintf(out, "\n{currentfile pix readhexstring pop}\n");
    fprintf(out, "image\n");
  }
  /* color image */
  else if (key == 'C') {
    fprintf(out, "/pix %d string def\n", 3 * ww);

    fprintf(out, "\n%% dimensions of data\n");
    fprintf(out, "%d %d 8\n", ww, hh);
    fprintf(out, "\n%% mapping matrix\n");
    fprintf(out, "[%d 0 0 %d 0 %d]\n", ww, -hh, hh);

    fprintf(out, "\n%% define 'colorimage' if it isn't defined\n");
    fprintf(out, "/colorimage where   %% do we know about 'colorimage'?\n");
    fprintf(out, "  { pop }           %% yes: pop off the 'dict' returned\n");
    fprintf(out, "  {                 %% no:  define one\n");
    fprintf(out, "    /colortogray {  %% define an RGB->I function\n");
    fprintf(out, "      /rgbdata exch store    %% call input 'rgbdata'\n");
    fprintf(out, "      rgbdata length 3 idiv\n");
    fprintf(out, "      /npixls exch store\n");
    fprintf(out, "      /rgbindx 0 store\n");
    fprintf(out, "      0 1 npixls 1 sub {\n");
    fprintf(out, "        grays exch\n");
    fprintf(out, "        rgbdata rgbindx       get 20 mul    %% Red\n");
    fprintf(out, "        rgbdata rgbindx 1 add get 32 mul    %% Green\n");
    fprintf(out, "        rgbdata rgbindx 2 add get 12 mul    %% Blue\n");
    fprintf(out, "        add add 64 idiv     %% I = .5G + .31R + .18B\n");
    fprintf(out, "        put\n");
    fprintf(out, "       /rgbindx rgbindx 3 add store\n");
    fprintf(out, "      } for\n");
    fprintf(out, "      grays 0 npixls getinterval\n");
    fprintf(out, "    } bind def\n");

    fprintf(out, "\n    %% Utility procedure for colorimage operator.\n");
    fprintf(out, "    %% This procedure takes two procedures off the\n");
    fprintf(out, "    %% stack and merges them into a single procedure.\n");

    fprintf(out, "\n    /mergeprocs { %% def\n");
    fprintf(out, "      dup length\n");
    fprintf(out, "      3 -1 roll\n");
    fprintf(out, "      dup\n");
    fprintf(out, "      length\n");
    fprintf(out, "     dup\n");
    fprintf(out, "      5 1 roll\n");
    fprintf(out, "      3 -1 roll\n");
    fprintf(out, "      add\n");
    fprintf(out, "      array cvx\n");
    fprintf(out, "      dup\n");
    fprintf(out, "      3 -1 roll\n");
    fprintf(out, "      0 exch\n");
    fprintf(out, "      putinterval\n");
    fprintf(out, "      dup\n");
    fprintf(out, "      4 2 roll\n");
    fprintf(out, "      putinterval\n");
    fprintf(out, "    } bind def\n");

    fprintf(out, "    /colorimage { %% def\n");
    fprintf(out, "      pop pop     %% remove 'false 3' operands\n");
    fprintf(out, "      {colortogray} mergeprocs\n");
    fprintf(out, "      image\n");
    fprintf(out, "    } bind def\n");
    fprintf(out, "  } ifelse          %% end of 'false' case\n");
    fprintf(out, "\n{currentfile pix readhexstring pop}\n");
    fprintf(out, "false 3 colorimage\n\n");
  }
}

void writeEPStrailer(FILE *out) {
  fprintf(out, "\nshowpage\n");
  fprintf(out, "\n%% stop using temporary dictionary\n");
  fprintf(out, "end\n");
  fprintf(out, "\n%% restore original state\n");
  fprintf(out, "origstate restore\n");
  fprintf(out, "\n%%%%Trailer\n");
}

void writeEPSRow(FILE *out, char key, ubyte *buffer, int size, ubyte bckbyt) {
  int c, k, l;
  ubyte byte;

  l = 0;

  switch (key) {
    case 'B': /* black & white */
      byte = 0x0;
      c = 0;

      for (k = 0; k < 3 * size; k += 3) {
        ubyte bbyte;

        bbyte = (ubyte)(0.30 * buffer[k] + 0.59 * buffer[k + 1] + 0.11 * buffer[k + 2] + 0.5);
        if (bbyte == bckbyt) byte |= (1 << (7 - c));

        if (++c == 8) {
          fprintf(out, "%.2x", byte);
          if (++l == 36) {
            fprintf(out, "\n");
            l = 0;
          }

          byte = 0x0;
          c = 0;
        }
      }

      /* padding */
      if (c) {
        for (l = 8; l > c; l--) {
          byte |= (1 << l);
        }

        fprintf(out, "%.2x", byte);
      }

      break;
    case 'G': /* greyscale */

      for (k = 0; k < 3 * size; k += 3) {
        byte = (ubyte)(0.30 * buffer[k] + 0.59 * buffer[k + 1] + 0.11 * buffer[k + 2]);
        fprintf(out, "%.2x", byte);
        if (++l == 36) {
          fprintf(out, "\n");
          l = 0;
        }
      }

      break;
    case 'C': /* color scale */

      for (k = 0; k < 3 * size; k++) {
        fprintf(out, "%.2x", buffer[k]);
        if (++l == 36) {
          fprintf(out, "\n");
          l = 0;
        }
      }

      break;
  }

  fprintf(out, "\n");
}

#ifdef __cplusplus
}
#endif
