/* This file is an image processing operation for GEGL
 *
 * GEGL 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.
 *
 * GEGL 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 GEGL; if not, see <https://www.gnu.org/licenses/>.
 *
 * Copyright 2009 Henrik Akesson <h.m.akesson (a) gmail.com>
 */

#include "config.h"
#include <glib/gi18n-lib.h>


#ifdef GEGL_PROPERTIES

property_file_path  (path, _("File"), "")
    description     (_("Target path and filename, use '-' for stdout."))

property_boolean (rawformat, _("Raw format"), TRUE)
property_int     (bitdepth, _("Bitdepth"), 16)
    description (_("8 and 16 are the currently accepted values."))
    value_range (8, 16)

#else

#define GEGL_OP_SINK
#define GEGL_OP_NAME ppm_save
#define GEGL_OP_C_SOURCE ppm-save.c

#define CHANNEL_COUNT           3

#include "gegl-op.h"
#include <stdio.h>

typedef enum {
  PIXMAP_ASCII  = 51,
  PIXMAP_RAW    = 54,
} map_type;

static void
ppm_save_write(FILE    *fp,
               gint     width,
               gint     height,
               gsize    numsamples,
               gsize    bpc,
               guchar  *data,
               map_type type)
{
  guint i;

  /* Write the header */
  fprintf (fp, "P%c\n%d %d\n", type, width, height );
  fprintf (fp, "%d\n", (bpc == sizeof (guchar)) ? 255 : 65535);

  /* Raw images writes the data in binary form */
  if (type == PIXMAP_RAW)
    {
      /* Fix endianness if necessary */
      if (bpc > 1)
        {
          gushort *ptr = (gushort *) data;

          for (i = 0; i < numsamples; i++)
            {
              *ptr = GUINT16_TO_BE (*ptr);
              ptr++;
            }
        }

      fwrite (data, bpc, numsamples, fp);
    }
  else
    {
      /* Plain PPM format */

      if (bpc == sizeof (guchar))
        {
          guchar *ptr = data;

          for (i = 0; i < numsamples; i++)
            {
              fprintf (fp, "%u ", (unsigned int) *ptr++);
              if ((i + 1) % (width * CHANNEL_COUNT) == 0)
                fprintf (fp, "\n");
            }
        }
      else if (bpc == sizeof (gushort))
        {
          gushort *ptr = (gushort *) data;

          for (i = 0; i < numsamples; i++)
            {
              fprintf (fp, "%u ", (unsigned int) *ptr++);
              if ((i + 1) % (width * CHANNEL_COUNT) == 0)
                fprintf (fp, "\n");
            }
        }
      else
        {
          g_warning ("%s: Programmer stupidity error", G_STRLOC);
        }
    }
}

static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         const GeglRectangle *rect,
         gint                 level)
{
  GeglProperties *o = GEGL_PROPERTIES (operation);

  FILE     *fp;
  guchar   *data;
  map_type  type;
  gsize     bpc;
  gsize     numsamples;
  gboolean  ret = FALSE;

#ifndef _UCRT
  fp = (!strcmp (o->path, "-") ? stdout : fopen(o->path, "wb") );
#else
  if (!strcmp (o->path, "-"))
    {
      fp = stdout;
    }
  else if (fopen_s (&fp, o->path, "wb") != 0)
    {
      fp = NULL;
    }
#endif

  if (!fp)
    return FALSE;

  if ((o->bitdepth != 8) && (o->bitdepth != 16))
    {
      g_warning ("Bitdepths of 8 and 16 are only accepted currently.");
      goto out;
    }

  type = (o->rawformat ? PIXMAP_RAW : PIXMAP_ASCII);
  bpc = (o->bitdepth == 8) ? (sizeof (guchar)) : (sizeof (gushort));
  numsamples = rect->width * rect->height * CHANNEL_COUNT;

  data = g_malloc (numsamples * bpc);

  switch (bpc)
    {
    case 1:
      gegl_buffer_get (input, rect, 1.0, babl_format ("R'G'B' u8"), data,
                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
      break;

    case 2:
      gegl_buffer_get (input, rect, 1.0, babl_format ("R'G'B' u16"), data,
                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
      break;

    default:
      g_warning ("%s: Programmer stupidity error", G_STRLOC);
    }

  ppm_save_write (fp, rect->width, rect->height, numsamples, bpc, data, type);

  g_free (data);

  ret = TRUE;

 out:
  if (fp != stdout)
    fclose( fp );

  return ret;
}


static void
gegl_op_class_init (GeglOpClass *klass)
{
  GeglOperationClass     *operation_class;
  GeglOperationSinkClass *sink_class;

  operation_class = GEGL_OPERATION_CLASS (klass);
  sink_class      = GEGL_OPERATION_SINK_CLASS (klass);

  sink_class->process = process;
  sink_class->needs_full = TRUE;

  gegl_operation_class_set_keys (operation_class,
    "name",        "gegl:ppm-save",
    "title",       _("PPM File Saver"),
    "categories",  "output",
    "description",
        _("PPM image saver (Portable pixmap saver.)"),
        NULL);

  gegl_operation_handlers_register_saver (
    ".ppm", "gegl:ppm-save");
}

#endif
