/*
    ragmaan/state_stack.c            (C) 2003 Raymond (zandbergen@home.nl)

    "ragmaan" is an anagram generator

    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 <glib.h>
#include <gtk/gtk.h>
#include <string.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "state_stack.h"
#include "hash_func.h"

struct state_stack_s state_stack;
struct state_s *state_stack_tos;
#define cfree(p) do { if (p) g_free(p); (p) = NULL; } while(0)

gint
state_stack_depth (void)
{
  return state_stack.depth;
}

void
state_stack_init (void)
{
  state_stack.target = NULL;
  state_stack.depth = 0;
  state_stack.alloc_depth = 10;
  state_stack.state =
    g_malloc (state_stack.alloc_depth * sizeof (struct state_s));
  state_stack_tos = state_stack.state;
  state_stack_tos->selected_path = NULL;
  state_stack_tos->subword = NULL;
}

void
state_stack_clear (const gchar * target)
{
  gint i;
  GtkTreeModel *m;
  struct state_s *si;
  for (i = 0; i <= state_stack.depth; i++)
    {
      si = state_stack.state + i;
      if (si->selected_path)
	{
	  gtk_tree_path_free (si->selected_path);
	  si->selected_path = NULL;
	}
      cfree (si->subword);
      if (i < state_stack.depth)
	{			
	  /* not for TOS - current model is freed separately */
	  m = si->model;
	  g_object_unref (G_OBJECT (m));
	}
    }
  state_stack.depth = 0;
  cfree(state_stack.target);
  state_stack.target = g_strdup (target);
  state_stack_tos = state_stack.state;
}

void
state_stack_store (gchar * subword)
{
  cfree (state_stack_tos->subword);
  state_stack_tos->subword = g_strdup (subword);
}

void
state_stack_push (void)
{
  gint tos;

  tos = ++state_stack.depth;
  /* need more memory? double it.*/
  if (state_stack.depth >= state_stack.alloc_depth)
    {
      state_stack.alloc_depth *= 2;
      state_stack.state =
	g_realloc (state_stack.state,
		   state_stack.alloc_depth * sizeof (struct state_s));
    }
  state_stack_tos = state_stack.state + tos;
  state_stack_tos->subword = NULL;
  state_stack_tos->selected_path = NULL;
}

void
state_stack_pop (void)
{
  gint tos;

  if (state_stack.depth == 0)
    return; /* don't complain */
  tos = --state_stack.depth;
  state_stack_tos = state_stack.state + tos;
  cfree (state_stack_tos->subword);
}

/* rest := target minus all subwords */

gchar *
state_stack_rest (void)
{
  gint i;
  gint tos = state_stack.depth;
  struct state_s *si;
  gchar *rest;

  rest = g_strdup (state_stack.target);

  /* depth may be 0, and we want at least one subtract() */
  subtract3 (rest, "");

  for (i = 0; i <= tos; i++)
    {
      si = state_stack.state + i;
      if (si->subword)
	subtract3 (rest, si->subword);
    }
  return rest;
}

/* string all stacked subwords together, plus residue */

gchar *
state_stack_to_string (void)
{
  GString *result;
  gchar *rest;
  gint tos = state_stack.depth;
  gint i;
  struct state_s *si;

  si = state_stack.state + 0;

  /* stack empty? */
  if (si->subword == NULL || si->subword[0] == 0)
      return g_strdup ("-");

  /* concat all subwords (+ whitespace) */
  result = g_string_sized_new (60);
  for (i = 0; i <= tos; i++)
    {
      si = state_stack.state + i;
      if (si->subword)
	{
	  if (i > 0)
	    result = g_string_append_c (result, ' ');
	  result = g_string_append (result, si->subword);
	}
    }
  /* anything left? */
  rest = state_stack_rest ();
  if (rest[0])
    g_string_append_printf (result, " + '%s'", rest);
  g_free (rest);

  return g_string_free (result, FALSE);
}
