/*
 * GIMP Dynamic Text -- This is a plug-in for The GIMP 1.0
 * Copyright (C) 1998,1999,2000 Marco Lamberto <lm@geocities.com>
 * Web page: http://www.geocities.com/Tokyo/1474/gimp/
 *
 * 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.
 *
 * $Id$
 */

#include "config.h"

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <gtk/gtk.h>

#include "libgimp/gimp.h"
#include "libgimp/stdplugins-intl.h"

#include "gdyntext.h"
#include "font_selection.h"



static void gdt_query (void);
static void gdt_run   (gchar       *name, 
		       gint         nparams, 
		       GimpParam   *param, 
		       gint        *nreturn_vals, 
		       GimpParam  **return_vals);


GimpPlugInInfo PLUG_IN_INFO = 
{
  NULL,
  NULL,
  gdt_query,
  gdt_run
};

GdtVals gdtvals;


#ifndef DEBUG_UI
MAIN()
#endif


static void 
gdt_query (void)
{
  static GimpParamDef gdt_args[] = 
  {
    /* standard params */
    { GIMP_PDB_INT32,    "run_mode",        "Interactive, non-interactive" },
    { GIMP_PDB_IMAGE,    "image",           "Input image"                  },
    { GIMP_PDB_DRAWABLE, "drawable",        "Input drawable"               },
    /* gdyntext params */
    { GIMP_PDB_STRING,   "text",            "Text to render"               },
    { GIMP_PDB_INT32,    "antialias",       "Generate antialiased text"    },
    { GIMP_PDB_INT32,    "alignment",       "Text alignment: { LEFT = 0, CENTER = 1, RIGHT = 2 }" },
    { GIMP_PDB_INT32,    "rotation",        "Text rotation (degrees)"      },
    { GIMP_PDB_INT32,    "line_spacing",    "Line spacing"                 },
    { GIMP_PDB_COLOR,    "color",           "Text color"                   },
    { GIMP_PDB_INT32,    "layer_alignment", "Layer alignment { NONE = 0, BOTTOM_LEFT = 1, BOTTOM_CENTER = 2, BOTTOM_RIGHT = 3, MIDDLE_LEFT = 4, CENTER = 5, MIDDLE_RIGHT = 6, TOP_LEFT = 7, TOP_CENTER = 8, TOP_RIGHT = 9 }" },
    { GIMP_PDB_STRING,   "fontname",        "The fontname (conforming to the X Logical Font Description Conventions)" }
  };
  static GimpParamDef gdt_rets[] = 
  {
    { GIMP_PDB_LAYER,    "layer",           "The text layer"               }
  };

  gimp_install_procedure ("plug_in_dynamic_text",
			  "GIMP Dynamic Text",
			  "",
			  "Marco Lamberto <lm@geocities.com>",
			  "Marco Lamberto",
			  "Jan 1999",
			  N_("<Image>/Filters/Text/Dynamic Text..."),
			  "RGB*,GRAY*,INDEXED*",
			  GIMP_PLUGIN,
			  G_N_ELEMENTS (gdt_args),
                          G_N_ELEMENTS (gdt_rets),
			  gdt_args, gdt_rets);
}


static void 
gdt_run (gchar       *name, 
	 gint         nparams, 
	 GimpParam   *param, 
	 gint        *nreturn_vals,
	 GimpParam  **return_vals)
{
  static GimpParam values[2];
  GimpRunMode run_mode;
  GdtVals oldvals;
  
 
  INIT_I18N_UI();

  gdtvals.valid		      = TRUE;
  gdtvals.change_layer_name   = FALSE;
  run_mode		      = param[0].data.d_int32;
  gdtvals.image_id	      = param[1].data.d_image;
  gdtvals.drawable_id	      = param[2].data.d_drawable;
  gdtvals.layer_id	      = param[2].data.d_layer;
  gdtvals.messages	      = NULL;
  gdtvals.preview	      = TRUE;
  
  *nreturn_vals = 2;
  *return_vals  = values;
	
  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = GIMP_PDB_SUCCESS;
  values[1].type          = GIMP_PDB_LAYER;
  values[1].data.d_int32  = -1;

  switch(run_mode) 
    {
    case GIMP_RUN_INTERACTIVE:
      memset (&oldvals, 0, sizeof(GdtVals));
      gimp_get_data ("plug_in_gdyntext", &oldvals);

      if (oldvals.valid) 
	{
	  strncpy (gdtvals.text, oldvals.text, sizeof(gdtvals.text));
	  strncpy (gdtvals.xlfd, oldvals.xlfd, sizeof(gdtvals.xlfd));
	  gdtvals.color	          = oldvals.color;
	  gdtvals.antialias	  = oldvals.antialias;
	  gdtvals.alignment       = oldvals.alignment;
	  gdtvals.rotation        = oldvals.rotation;
	  gdtvals.line_spacing	  = oldvals.line_spacing;
	  gdtvals.layer_alignment = oldvals.layer_alignment;
	  gdtvals.preview	  = oldvals.preview;
	} 
      else
	{
	  gdtvals.valid             = FALSE;
	}

      gdt_load (&gdtvals);
      if (!gdt_create_ui (&gdtvals))
	return;
      break;

    case GIMP_RUN_NONINTERACTIVE:
#ifdef DEBUG
      g_print ("%d\n", nparams);
#endif
      if (nparams != 11) 
	{
	  values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
	  return;
	} 
      else 
	{
	  gdtvals.new_layer	  = !gimp_drawable_has_alpha (gdtvals.drawable_id);
	  strncpy(gdtvals.text, param[3].data.d_string, sizeof(gdtvals.text));
	  gdtvals.antialias	  = param[4].data.d_int32;
	  gdtvals.alignment	  = param[5].data.d_int32;
	  gdtvals.rotation	  = param[6].data.d_int32;
	  gdtvals.line_spacing	  = param[7].data.d_int32;
	  gdtvals.color           = param[8].data.d_color;
	  gdtvals.layer_alignment = param[9].data.d_int32;
	  strncpy(gdtvals.xlfd, param[10].data.d_string, sizeof(gdtvals.xlfd));
	}
      break;

    case GIMP_RUN_WITH_LAST_VALS:
      gimp_get_data ("plug_in_gdyntext", &gdtvals);
      gdtvals.image_id	  = param[1].data.d_image;
      gdtvals.drawable_id = param[2].data.d_drawable;
      gdtvals.layer_id	  = param[2].data.d_layer;
      gdtvals.new_layer   = !gimp_drawable_has_alpha (gdtvals.drawable_id);
      break;
    }
  
  gdt_render_text (&gdtvals);

  if (run_mode == GIMP_RUN_INTERACTIVE)
    {
      gdtvals.valid = TRUE;
      gimp_set_data ("plug_in_gdyntext", &gdtvals, sizeof(GdtVals));
    }

  values[1].data.d_int32 = gdtvals.layer_id; 
}


void 
gdt_load (GdtVals *data)
{
  gint32          color;
  gchar          *gdtparams  = NULL;
  gchar          *gdtparams0 = NULL;
  gchar         **params     = NULL;
  GimpParasite   *parasite   = NULL;  
	
  if (gdt_compat_load (data))
    return;
  
  if ((parasite = gimp_drawable_parasite_find (data->drawable_id, 
					       GDYNTEXT_PARASITE)) != NULL)
    {
      gdtparams = g_strdup (gimp_parasite_data (parasite));
      gimp_parasite_free (parasite);
    }
  
  if (gdtparams == NULL)
    gdtparams = gimp_layer_get_name (data->layer_id);
  
  if (!gimp_drawable_has_alpha (data->drawable_id) ||
      strncmp (gdtparams, "GDT", 3) != 0)
    {
      data->messages = g_list_append (data->messages, _(" Current layer isn't a GDynText layer or it has no alpha channel."
							" Forcing new layer creation."));
      data->new_layer = TRUE;

      if (!data->valid)   /* setup default values if it wasn't already done */
	{		
	  strcpy (data->text, "");
	  strcpy (data->xlfd, "");

	  gimp_palette_get_foreground (&data->color);

	  data->antialias         = TRUE;
	  data->alignment 	  = LEFT;
	  data->rotation	  = 0;
	  data->line_spacing	  = 0;
	  data->layer_alignment	  = LA_NONE;
	  data->change_layer_name = FALSE;
	  data->messages	  = NULL;
	  data->preview		  = TRUE;
	}

      return;
    }

#ifdef DEBUG
  g_print ("'%s'\n", gdtparams);
#endif
  {
    gchar *st;
    gint len;
    
    st = strchr (gdtparams, '{') + 1;
    len = strrchr (st, '}') - st;
    gdtparams0 = g_strndup (st, len);
  }
#ifdef DEBUG
  g_print ("'%s'\n", gdtparams0);
#endif
  params = g_strsplit (gdtparams0, "}{", -1);
  g_free (gdtparams0);
  
  data->new_layer       = FALSE;
  data->antialias       = atoi (params[ANTIALIAS]);
  data->alignment       = atoi (params[ALIGNMENT]);
  data->rotation        = atoi (params[ROTATION]);
  data->line_spacing	= atoi (params[LINE_SPACING]);
  data->layer_alignment	= atoi (params[LAYER_ALIGNMENT]);

  color = strtol (params[COLOR], (gchar **)NULL, 16);
  gimp_rgb_set_uchar (&data->color, color >> 16, color >> 8, color);

  strncpy (data->xlfd, params[XLFD], sizeof(data->xlfd));
  strncpy (data->text, params[TEXT], sizeof(data->text));

  {
    gchar *text = g_strcompress (data->text);
    g_snprintf (data->text, sizeof(data->text), "%s", text);
    g_free (text);
  }
  g_free (gdtparams);
  g_strfreev (params);
}


void 
gdt_save (GdtVals *data)
{
  gchar        *lname;
  gchar        *text;
  guchar        r, g, b;
  gint32        color;
  GimpParasite *parasite;
  
  text = g_strescape (data->text, NULL);
  gimp_rgb_get_uchar (&data->color, &r, &g, &b);
  color = (r << 16) | (g << 8) | b;
  lname = g_strdup_printf (GDYNTEXT_MAGIC
			   "{%s}{%d}{%d}{%d}{%d}{%06X}{%d}{%s}",
			   text,
			   data->antialias, 
			   data->alignment, 
			   data->rotation, 
			   data->line_spacing,
			   color, 
			   data->layer_alignment, 
			   data->xlfd);
  g_free(text);
  
  parasite = gimp_parasite_new (GDYNTEXT_PARASITE,
				GIMP_PARASITE_PERSISTENT | GIMP_PARASITE_UNDOABLE, strlen(lname), lname);
  gimp_drawable_parasite_attach (data->drawable_id, parasite);
  gimp_parasite_free (parasite);
  
  /* change the layer name as in GIMP 1.x */
  if (data->change_layer_name)
    gimp_layer_set_name (data->layer_id, _("GDynText Layer"));

  gimp_displays_flush ();
  g_free (lname);
}


void 
gdt_render_text (GdtVals *data)
{
  gdt_render_text_p (data, TRUE);
}


void 
gdt_render_text_p (GdtVals  *data, 
		   gboolean  show_progress)
{
  gint layer_ox, layer_oy, i, xoffs;
  gint32 layer_f, selection_empty, selection_channel = -1;
  gint32 text_width, text_height = 0;
  gint32 text_ascent, text_descent;
  gint32 layer_width, layer_height;
  gint32 image_width, image_height;
  gint32 space_width;
  gfloat font_size;
  gint32 font_size_type;
  gchar **text_xlfd, **text_lines;
  gint32 *text_lines_w;
  GimpRGB  old_color;
  
  if (show_progress)
    gimp_progress_init (_("GIMP Dynamic Text"));
  
  /* undo start */
  gimp_undo_push_group_start (gdtvals.image_id);
  
  /* save and remove current selection */
  selection_empty = gimp_selection_is_empty (data->image_id);
  if (!selection_empty) 
    {
      /* there is an active selection to save */
      selection_channel = gimp_selection_save (data->image_id);
      gimp_selection_none (data->image_id);
    }

  text_xlfd = g_strsplit (data->xlfd, "-", -1);
  if (!strlen (text_xlfd[XLFD_PIXEL_SIZE]) ||
      !strcmp (text_xlfd[XLFD_PIXEL_SIZE], "*"))
    {
      font_size = atof (text_xlfd[XLFD_POINT_SIZE]);
      font_size_type = FONT_METRIC_POINTS;
    } 
  else 
    {
      font_size = atof (text_xlfd[XLFD_PIXEL_SIZE]);
      font_size_type = FONT_METRIC_PIXELS;
    }
  g_strfreev (text_xlfd);

  /* retrieve space char width */
  gimp_text_get_extents_fontname ("AA", 
				  font_size, 
				  font_size_type, 
				  data->xlfd,
				  &text_width,
				  &text_height,
				  &text_ascent,
				  &text_descent);
  space_width = text_width;
  gimp_text_get_extents_fontname ("AA", 
				  font_size, 
				  font_size_type, 
				  data->xlfd,
				  &text_width,
				  &text_height,
				  &text_ascent,
				  &text_descent);
  space_width -= text_width;
#ifdef DEBUG
  g_print ("GDT: space width = %d\n", space_width);
#endif
  
  /* setup text and compute layer size */
  text_lines = g_strsplit (data->text, "\n", -1);
  for (i = 0; text_lines[i]; i++);
  text_lines_w = g_new0 (gint32, i);
  layer_width = layer_height = 0;

  for (i = 0; text_lines[i]; i++) 
    {
      gimp_text_get_extents_fontname (strlen(text_lines[i]) > 0 ? text_lines[i] : " ",
				      font_size,
				      font_size_type,
				      data->xlfd,
				      &text_width,
				      &text_height,
				      &text_ascent,
				      &text_descent);
#ifdef DEBUG
      g_print ("GDT: %4dx%4d A:%3d D:%3d [%s]\n", text_width, text_height, text_ascent, text_descent, text_lines[i]);
#endif
      text_lines_w[i] = text_width;
      if (layer_width < text_width)
	layer_width = text_width;
      layer_height += text_height + data->line_spacing;
    }
  layer_height -= data->line_spacing;
  
  if (layer_height == 0) 
    layer_height = 10;
  if (layer_width == 0) 
    layer_width = 10;

  if (!data->new_layer) 
    {
      /* resize the old layer */
      gimp_layer_resize (data->layer_id, layer_width, layer_height, 0, 0);
    } 
  else 
    {
      /* create a new layer */
      data->layer_id = data->drawable_id = gimp_layer_new (data->image_id,
							   _("GDynText Layer"), layer_width, layer_height,
							   (GimpImageType)(gimp_image_base_type (data->image_id) * 2 + 1),
							   100.0, GIMP_NORMAL_MODE);
      gimp_layer_add_alpha (data->layer_id);
      gimp_image_add_layer (data->image_id, data->layer_id, 0);
      gimp_image_set_active_layer (data->image_id, data->layer_id);
    }
  
  /* clear layer */
  gimp_layer_set_preserve_transparency (data->layer_id, 0);
  gimp_edit_clear (data->drawable_id);
  
  /* get layer offsets */
  gimp_drawable_offsets (data->layer_id, &layer_ox, &layer_oy);
  
  /* get foreground color */
  gimp_palette_get_foreground (&old_color);
  
  /* set foreground color to the wanted text color */
  gimp_palette_set_foreground (&data->color);
  
  /* write text */
  for (i = 0; text_lines[i]; i++) 
    {
      switch (data->alignment) 
	{
	case LEFT:
	  xoffs = 0;
	  break;
	case RIGHT:
	  xoffs = layer_width - text_lines_w[i];
	  break;
	case CENTER:
	  xoffs = (layer_width - text_lines_w[i]) / 2;
	  break;
	default:
	  xoffs = 0;
	}
      layer_f = gimp_text_fontname (data->image_id,
				    data->drawable_id,
				    (gdouble)layer_ox +
				    strspn (text_lines[i], " ") * space_width + xoffs,	        /* x */
				    (gdouble)layer_oy + i * (text_height + data->line_spacing), /* y */
				    text_lines[i],
				    -1,  /* border */
				    data->antialias,
				    font_size,
				    font_size_type,
				    data->xlfd);
      
      /* FIXME: ascent/descent stuff, use gimp_layer_translate */
#ifdef DEBUG
      g_print ("GDT: MH:%d LH:%d\n", text_height, gimp_drawable_height (layer_f));
#endif
      
      gimp_floating_sel_anchor (layer_f);
      
      if (show_progress)
	gimp_progress_update ((gdouble)(i + 2) * 100.0 * (gdouble)text_height / (gdouble)layer_height);
    }
  g_strfreev (text_lines);
  g_free (text_lines_w);
  
  /* set foreground color to the old one */
  gimp_palette_set_foreground (&old_color);
  
  /* apply rotation */
  if (data->rotation != 0 && abs(data->rotation) != 360) 
    {
      gimp_rotate (data->drawable_id, 
		   TRUE,
		   (gdouble)data->rotation * G_PI / 180.0);
      gimp_layer_set_offsets (data->layer_id, layer_ox, layer_oy);
    }

  /* sets the layer alignment */
  layer_width  = gimp_drawable_width (data->drawable_id);
  layer_height = gimp_drawable_height (data->drawable_id);
  image_width  = gimp_image_width (data->image_id);
  image_height = gimp_image_height (data->image_id);

  switch (data->layer_alignment) 
    {
    case LA_TOP_LEFT:
      gimp_layer_set_offsets(data->layer_id, 0, 0);
      break;
    case LA_TOP_CENTER:
      gimp_layer_set_offsets(data->layer_id, (image_width - layer_width) >> 1, 0);
      break;
    case LA_TOP_RIGHT:
      gimp_layer_set_offsets(data->layer_id, image_width - layer_width, 0);
      break;
    case LA_MIDDLE_LEFT:
      gimp_layer_set_offsets(data->layer_id, 0,
			     (image_height - layer_height) >> 1);
      break;
    case LA_CENTER:
      gimp_layer_set_offsets(data->layer_id, (image_width - layer_width) >> 1,
			     (image_height - layer_height) >> 1);
      break;
    case LA_MIDDLE_RIGHT:
      gimp_layer_set_offsets(data->layer_id, image_width - layer_width,
			     (image_height - layer_height) >> 1);
      break;
    case LA_BOTTOM_LEFT:
      gimp_layer_set_offsets(data->layer_id, 0, image_height - layer_height);
      break;
    case LA_BOTTOM_CENTER:
      gimp_layer_set_offsets(data->layer_id, (image_width - layer_width) >> 1,
			     image_height - layer_height);
      break;
    case LA_BOTTOM_RIGHT:
      gimp_layer_set_offsets(data->layer_id, image_width - layer_width,
			     image_height - layer_height);
      break;
    case LA_NONE:
    default:
      break;
    }
  
  gimp_layer_set_preserve_transparency (data->layer_id, 1);

  /* restore old selection if any */
  if (selection_empty == FALSE) 
    {
      gimp_selection_load (selection_channel);
      gimp_image_remove_channel (data->image_id, selection_channel);
    }

  gdt_save (data);
  
  /* undo end */
  gimp_undo_push_group_end (gdtvals.image_id);
  
  if (show_progress)
    gimp_progress_update (100.0);
  
  gimp_displays_flush ();
}
