/* The GIMP -- an image manipulation program
 * Copyright (C) 1999 Manish Singh <yosh@gimp.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#include "config.h"

#include <string.h>

#include <gtk/gtk.h>

#include "app/dialog_handler.h"

#include "gimpmodregister.h"

#include <libgimp/gimpcolordisplay.h>
#include <libgimp/gimpmodule.h>
#include <libgimp/gimpparasite.h>
#include <libgimp/gimpui.h>
#include <libgimp/gimpmath.h>

#include "libgimp/gimpintl.h"


#define COLOR_DISPLAY_NAME _("High Contrast")

typedef struct _ContrastContext ContrastContext;

struct _ContrastContext
{
  GFunc    ok_func;
  gpointer ok_data;
  GFunc    cancel_func;
  gpointer cancel_data;

  gdouble  contrast;
  guchar  *lookup;

  GtkWidget *shell;
  GtkWidget *spinner;
};

static gpointer       contrast_new                   (gint             type);
static gpointer       contrast_clone                 (gpointer         cd_ID);
static void           contrast_create_lookup_table   (ContrastContext *context);
static void           contrast_destroy               (gpointer         cd_ID);
static void           contrast_convert               (gpointer         cd_ID,
						      guchar          *buf,
						      gint             w,
						      gint             h,
						      gint             bpp,
						      gint             bpl);
static void           contrast_load                  (gpointer         cd_ID,
						      GimpParasite    *state);
static GimpParasite * contrast_save                  (gpointer         cd_ID);
static void           contrast_configure_ok_callback (GtkWidget       *widget,
						      gpointer         data);
static void       contrast_configure_cancel_callback (GtkWidget       *widget,
						      gpointer         data);
static void           contrast_configure             (gpointer         cd_ID,
						      GFunc            ok_func,
						      gpointer         ok_data,
						      GFunc            cancel_func,
						      gpointer         cancel_data);
static void           contrast_configure_cancel      (gpointer         cd_ID);

static GimpColorDisplayMethods methods = 
{
  NULL,
  contrast_new,
  contrast_clone,
  contrast_convert,
  contrast_destroy,
  NULL,
  contrast_load,
  contrast_save,
  contrast_configure,
  contrast_configure_cancel
};

static GimpModuleInfo info = 
{
  NULL,
  N_("High Contrast color display filter"),
  "Jay Cox <jaycox@earthlink.net>",
  "v0.2",
  "(c) 2000, released under the GPL",
  "October 14, 2000"
};

G_MODULE_EXPORT GimpModuleStatus
module_init (GimpModuleInfo **inforet)
{
#ifndef __EMX__
  if (gimp_color_display_register (COLOR_DISPLAY_NAME, &methods))
#else
  if (mod_color_display_register (COLOR_DISPLAY_NAME, &methods))
#endif
    {
      *inforet = &info;
      return GIMP_MODULE_OK;
    }
  else
    return GIMP_MODULE_UNLOAD;
}

G_MODULE_EXPORT void
module_unload (void  *shutdown_data,
	       void (*completed_cb) (void *),
	       void  *completed_data)
{
#ifndef __EMX__
  gimp_color_display_unregister (COLOR_DISPLAY_NAME);
#else
  mod_color_display_unregister (COLOR_DISPLAY_NAME);
#endif
}


static gpointer
contrast_new (gint type)
{
  ContrastContext *context;

  context = g_new0 (ContrastContext, 1);
  context->contrast = 4.0;
  context->lookup = g_new (guchar, 256);

  contrast_create_lookup_table (context);

  return context;
}

static gpointer
contrast_clone (gpointer cd_ID)
{
  ContrastContext *src_context = cd_ID;
  ContrastContext *context;

  context = contrast_new (0);
  context->contrast = src_context->contrast;
  
  memcpy (context->lookup, src_context->lookup, sizeof (guchar) * 256);

  return context;
}

static void
contrast_create_lookup_table (ContrastContext *context)
{
  gint i;

  if (context->contrast == 0.0)
    context->contrast = 1.0;

  for (i = 0; i < 256; i++)
    {
      context->lookup[i] 
	= (guchar) (int)(255 * .5 * (1 + sin(context->contrast*2*G_PI*i/255.0)));
    }
}

static void
contrast_destroy (gpointer cd_ID)
{
  ContrastContext *context = cd_ID;

  if (context->shell)
    {
      dialog_unregister (context->shell);
      gtk_widget_destroy (context->shell);
    }

  g_free (context->lookup);
  g_free (context);
}

static void
contrast_convert (gpointer  cd_ID,
		  guchar   *buf,
		  gint      width,
		  gint      height,
		  gint      bpp,
		  gint      bpl)
{
  guchar *lookup = ((ContrastContext *) cd_ID)->lookup;
  gint    i, j = height;

  /* You will not be using the entire buffer most of the time. 
   * Hence, the simplistic code for this is as follows:
   *
   * for (j = 0; j < height; j++)
   *   {
   *     for (i = 0; i < width * bpp; i++)
   *       buf[i] = lookup[buf[i]];
   *     buf += bpl;
   *   }
   */

  width *= bpp;
  bpl -= width;

  while (j--)
    {
      i = width;
      while (i--)
	{
	  *buf = lookup[*buf];
	  buf++;
	}
      buf += bpl;
    }
}

static void
contrast_load (gpointer      cd_ID,
	       GimpParasite *state)
{
  ContrastContext *context = cd_ID;

#if G_BYTE_ORDER == G_BIG_ENDIAN
  memcpy (&context->contrast, gimp_parasite_data (state), sizeof (gdouble));
#else
  guint32 buf[2], *data = gimp_parasite_data (state);

  buf[0] = g_ntohl (data[1]);
  buf[1] = g_ntohl (data[0]);

  memcpy (&context->contrast, buf, sizeof (gdouble));
#endif

  contrast_create_lookup_table (context);
}

static GimpParasite *
contrast_save (gpointer cd_ID)
{
  ContrastContext *context = cd_ID;
  guint32 buf[2];

  memcpy (buf, &context->contrast, sizeof (gdouble));

#if G_BYTE_ORDER == G_LITTLE_ENDIAN
  {
    guint32 tmp = g_htonl (buf[0]);
    buf[0] = g_htonl (buf[1]);
    buf[1] = tmp;
  }
#endif

  return gimp_parasite_new ("Display/Contrast", GIMP_PARASITE_PERSISTENT,
			    sizeof (gdouble), &buf);
}

static void
contrast_configure_ok_callback (GtkWidget *widget,
				gpointer   data)
{
  ContrastContext *context = data;

  context->contrast = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (context->spinner));
  contrast_create_lookup_table (context);

  dialog_unregister (context->shell);
  gtk_widget_destroy (GTK_WIDGET (context->shell));
  context->shell = NULL;

  if (context->ok_func)
    context->ok_func (context, context->ok_data);
}

static void
contrast_configure_cancel_callback (GtkWidget *widget,
				    gpointer   data)
{
  ContrastContext *context = data;

  dialog_unregister (context->shell);
  gtk_widget_destroy (GTK_WIDGET (context->shell));
  context->shell = NULL;

  if (context->cancel_func)
    context->cancel_func (context, context->cancel_data);
}

static void
contrast_configure (gpointer cd_ID,
		    GFunc    ok_func,
		    gpointer ok_data,
		    GFunc    cancel_func,
		    gpointer cancel_data)
{
  ContrastContext *context = cd_ID;
  GtkWidget *hbox;
  GtkWidget *label;
  GtkObject *adjustment;

  if (!context->shell)
    {
      context->ok_func = ok_func;
      context->ok_data = ok_data;
      context->cancel_func = cancel_func;
      context->cancel_data = cancel_data;

      context->shell =
	gimp_dialog_new (_("High Contrast"), "high contrast",
			 gimp_standard_help_func, "modules/highcontrast.html",
			 GTK_WIN_POS_MOUSE,
			 FALSE, TRUE, FALSE,

			 GTK_STOCK_OK, contrast_configure_ok_callback,
			 cd_ID, NULL, NULL, TRUE, FALSE,
			 GTK_STOCK_CANCEL, contrast_configure_cancel_callback,
			 cd_ID, NULL, NULL, FALSE, TRUE,

			 NULL);

      dialog_register (context->shell);

      hbox = gtk_hbox_new (FALSE, 2);
      gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
      gtk_box_pack_start (GTK_BOX (GTK_DIALOG (context->shell)->vbox),
			  hbox, FALSE, FALSE, 0);

      label = gtk_label_new ( _("Contrast Cycles:"));
      gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
      gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);

      adjustment = gtk_adjustment_new (context->contrast, 1.0, 20.0, 0.5, 1.0, 0.0);
      context->spinner = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment),
					      0.1, 3);
      gtk_widget_set_usize (context->spinner, 100, 0);
      gtk_box_pack_start (GTK_BOX (hbox), context->spinner, FALSE, FALSE, 0); 
    }

  gtk_widget_show_all (context->shell);
}

static void
contrast_configure_cancel (gpointer cd_ID)
{
  ContrastContext *context = cd_ID;
 
  if (context->shell)
    {
      dialog_unregister (context->shell);
      gtk_widget_destroy (context->shell);
      context->shell = NULL;
    }

  if (context->cancel_func)
    context->cancel_func (context, context->cancel_data);
}
