GSList *
empathy_smiley_manager_parse (EmpathySmileyManager *manager,
			      const gchar          *text)
{
	EmpathySmileyManagerPriv *priv = GET_PRIV (manager);
	EmpathySmiley            *smiley;
	SmileyManagerTree        *cur_tree = priv->tree;
	const gchar              *t;
	const gchar              *cur_str = text;
	GSList                   *smileys = NULL;

	g_return_val_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager), NULL);
	g_return_val_if_fail (text != NULL, NULL);

	for (t = text; *t; t = g_utf8_next_char (t)) {
		SmileyManagerTree *child;
		gunichar           c;
		
		c = g_utf8_get_char (t);
		child = smiley_manager_tree_find_child (cur_tree, c);

		if (cur_tree == priv->tree) {
			if (child) {
				if (t > cur_str) {
					smiley = smiley_new (NULL, g_strndup (cur_str, t - cur_str));
					smileys = g_slist_prepend (smileys, smiley);
				}
				cur_str = t;
				cur_tree = child;
			}

			continue;
		}

		if (child) {
			cur_tree = child;
			continue;
		}

		smiley = smiley_new (cur_tree->pixbuf, g_strndup (cur_str, t - cur_str));
		smileys = g_slist_prepend (smileys, smiley);
		if (cur_tree->pixbuf) {
			cur_str = t;
			cur_tree = smiley_manager_tree_find_child (priv->tree, c);

			if (!cur_tree) {
				cur_tree = priv->tree;
			}
		} else {
			cur_str = t;
			cur_tree = priv->tree;
		}
	}

	smiley = smiley_new (cur_tree->pixbuf, g_strndup (cur_str, t - cur_str));
	smileys = g_slist_prepend (smileys, smiley);

	return g_slist_reverse (smileys);
}
static SmileyManagerTree *
smiley_manager_tree_find_or_insert_child (SmileyManagerTree *tree, gunichar c)
{
	SmileyManagerTree *child;

	child = smiley_manager_tree_find_child (tree, c);

	if (!child) {
		child = smiley_manager_tree_new (c);
		tree->childrens = g_slist_prepend (tree->childrens, child);
	}

	return child;
}
GSList *
empathy_smiley_manager_parse_len (EmpathySmileyManager *manager,
				  const gchar          *text,
				  gssize                len)
{
	EmpathySmileyManagerPriv *priv = GET_PRIV (manager);
	EmpathySmileyHit         *hit;
	GSList                   *hits = NULL;
	SmileyManagerTree        *cur_tree = priv->tree;
	const gchar              *cur_str;
	const gchar              *start = NULL;

	g_return_val_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager), NULL);
	g_return_val_if_fail (text != NULL, NULL);

	/* If len is negative, parse the string until we find '\0' */
	if (len < 0) {
		len = G_MAXSSIZE;
	}

	/* Parse the len first bytes of text to find smileys. Each time a smiley
	 * is detected, append a EmpathySmileyHit struct to the returned list,
	 * containing the smiley pixbuf and the position of the text to be
	 * replaced by it.
	 * cur_str is a pointer in the text showing the current position
	 * of the parsing. It is always at the begining of an UTF-8 character,
	 * because we support unicode smileys! For example we could want to
	 * replace ™ by an image. */

	for (cur_str = text;
	     *cur_str != '\0' && cur_str - text < len;
	     cur_str = g_utf8_next_char (cur_str)) {
		SmileyManagerTree *child;
		gunichar           c;

		c = g_utf8_get_char (cur_str);
		child = smiley_manager_tree_find_child (cur_tree, c);

		/* If we have a child it means c is part of a smiley */
		if (child) {
			if (cur_tree == priv->tree) {
				/* c is the first char of some smileys, keep
				 * the begining position */
				start = cur_str;
			}
			cur_tree = child;
			continue;
		}

		/* c is not part of a smiley. let's check if we found a smiley
		 * before it. */
		if (cur_tree->pixbuf != NULL) {
			/* found! */
			hit = smiley_hit_new (cur_tree, start - text,
					      cur_str - text);
			hits = g_slist_prepend (hits, hit);

			/* c was not part of this smiley, check if a new smiley
			 * start with it. */
			cur_tree = smiley_manager_tree_find_child (priv->tree, c);
			if (cur_tree) {
				start = cur_str;
			} else {
				cur_tree = priv->tree;
			}
		} else if (cur_tree != priv->tree) {
			/* We searched a smiley starting at 'start' but we ended
			 * with no smiley. Look again starting from next char.
			 *
			 * For example ">:)" and ":(" are both valid smileys,
			 * when parsing text ">:(" we first see '>' which could
			 * be the start of a smiley. 'start' variable is set to
			 * that position and we parse next char which is ':' and
			 * is still potential smiley. Then we see '(' which is
			 * NOT part of the smiley, ">:(" does not exist, so we
			 * have to start again from ':' to find ":(" which is
			 * correct smiley. */
			cur_str = start;
			cur_tree = priv->tree;
		}
	}

	/* Check if last char of the text was the end of a smiley */
	if (cur_tree->pixbuf != NULL) {
		hit = smiley_hit_new (cur_tree, start - text, cur_str - text);
		hits = g_slist_prepend (hits, hit);
	}

	return g_slist_reverse (hits);
}