/* 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 2007 Øyvind Kolås <oeyvindk@hig.no>
 */

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


#ifdef GEGL_PROPERTIES

    /* No properties */

#else

#define GEGL_OP_COMPOSER
#define GEGL_OP_NAME hstack
#define GEGL_OP_C_SOURCE hstack.c

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

static void prepare (GeglOperation *operation)
{
  gegl_operation_set_format (operation, "output", babl_format ("RGBA float"));
}

static GeglRectangle
get_bounding_box (GeglOperation *operation)
{
  GeglRectangle  result = {0,0,0,0};
  GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (operation,
                                                                     "input");
  GeglRectangle *aux_rect = gegl_operation_source_get_bounding_box (operation,
                                                                      "aux");

  if (!in_rect || !aux_rect)
    return result;

  result = *in_rect;
  if (result.width  != 0 &&
      result.height != 0)
    {
      result.width += aux_rect->width;
      if (aux_rect->height > result.height)
        result.height = aux_rect->height;
    }

  return result;
}

static GeglRectangle
get_required_for_output (GeglOperation       *self,
                       const gchar         *input_pad,
                       const GeglRectangle *roi)
{
  GeglRectangle request = *roi;

  if (!strcmp (input_pad, "aux"))
    {
      GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (self,
                                                                         "input");
      GeglRectangle *aux_rect = gegl_operation_source_get_bounding_box (self,
                                                                         "aux");

      if (request.width != 0 &&
          request.height != 0)
        {
          request.x -= in_rect->width + aux_rect->x;
        }
    }

  return request;
}

static GeglRectangle
get_invalidated_by_change (GeglOperation       *self,
                           const gchar         *input_pad,
                           const GeglRectangle *region)
{
  if (!strcmp (input_pad, "input"))
    {
      return *region;
    }
  else
    {
      /*region.x -= radius * 2;*/
    }
  return *region;
}

static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *aux,
         GeglBuffer          *output,
         const GeglRectangle *result,
         gint                 level)
{
  GeglBuffer            *temp_in;
  GeglBuffer            *temp_aux;

  /* FIXME: just pass the originals buffers if the result rectangle does not
   * include both input buffers
   */

  if (gegl_rectangle_equal (result, gegl_buffer_get_extent (input)))
    temp_in = g_object_ref (input);
  else
    temp_in = gegl_buffer_create_sub_buffer (input, result);

  if (gegl_rectangle_equal (result, gegl_buffer_get_extent (aux)))
    temp_aux = g_object_ref (aux);
  else
    temp_aux = gegl_buffer_create_sub_buffer (aux, result);

    {
      gfloat *buf  = g_new0 (gfloat, result->width * result->height * 4);
      gfloat *bufB = g_new0 (gfloat, result->width * result->height * 4);

      gegl_buffer_get (temp_in,  NULL, 1.0, babl_format ("RGBA float"), buf,
                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
      gegl_buffer_get (temp_aux, NULL, 1.0, babl_format ("RGBA float"), bufB,
                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
        {
          gint offset=0;
          gint x,y;
          for (y=0;y<gegl_buffer_get_height (output);y++)
            for (x=0;x<gegl_buffer_get_width (output);x++)
              {
                if (x + result->x >= gegl_buffer_get_width (input))
                  {
                    buf[offset+0]=bufB[offset+0];
                    buf[offset+1]=bufB[offset+1];
                    buf[offset+2]=bufB[offset+2];
                    buf[offset+3]=bufB[offset+3];
                  }
                offset+=4;
              }
        }
      gegl_buffer_set (output, NULL, 0, babl_format ("RGBA float"), buf,
                       GEGL_AUTO_ROWSTRIDE);

      g_free (buf);
      g_free (bufB);
    }
  g_object_unref (temp_in);
  g_object_unref (temp_aux);

  return  TRUE;
}


static void
gegl_op_class_init (GeglOpClass *klass)
{
  GeglOperationClass         *operation_class;
  GeglOperationComposerClass *composer_class;

  operation_class = GEGL_OPERATION_CLASS (klass);
  composer_class  = GEGL_OPERATION_COMPOSER_CLASS (klass);

  composer_class->process = process;
  operation_class->prepare = prepare;
  operation_class->get_bounding_box = get_bounding_box;
  operation_class->get_invalidated_by_change = get_invalidated_by_change;
  operation_class->get_required_for_output = get_required_for_output;

  gegl_operation_class_set_keys (operation_class,
  "name"        , "gegl:hstack",
  "categories"  , "misc",
  "description" ,
        _("Horizontally stack inputs, (in \"output\" \"aux\" is placed to the right of \"input\")"),
        NULL);
}

#endif
