/* This file is part of 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 <http://www.gnu.org/licenses/>.
 *
 * Copyright 2003-2007 Calvin Williamson, Øyvind Kolås.
 */

#include "config.h"

#include <string.h>
#include <glib-object.h>

#include "gegl.h"
#include "gegl-types-internal.h"
#include "gegl-utils.h"
#include "gegl-types-internal.h"


gint
_gegl_float_epsilon_zero (float value)
{
  return value > -GEGL_FLOAT_EPSILON && value < GEGL_FLOAT_EPSILON;
}

gint
_gegl_float_epsilon_equal (float v1, float v2)
{
  register float diff = v1 - v2;

  return diff > -GEGL_FLOAT_EPSILON && diff < GEGL_FLOAT_EPSILON;
}

void
gegl_rectangle_set (GeglRectangle *r,
                    gint           x,
                    gint           y,
                    guint          w,
                    guint          h)
{
  r->x      = x;
  r->y      = y;
  r->width  = w;
  r->height = h;
}

void
gegl_rectangle_bounding_box (GeglRectangle       *dest,
                             const GeglRectangle *src1,
                             const GeglRectangle *src2)
{
  gboolean s1_has_area = src1->width && src1->height;
  gboolean s2_has_area = src2->width && src2->height;

  if (!s1_has_area && !s2_has_area)
    gegl_rectangle_set (dest, 0, 0, 0, 0);
  else if (!s1_has_area)
    gegl_rectangle_copy (dest, src2);
  else if (!s2_has_area)
    gegl_rectangle_copy (dest, src1);
  else
    {
      gint x1 = MIN (src1->x, src2->x);
      gint x2 = MAX (src1->x + src1->width, src2->x + src2->width);
      gint y1 = MIN (src1->y, src2->y);
      gint y2 = MAX (src1->y + src1->height, src2->y + src2->height);

      dest->x      = x1;
      dest->y      = y1;
      dest->width  = x2 - x1;
      dest->height = y2 - y1;
    }
}

gboolean
gegl_rectangle_intersect (GeglRectangle       *dest,
                          const GeglRectangle *src1,
                          const GeglRectangle *src2)
{
  gint x1, x2, y1, y2;

  x1 = MAX (src1->x, src2->x);
  x2 = MIN (src1->x + src1->width, src2->x + src2->width);

  if (x2 <= x1)
    {
      if (dest)
        gegl_rectangle_set (dest, 0, 0, 0, 0);
      return FALSE;
    }

  y1 = MAX (src1->y, src2->y);
  y2 = MIN (src1->y + src1->height, src2->y + src2->height);

  if (y2 <= y1)
    {
      if (dest)
        gegl_rectangle_set (dest, 0, 0, 0, 0);
      return FALSE;
    }

  if (dest)
    gegl_rectangle_set (dest, x1, y1, x2 - x1, y2 - y1);
  return TRUE;
}

void
gegl_rectangle_copy (GeglRectangle       *to,
                     const GeglRectangle *from)
{
  to->x      = from->x;
  to->y      = from->y;
  to->width  = from->width;
  to->height = from->height;
}

gboolean
gegl_rectangle_contains (const GeglRectangle *r,
                         const GeglRectangle *s)
{
  g_return_val_if_fail (r && s, FALSE);

  if (s->x >= r->x &&
      s->y >= r->y &&
      (s->x + s->width) <= (r->x + r->width) &&
      (s->y + s->height) <= (r->y + r->height))
    return TRUE;
  else
    return FALSE;
}

gboolean
gegl_rectangle_equal (const GeglRectangle *r,
                      const GeglRectangle *s)
{
  g_return_val_if_fail (r && s, FALSE);

  if (r->x == s->x &&
      r->y == s->y &&
      r->width == s->width &&
      r->height == s->height)
    return TRUE;
  else
    return FALSE;
}

gboolean
gegl_rectangle_equal_coords (const GeglRectangle *r,
                             gint                 x,
                             gint                 y,
                             gint                 w,
                             gint                 h)
{
  g_return_val_if_fail (r, FALSE);

  if (r->x == x &&
      r->y == y &&
      r->width == w &&
      r->height == h)
    return TRUE;
  else
    return FALSE;
}

gboolean
gegl_rectangle_is_empty (const GeglRectangle *r)
{
  g_return_val_if_fail (r != NULL, FALSE);
  return r->width == 0 && r->height == 0;
}

static GeglRectangle *
gegl_rectangle_dup (const GeglRectangle *rectangle)
{
  GeglRectangle *result = g_new (GeglRectangle, 1);

  *result = *rectangle;

  return result;
}

GeglRectangle
gegl_rectangle_infinite_plane (void)
{
  GeglRectangle infinite_plane_rect = {G_MININT / 2, G_MININT / 2, G_MAXINT, G_MAXINT};
  return infinite_plane_rect;
}

gboolean
gegl_rectangle_is_infinite_plane (const GeglRectangle *rectangle)
{
  return (rectangle->x      == G_MININT / 2 &&
          rectangle->y      == G_MININT / 2 &&
          rectangle->width  == G_MAXINT     &&
          rectangle->height == G_MAXINT);
}

void
gegl_rectangle_dump (const GeglRectangle *rectangle)
{
  g_print ("%d, %d, %d×%d\n",
           rectangle->x,
           rectangle->y,
           rectangle->width,
           rectangle->height);
}

GType
gegl_rectangle_get_type (void)
{
  static GType our_type = 0;

  if (our_type == 0)
    our_type = g_boxed_type_register_static (g_intern_static_string ("GeglRectangle"),
                                             (GBoxedCopyFunc) gegl_rectangle_dup,
                                             (GBoxedFreeFunc) g_free);
  return our_type;
}

#define GEGL_ALIGN 16

gpointer
gegl_malloc (gsize size);

/* utility call that makes sure allocations are 16 byte aligned.
 * making RGBA float buffers have aligned access for pixels.
 */
gpointer gegl_malloc (gsize size)
{
  gchar *mem;
  gchar *ret;
  gint   offset;

  mem    = g_malloc (size + GEGL_ALIGN + sizeof(gpointer));
  offset = GEGL_ALIGN - (GPOINTER_TO_UINT(mem) + sizeof(gpointer)) % GEGL_ALIGN;
  ret    = (gpointer)(mem + sizeof(gpointer) + offset);

  /* store the real malloc one pointer in front of this malloc */
  *(gpointer*)(ret-sizeof(gpointer))=mem;
  return (gpointer) ret;
}

gpointer gegl_calloc (gsize size, int n_memb)
{
  gchar *ret = gegl_malloc (size * n_memb);
  memset (ret, 0, size * n_memb);
  return ret;
}

void
gegl_free (gpointer buf);
void
gegl_free (gpointer buf)
{
  g_assert (buf);
  g_free (*((gpointer*)buf -1));
}
