/* 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 2008 Øyvind Kolås
 */

#include "config.h"

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

#include "gegl.h"
#include "gegl-types-internal.h"
#include "gegl-operation-temporal.h"
#include "gegl-utils.h"
#include "graph/gegl-node.h"
#include "graph/gegl-connection.h"
#include "graph/gegl-pad.h"
#include "buffer/gegl-region.h"
#include "buffer/gegl-buffer.h"

struct _GeglOperationTemporalPrivate
{
  gint                count;
  gint                history_length;

  gint                width;
  gint                height;
  gint                next_to_write;
  GeglBuffer         *frame_store;
};

static void gegl_operation_temporal_prepare (GeglOperation *operation);

G_DEFINE_TYPE (GeglOperationTemporal,
               gegl_operation_temporal,
               GEGL_TYPE_OPERATION_FILTER)

#define GEGL_OPERATION_TEMPORAL_GET_PRIVATE(obj) \
  G_TYPE_INSTANCE_GET_PRIVATE (obj, GEGL_TYPE_OPERATION_TEMPORAL, GeglOperationTemporalPrivate)



GeglBuffer *
gegl_operation_temporal_get_frame (GeglOperation *op,
                                   gint           frame)
{
  GeglOperationTemporal *temporal= GEGL_OPERATION_TEMPORAL (op);
  GeglOperationTemporalPrivate *priv = temporal->priv;
  GeglBuffer   *buffer;

   if (frame > priv->count)
     {
       frame = (priv->count-1);
       if (frame<0)
         frame=0;
       g_print ("%i > priv->count(%i), using frame %i", frame, priv->count,
        frame);
     }
   else
     {
       frame = (priv->next_to_write - 1 + priv->history_length + frame) % priv->history_length;
       g_print ("using frame %i", frame);
     }
  /* build in shift */
  buffer = g_object_new (GEGL_TYPE_BUFFER,
                         "source",  priv->frame_store,
                         "shift-y", frame * priv->height,
                         "width", priv->width,
                         "height", priv->height,
                         "x", 0,
                         "y", 0,
                         NULL);
  return buffer;
}

static gboolean gegl_operation_temporal_process (GeglOperation       *self,
                                                 GeglBuffer          *input,
                                                 GeglBuffer          *output,
                                                 const GeglRectangle *result,
                                                 gint                 level)
{
  GeglOperationTemporal *temporal = GEGL_OPERATION_TEMPORAL (self);
  GeglOperationTemporalPrivate *priv = temporal->priv;
  GeglOperationTemporalClass *temporal_class;

  temporal_class = GEGL_OPERATION_TEMPORAL_GET_CLASS (self);

  priv->width  = result->width;
  priv->height = result->height;

  {
   GeglRectangle write_rect = *result;
   write_rect.y = priv->next_to_write * priv->height;

   gegl_buffer_copy (input, result, priv->frame_store, &write_rect);
   priv->count++;
   priv->next_to_write++;
   if (priv->next_to_write >= priv->history_length)
     priv->next_to_write=0;
  }

 if (temporal_class->process)
   return temporal_class->process (self, input, output, result, level);
 return FALSE;
}

static void gegl_operation_temporal_prepare (GeglOperation *operation)
{
  gegl_operation_set_format (operation, "output", babl_format ("RGB u8"));
  gegl_operation_set_format (operation, "input", babl_format ("RGB u8"));
}

static void
gegl_operation_temporal_class_init (GeglOperationTemporalClass *klass)
{
  GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
  GeglOperationFilterClass *operation_filter_class = GEGL_OPERATION_FILTER_CLASS (klass);

  operation_class->prepare = gegl_operation_temporal_prepare;
  operation_filter_class->process = gegl_operation_temporal_process;

  g_type_class_add_private (klass, sizeof (GeglOperationTemporalPrivate));
}

static void
gegl_operation_temporal_init (GeglOperationTemporal *self)
{
  GeglOperationTemporalPrivate *priv;
  GeglRectangle rect = { 0, 0, 4096, 4096*600 };

  self->priv = GEGL_OPERATION_TEMPORAL_GET_PRIVATE(self);
  priv=self->priv;
  priv->count          = 0;
  priv->history_length = 500;
  priv->width          = 1024;
  priv->height         = 1024;
  priv->next_to_write  = 0;

  /* FIXME: the format used for the frame_store should be autodetected from
   * input
   */
  priv->frame_store    =
      gegl_buffer_new (&rect, babl_format ("RGB u8"));
;
}

void gegl_operation_temporal_set_history_length (GeglOperation *op,
                                                 gint           history_length)
{
  GeglOperationTemporal *self = GEGL_OPERATION_TEMPORAL (op);
  GeglOperationTemporalPrivate *priv = self->priv;
  priv->history_length = history_length;
}

guint gegl_operation_temporal_get_history_length (GeglOperation *op)
{
  GeglOperationTemporal *self = GEGL_OPERATION_TEMPORAL (op);
  GeglOperationTemporalPrivate *priv = self->priv;
  return priv->history_length;
}
