/*
    ragmaan/config_file.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 <stdio.h>
#include <string.h>
#include <glib.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "ragmaan.h"
#include "scratchpad.h"
#include "config_file.h"

#define RC PACKAGE "rc"
#define TOK_INDEX "index"

enum /* must match elem_name below */
{ ELEM_ROOT, ELEM_WORDLIST, ELEM_SCRATCHPAD, ELEM_HISTORY, ELEM_TARGET,
  ELEM_FIND, ELEM_NONE
};

static const char *elem_name[] =
  { PACKAGE, "wordlist_file_name", "scratchpad_file_name", "history",
  "target", "find", 0
};

struct elem_data_s
{
  gint token;
  GList **history;
  GList *token_stack;
};

static gchar *config_home;
static gchar *config_system;

/* construct paths in config_home and config_system */

static void
config_init (void)
{
  if (!config_home)
    {
      /* (leak) */
      config_home = g_strdup_printf ("%s%c.%s", g_get_home_dir (), G_DIR_SEPARATOR, RC);	
      config_system =
	g_strdup_printf ("%s%c%s", DATADIR, G_DIR_SEPARATOR, RC);
    }
}

/* XML parser callback */

static void
start_element (GMarkupParseContext * context,
	       const gchar * element_name,
	       const gchar ** attribute_names,
	       const gchar ** attribute_values,
	       gpointer user_data, GError ** error)
{
  struct elem_data_s *elem_data = user_data;
  gint i;

  for (i = 0; elem_name[i]; i++)
    if (g_ascii_strcasecmp (elem_name[i], element_name) == 0)
      break;
  elem_data->token = i;
  elem_data->token_stack =
    g_list_append (elem_data->token_stack, GINT_TO_POINTER (i));
}

/* XML parser callback */

void
end_element (GMarkupParseContext * context,
	     const gchar * element_name, gpointer user_data, GError ** error)
{
  struct elem_data_s *elem_data = user_data;
  GList *t;
  elem_data->token_stack =
    g_list_delete_link (elem_data->token_stack,
			g_list_last (elem_data->token_stack));
  t = g_list_last (elem_data->token_stack);
  elem_data->token = t ? GPOINTER_TO_INT (t->data) : -1;
}

/* XML parser callback */

static void
text (GMarkupParseContext * context,
      const gchar * text, gsize text_len, gpointer user_data, GError ** error)
{
  struct elem_data_s *elem_data = user_data;
  gchar *v = g_strndup (text, text_len);
  switch (elem_data->token)
    {
    case ELEM_WORDLIST:
      db_name = v;
      break;
    case ELEM_TARGET:
      elem_data->history = &global_widgets.target_history;
      break;
    case ELEM_FIND:
      elem_data->history = &global_widgets.find_history;
      break;
    case ELEM_HISTORY:
      if (elem_data->history
	  && g_list_length (*elem_data->history) < TARGET_HISTORY_SIZE)
	*elem_data->history = g_list_append (*elem_data->history, v);
      break;
    case ELEM_SCRATCHPAD:
      scratchpad_update_name (v, TRUE);
      g_free (v);
      break;
    }
}

/* try to read a single config file */

static gboolean
config_read1 (gchar * file_name)
{
  GError *e = NULL;
  GMarkupParser p;
  GMarkupParseContext *pc;
  GIOChannel *fp;
  gchar *config_data;
  gint config_len;
  struct elem_data_s elem_data;
  elem_data.history = NULL;
  elem_data.token_stack = NULL;

  /* read entire file */

  fp = g_io_channel_new_file (file_name, "r", &e);
  if (e)
    return FALSE;
  g_io_channel_read_to_end (fp, &config_data, &config_len, &e);
  if (e)
    return FALSE;
  g_io_channel_shutdown (fp, TRUE, &e);

  /* init parser object */

  p.start_element = start_element;
  p.text = text;
  p.end_element = end_element;
  p.passthrough = NULL;
  p.error = NULL;

  /* parse */

  pc = g_markup_parse_context_new (&p, 0, &elem_data, NULL);
  g_markup_parse_context_parse (pc, config_data, config_len, NULL);
  g_markup_parse_context_end_parse (pc, NULL);
  g_markup_parse_context_free (pc);
  return TRUE;
}

/* read config file - prefer $HOME, fall back to system-wide */

void
config_read (void)
{
  config_init ();
  if (!config_read1 (config_home))
    config_read1 (config_system);
}

/* who owns the returned string ??? leak ??? */
#define ESC(s) g_markup_escape_text((s), strlen(s))

/* write some global state vars to config file */

static void
print_history (GString * buf, gint hist_type_token, GList * history)
{
  gint i;
  const gchar *e = elem_name[hist_type_token];
  g_string_append_printf (buf, " <%s>\n", e);
  for (i = 0; history && i < TARGET_HISTORY_SIZE; i++)
    {
      g_string_append_printf (buf, "  <%1$s>%2$s</%1$s>\n",
			      elem_name[ELEM_HISTORY],
			      ESC ((gchar *) history->data));
      history = g_list_next (history);
    }
  g_string_append_printf (buf, " </%s>\n", e);
}

void
config_write (void)
{
  GError *e = NULL;
  GIOChannel *fp;
  GString *buf = g_string_new ("");

  config_init ();
  fp = g_io_channel_new_file (config_home, "w", &e);
  if (e)
    {
      g_print ("could not write %s: %s\n", config_home, e->message);
      return;
    }
  g_io_channel_set_encoding (fp, NULL, &e);

  g_string_append_printf (buf, "<%s>\n", elem_name[ELEM_ROOT]);

  /* save current wordlist name */

  if (db_name)
    g_string_append_printf (buf, " <%1$s>%2$s</%1$s>\n",
			    elem_name[ELEM_WORDLIST], ESC (db_name));

  /* save current scratchpad name */

  if (scratchpad_name)
    g_string_append_printf (buf, " <%1$s>%2$s</%1$s>\n",
			    elem_name[ELEM_SCRATCHPAD],
			    ESC (scratchpad_name));

  /* save histories */
  print_history (buf, ELEM_TARGET, global_widgets.target_history);
  print_history (buf, ELEM_FIND, global_widgets.find_history);

  g_string_append_printf (buf, "</%s>\n", elem_name[ELEM_ROOT]);

  g_io_channel_write_chars (fp, buf->str, buf->len, NULL, &e);
  g_string_free (buf, TRUE);
  g_io_channel_shutdown (fp, TRUE, &e);
}
