#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <pango/pango.h>
#include <gtk/gtk.h>

#if defined(DEBUG_ITER_TEST)
#include <stdio.h>
#define DBG_MSG(...) fprintf (stderr, "==>" __VA_ARGS__)
#else
#define DBG_MSG(...)
#endif

#define LAYOUT_WIDTH (80 * PANGO_SCALE)


/* Note: The test expects that any newline sequence is of length 1
 * use \n (not \r\n) in the test texts.
 * I think the iterator itself should support \r\n without trouble,
 * but there are comments in layout-iter.c suggesting otherwise.
 */
char *test_texts[] =
  {
    /* English with embedded RTL runs (from ancient-hebrew.org) */
    "The Hebrew word \xd7\x90\xd7\x93\xd7\x9d\xd7\x94 (adamah) is the feminine form of \xd7\x90\xd7\x93\xd7\x9d meaning \"ground\"\n",
    /* Arabic, with vowel marks (from Sura Al Fatiha) */
    "\xd8\xa8\xd9\x90\xd8\xb3\xd9\x92\xd9\x85\xd9\x90 \xd8\xa7\xd9\x84\xd9\x84\xd9\x91\xd9\x87\xd9\x90 \xd8\xa7\xd9\x84\xd8\xb1\xd9\x91\xd9\x8e\xd8\xad\xd9\x92\xd9\x85\xd9\x80\xd9\x8e\xd9\x86\xd9\x90 \xd8\xa7\xd9\x84\xd8\xb1\xd9\x91\xd9\x8e\xd8\xad\xd9\x90\xd9\x8a\xd9\x85\xd9\x90\n\xd8\xa7\xd9\x84\xd9\x92\xd8\xad\xd9\x8e\xd9\x85\xd9\x92\xd8\xaf\xd9\x8f \xd9\x84\xd9\x84\xd9\x91\xd9\x87\xd9\x90 \xd8\xb1\xd9\x8e\xd8\xa8\xd9\x91\xd9\x90 \xd8\xa7\xd9\x84\xd9\x92\xd8\xb9\xd9\x8e\xd8\xa7\xd9\x84\xd9\x8e\xd9\x85\xd9\x90\xd9\x8a\xd9\x86\xd9\x8e\n",
    /* Arabic, with embedded LTR runs (from a Linux guide) */
    "\xd8\xa7\xd9\x84\xd9\x85\xd8\xaa\xd8\xba\xd9\x8a\xd8\xb1 LC_ALL \xd9\x8a\xd8\xba\xd9\x8a\xd9\x8a\xd8\xb1 \xd9\x83\xd9\x84 \xd8\xa7\xd9\x84\xd9\x85\xd8\xaa\xd8\xba\xd9\x8a\xd8\xb1\xd8\xa7\xd8\xaa \xd8\xa7\xd9\x84\xd8\xaa\xd9\x8a \xd8\xaa\xd8\xa8\xd8\xaf\xd8\xa3 \xd8\xa8\xd8\xa7\xd9\x84\xd8\xb1\xd9\x85\xd8\xb2 LC.",
    /* Hebrew, with vowel marks (from Genesis) */
    "\xd7\x91\xd6\xbc\xd6\xb0\xd7\xa8\xd6\xb5\xd7\x90\xd7\xa9\xd7\x81\xd6\xb4\xd7\x99\xd7\xaa, \xd7\x91\xd6\xbc\xd6\xb8\xd7\xa8\xd6\xb8\xd7\x90 \xd7\x90\xd6\xb1\xd7\x9c\xd6\xb9\xd7\x94\xd6\xb4\xd7\x99\xd7\x9d, \xd7\x90\xd6\xb5\xd7\xaa \xd7\x94\xd6\xb7\xd7\xa9\xd6\xbc\xd7\x81\xd6\xb8\xd7\x9e\xd6\xb7\xd7\x99\xd6\xb4\xd7\x9d, \xd7\x95\xd6\xb0\xd7\x90\xd6\xb5\xd7\xaa \xd7\x94\xd6\xb8\xd7\x90\xd6\xb8\xd7\xa8\xd6\xb6\xd7\xa5",
    /* Hebrew, with embedded LTR runs (from a Linux guide) */
    "\xd7\x94\xd7\xa7\xd7\x9c\xd7\x93\xd7\x94 \xd7\xa2\xd7\x9c \xd7\xa9\xd7\xa0\xd7\x99 \xd7\x94 SHIFT\xd7\x99\xd7\x9d (\xd7\x99\xd7\x9e\xd7\x99\xd7\x9f \xd7\x95\xd7\xa9\xd7\x9e\xd7\x90\xd7\x9c \xd7\x91\xd7\x99\xd7\x97\xd7\x93) \xd7\x90\xd7\x9e\xd7\x95\xd7\xa8\xd7\x99\xd7\x9d \xd7\x9c\xd7\x94\xd7\x93\xd7\x9c\xd7\x99\xd7\xa7 \xd7\x90\xd7\xaa \xd7\xa0\xd7\x95\xd7\xa8\xd7\xaa \xd7\x94 Scroll Lock , \xd7\x95\xd7\x9c\xd7\x94\xd7\xa2\xd7\x91\xd7\x99\xd7\xa8 \xd7\x90\xd7\x95\xd7\xaa\xd7\xa0\xd7\x95 \xd7\x9c\xd7\x9e\xd7\xa6\xd7\x91 \xd7\x9b\xd7\xaa\xd7\x99\xd7\x91\xd7\x94 \xd7\x91\xd7\xa2\xd7\x91\xd7\xa8\xd7\x99\xd7\xaa.",
    NULL
  };

/* char iteration test:
 *  - Total num of iterations match number of chars
 *  - GlyphString's index_to_x positions match those returned by the Iter
 */
void
iter_char_test (PangoLayout *layout, char *text)
{
  PangoRectangle   extents, runextents;
  PangoLayoutIter *iter;
  PangoLayoutRun  *run;
  int              textlen, textbytes;
  int              i, index, offset, expected_x, extra_eol;
  gchar           *ptr;
  gboolean         iter_next_ok, rtl;

  textbytes = strlen (text);
  textlen = g_utf8_strlen (text, textbytes);
  DBG_MSG ("testing text(len=%d,bytes=%d):<%s>\n", 
	   textlen, textbytes, text);

  pango_layout_set_text (layout, text, -1);

  iter = pango_layout_get_iter (layout);

  /* Extra EOL's (inserted by the auto-wrapping mechanism), don't
   * match any character in original text, so should be added to
   * the expected number of iterations.
   */
  extra_eol = 0;

  for (i = 0, iter_next_ok = TRUE; i < textlen+extra_eol; ++i)
    {
      g_assert (iter_next_ok);

      index = pango_layout_iter_get_index (iter);
      ptr = text + index;
      DBG_MSG ("i=%d (visual), index = %d extra_eol=%d '%.*s':\n", 
	       i, index, extra_eol,
	       g_utf8_next_char(ptr)-ptr, ptr);

      pango_layout_iter_get_char_extents (iter, &extents);
      DBG_MSG ("  char extents: x=%d,y=%d w=%d,h=%d\n",
	       extents.x, extents.y, 
	       extents.width, extents.height);
      
      run = pango_layout_iter_get_run (iter);
      if (run == NULL)
	{
	  DBG_MSG ("Empty Run\n");
	  /* If there's no \n: linebreak was inserted by linewrapping,
	   * so it does not match any char */
	  if ((index >= textbytes) || (*ptr != '\n'))
	    {
	      ++extra_eol;
	      DBG_MSG ("Extra EOL!\n");
	    }
	}
      else
	{
	  /* Get needed data for the GlyphString */
	  pango_layout_iter_get_run_extents(iter, NULL, &runextents);
	  offset = run->item->offset;
	  rtl = run->item->analysis.level%2;
	  DBG_MSG ("  (current run: ofs=%d,x=%d,len=%d,rtl=%d)\n", 
		   offset, runextents.x, run->item->length, rtl);

	  /* Calculate expected x result using index_to_x */
	  pango_glyph_string_index_to_x (run->glyphs, 
					 text + offset, run->item->length,
					 &run->item->analysis,
					 index - offset, FALSE, &expected_x);
	  expected_x += runextents.x;

	  /* Note: the iterator uses the same rounding method as 
	   * GlyphString::index_to_x (i.e. always truncate).
	   * However, for RTL chars index_to_x gives the rightmost coordinate.
	   * Substracting extents.width to make up for that might cause a 
	   * rounding mismatch, hence the 1.0 tolerance.
	   */
	  if (rtl)
	    expected_x -= extents.width;

	  DBG_MSG ("  (GlyphString ind=%d (offset=%d), expected x=%d)\n", 
		   index - offset, offset, expected_x);
	  g_assert (abs(expected_x - extents.x) <= 1);
	}
      
      iter_next_ok = pango_layout_iter_next_char (iter);
      DBG_MSG ("more to go? %d\n", iter_next_ok);
    }

  /* layout contains an extra EOL at the end, verify it */
  g_assert (NULL == pango_layout_iter_get_run (iter));
  DBG_MSG ("verified extra EOL at end");

  /* Skip it, verify the end */
  iter_next_ok = pango_layout_iter_next_char (iter);
  g_assert (! iter_next_ok);

  pango_layout_iter_free (iter);

}

int
main (int argc, char *argv[])
{
  char        **ptext;
  GtkWidget    *window;
  PangoContext *context;
  PangoLayout  *layout;

  gtk_init (&argc, &argv);  
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  context = gtk_widget_get_pango_context (window);

  layout = pango_layout_new (context);
  pango_layout_set_width (layout, LAYOUT_WIDTH);

  for (ptext = test_texts; *ptext != NULL; ++ptext)
    {
      DBG_MSG ("--------- checking next text ----------\n");
      iter_char_test (layout, *ptext);
    }

  g_object_unref (layout);
  return 0;
}
