/*
 * Copyright (c) 2002, 2003, 2004 Jean-Yves Lefort
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of Jean-Yves Lefort nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <glib/gi18n.h>
#include <glib-object.h>
#include "sg-parser.h"
#include "st-handler.h"
#include "st-handler-field.h"
#include "st-handlers.h"
#include "st-action.h"
#include "st-settings.h"
#include "st-util.h"

/*** type definitions ********************************************************/

enum {
  STATEMENT_OPTIONS = 1,
  STATEMENT_OPTIONS_VIEW_MENUBAR,		/* obsolete */
  STATEMENT_OPTIONS_VIEW_TOOLBAR,
  STATEMENT_OPTIONS_VIEW_TABS,
  STATEMENT_OPTIONS_VIEW_TAB_ICONS,
  STATEMENT_OPTIONS_VIEW_STATUSBAR,
  STATEMENT_OPTIONS_TOOLBAR_THEME,		/* obsolete */
  STATEMENT_OPTIONS_TOOLBAR_STYLE,
  STATEMENT_OPTIONS_TOOLBAR_SIZE,
  STATEMENT_OPTIONS_SAVE_AUTOMATIC,		/* obsolete */
  STATEMENT_OPTIONS_MAIN_WINDOW_WIDTH,
  STATEMENT_OPTIONS_MAIN_WINDOW_HEIGHT,
  STATEMENT_OPTIONS_MAIN_WINDOW_DIVIDER,	/* deprecated */
  STATEMENT_OPTIONS_WINDOW_WIDTH,		/* deprecated */
  STATEMENT_OPTIONS_WINDOW_HEIGHT,		/* deprecated */
  STATEMENT_OPTIONS_WINDOW_DIVIDER,		/* deprecated */
  STATEMENT_OPTIONS_PREFERENCES_WINDOW_WIDTH,
  STATEMENT_OPTIONS_PREFERENCES_WINDOW_HEIGHT,
  STATEMENT_OPTIONS_PREFERENCES_SELECTED_PAGE,
  STATEMENT_OPTIONS_PREFERENCES_PLUGINS_EXPANDED,
  STATEMENT_OPTIONS_STREAM_PROPERTIES_WINDOW_WIDTH,
  STATEMENT_OPTIONS_STREAM_COLUMNS_WINDOW_WIDTH,
  STATEMENT_OPTIONS_STREAM_COLUMNS_WINDOW_HEIGHT,
  STATEMENT_OPTIONS_SELECTED_PREFERENCES_PAGE,	/* obsolete */
  STATEMENT_OPTIONS_PROXY_ENABLED,
  STATEMENT_OPTIONS_PROXY_TYPE,
  STATEMENT_OPTIONS_PROXY_URL,			/* deprecated */
  STATEMENT_OPTIONS_PROXY_SERVER,
  STATEMENT_OPTIONS_PROXY_PORT,
  STATEMENT_OPTIONS_PROXY_AUTH_ENABLED,
  STATEMENT_OPTIONS_PROXY_AUTH_NAME,
  STATEMENT_OPTIONS_PROXY_AUTH_PASSWORD,
  STATEMENT_OPTIONS_GALEON_THEMES_ENABLED,	/* obsolete */
  STATEMENT_OPTIONS_GALEON_THEMES_SYSTEM_DIR,	/* obsolete */
  STATEMENT_OPTIONS_GALEON_THEMES_USER_DIR,	/* obsolete */
  STATEMENT_OPTIONS_SELECTED_HANDLER,
  STATEMENT_OPTIONS_ALWAYS_REFRESH,		/* deprecated */
  STATEMENT_OPTIONS_ALWAYS_RELOAD,
  STATEMENT_OPTIONS_FIND_TOKEN,
  STATEMENT_OPTIONS_FIND_CASE_SENSITIVE,
  STATEMENT_OPTIONS_FIND_WRAP_AROUND,
  STATEMENT_OPTIONS_FIND_HISTORY,
  STATEMENT_OPTIONS_MUSIC_DIR,

  STATEMENT_ACTION,
  STATEMENT_ACTION_COMMAND,
  STATEMENT_ACTION_PROGRAM,			/* deprecated */

  STATEMENT_HANDLER,
  STATEMENT_HANDLER_FIELDS_SORT_INDEX,
  STATEMENT_HANDLER_FIELDS_SORT_ORDER,
  STATEMENT_HANDLER_PANED_POSITION,
  STATEMENT_HANDLER_SELECTED_CATEGORY,		/* obsolete */
  STATEMENT_HANDLER_SELECTED_STREAM,		/* obsolete */
  STATEMENT_HANDLER_FIELDS_WIDTH,		/* deprecated */
  STATEMENT_HANDLER_FIELD,
  STATEMENT_HANDLER_FIELD_VISIBLE,
  STATEMENT_HANDLER_FIELD_WIDTH,
  STATEMENT_HANDLER_FIELD_POSITION,
  STATEMENT_HANDLER_KEY,
  STATEMENT_HANDLER_EXPANDED_CATEGORIES,	/* obsolete */
  STATEMENT_HANDLER_SELECTED_STREAMS		/* obsolete */
};

typedef struct
{
  SGParser		*parser;
  SGParserStatement	*statement;

  char			*action;
  
  GSList		*handlers;
  STHandler		*handler;
  GSList		*handler_field_iter;
  STHandlerField	*handler_field;
  GParamSpec		*handler_config_pspec;
  char			*handler_config_key;
  GValueArray		*handler_config_values;
} LoadInfo;

/*** constant definitions ****************************************************/

static SGParserDefinition config_definitions[] = {
  {
    0,			STATEMENT_OPTIONS,
    "options",		TRUE,		G_TYPE_NONE
  },
  {				/* obsolete */
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_VIEW_MENUBAR,
    "view_menubar",	FALSE,		G_TYPE_INT
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_VIEW_TOOLBAR,
    "view_toolbar",	FALSE,		G_TYPE_BOOLEAN
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_VIEW_TABS,
    "view_tabs",	FALSE,		G_TYPE_BOOLEAN
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_VIEW_TAB_ICONS,
    "view_tab_icons",	FALSE,		G_TYPE_BOOLEAN
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_VIEW_STATUSBAR,
    "view_statusbar",	FALSE,		G_TYPE_BOOLEAN
  },
  {				/* obsolete */
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_TOOLBAR_THEME,
    "toolbar_theme",	FALSE,		G_TYPE_STRING
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_TOOLBAR_STYLE,
    "toolbar_style",	FALSE,		G_TYPE_INT
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_TOOLBAR_SIZE,
    "toolbar_size",	FALSE,		G_TYPE_INT
  },
  {				/* obsolete */
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_SAVE_AUTOMATIC,
    "save_automatic",	FALSE,		G_TYPE_BOOLEAN
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_MAIN_WINDOW_WIDTH,
    "main_window_width",	FALSE,	G_TYPE_INT
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_MAIN_WINDOW_HEIGHT,
    "main_window_height",	FALSE,	G_TYPE_INT
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_MAIN_WINDOW_HEIGHT,
    "main_window_height",	FALSE,	G_TYPE_INT
  },
  {				/* deprecated */
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_MAIN_WINDOW_DIVIDER,
    "main_window_divider",	FALSE,	G_TYPE_INT
  },
  {				/* deprecated */
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_WINDOW_WIDTH,
    "window_width",	FALSE,	G_TYPE_INT
  },
  {				/* deprecated */
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_WINDOW_HEIGHT,
    "window_height",	FALSE,	G_TYPE_INT
  },
  {				/* deprecated */
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_WINDOW_DIVIDER,
    "window_divider",	FALSE,	G_TYPE_INT
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_PREFERENCES_WINDOW_WIDTH,
    "preferences_window_width", FALSE,	G_TYPE_INT
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_PREFERENCES_WINDOW_HEIGHT,
    "preferences_window_height", FALSE,	G_TYPE_INT
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_PREFERENCES_SELECTED_PAGE,
    "preferences_selected_page", FALSE,	G_TYPE_STRING
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_PREFERENCES_PLUGINS_EXPANDED,
    "preferences_plugins_expanded", FALSE, G_TYPE_BOOLEAN
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_STREAM_PROPERTIES_WINDOW_WIDTH,
    "stream_properties_window_width", FALSE, G_TYPE_INT
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_STREAM_COLUMNS_WINDOW_WIDTH,
    "stream_columns_window_width", FALSE, G_TYPE_INT
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_STREAM_COLUMNS_WINDOW_HEIGHT,
    "stream_columns_window_height", FALSE, G_TYPE_INT
  },
  {				/* obsolete */
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_SELECTED_PREFERENCES_PAGE,
    "selected_preferences_page", FALSE,	G_TYPE_INT
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_PROXY_ENABLED,
    "proxy_enabled",	FALSE,		G_TYPE_BOOLEAN
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_PROXY_TYPE,
    "proxy_type",	FALSE,		G_TYPE_INT
  },
  {				/* deprecated */
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_PROXY_URL,
    "proxy_url",	FALSE,		G_TYPE_STRING
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_PROXY_SERVER,
    "proxy_server",	FALSE,		G_TYPE_STRING
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_PROXY_PORT,
    "proxy_port",	FALSE,		G_TYPE_INT
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_PROXY_AUTH_ENABLED,
    "proxy_auth_enabled",	FALSE,	G_TYPE_BOOLEAN
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_PROXY_AUTH_NAME,
    "proxy_auth_name",	FALSE,		G_TYPE_STRING
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_PROXY_AUTH_PASSWORD,
    "proxy_auth_password",	FALSE,	G_TYPE_STRING
  },
  {				/* obsolete */
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_GALEON_THEMES_ENABLED,
    "galeon_themes_enabled",	FALSE,	G_TYPE_INT
  },
  {				/* obsolete */
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_GALEON_THEMES_SYSTEM_DIR,
    "galeon_themes_system_dir",	FALSE,	G_TYPE_STRING
  },
  {				/* obsolete */
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_GALEON_THEMES_USER_DIR,
    "galeon_themes_user_dir",	FALSE,	G_TYPE_STRING
  },
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_SELECTED_HANDLER,
    "selected_handler",	FALSE,		G_TYPE_STRING
  }, 
  {				/* deprecated */
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_ALWAYS_REFRESH,
    "always_refresh",	FALSE,		G_TYPE_BOOLEAN
  }, 
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_ALWAYS_RELOAD,
    "always_reload",	FALSE,		G_TYPE_BOOLEAN
  }, 
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_FIND_TOKEN,
    "find_token",	FALSE,		G_TYPE_STRING
  }, 
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_FIND_CASE_SENSITIVE,
    "find_case_sensitive",	FALSE,	G_TYPE_BOOLEAN
  }, 
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_FIND_WRAP_AROUND,
    "find_wrap_around",		FALSE,	G_TYPE_BOOLEAN
  }, 
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_FIND_HISTORY,
    "find_history",	TRUE,		G_TYPE_NONE
  }, 
  {
    STATEMENT_OPTIONS,	STATEMENT_OPTIONS_MUSIC_DIR,
    "music_dir",	FALSE,		G_TYPE_STRING
  }, 
  {
    0,			STATEMENT_ACTION,
    "action",		TRUE,		G_TYPE_STRING
  },
  {
    STATEMENT_ACTION,	STATEMENT_ACTION_COMMAND,
    "command",		FALSE,		G_TYPE_STRING
  },
  {				/* deprecated */
    STATEMENT_ACTION,	STATEMENT_ACTION_PROGRAM,
    "program",		FALSE,		G_TYPE_STRING
  },
  {
    0,			STATEMENT_HANDLER,
    "handler",		TRUE,		G_TYPE_STRING
  },
  {
    STATEMENT_HANDLER,	STATEMENT_HANDLER_FIELDS_SORT_INDEX,
    "fields_sort_index",	FALSE,	G_TYPE_INT
  },
  {
    STATEMENT_HANDLER,	STATEMENT_HANDLER_FIELDS_SORT_ORDER,
    "fields_sort_order",	FALSE,	G_TYPE_INT
  },
  {
    STATEMENT_HANDLER,	STATEMENT_HANDLER_PANED_POSITION,
    "paned_position",		FALSE,	G_TYPE_INT
  },
  {				/* obsolete */
    STATEMENT_HANDLER,	STATEMENT_HANDLER_SELECTED_CATEGORY,
    "selected_category",	FALSE,	G_TYPE_STRING
  },
  {				/* obsolete */
    STATEMENT_HANDLER,	STATEMENT_HANDLER_SELECTED_STREAM,
    "selected_stream",	FALSE,		G_TYPE_STRING
  },
  {				/* deprecated */
    STATEMENT_HANDLER,	STATEMENT_HANDLER_FIELDS_WIDTH,
    "fields_width",	TRUE,		G_TYPE_NONE
  },
  {
    STATEMENT_HANDLER,	STATEMENT_HANDLER_FIELD,
    "field",		TRUE,		G_TYPE_NONE
  },
  {
    STATEMENT_HANDLER_FIELD,	STATEMENT_HANDLER_FIELD_VISIBLE,
    "visible",		FALSE,		G_TYPE_BOOLEAN
  },
  {
    STATEMENT_HANDLER_FIELD,	STATEMENT_HANDLER_FIELD_WIDTH,
    "width",		FALSE,		G_TYPE_INT
  },
  {
    STATEMENT_HANDLER_FIELD,	STATEMENT_HANDLER_FIELD_POSITION,
    "position",		FALSE,		G_TYPE_INT
  },
  {
    STATEMENT_HANDLER,	STATEMENT_HANDLER_KEY,
    "key",		TRUE,		G_TYPE_STRING
  },
  {				/* obsolete */
    STATEMENT_HANDLER,	STATEMENT_HANDLER_EXPANDED_CATEGORIES,
    "expanded_categories",	TRUE,	G_TYPE_NONE
  },
  {				/* obsolete */
    STATEMENT_HANDLER,	STATEMENT_HANDLER_SELECTED_STREAMS,
    "selected_streams",	TRUE,		G_TYPE_NONE
  },
  { 0, 0, NULL, 0, 0 }
};
  
/*** function declarations ***************************************************/

static void st_config_load_find_history              (LoadInfo *info);

static void st_config_load_handler_begin	     (LoadInfo *info);
static void st_config_load_handler_end		     (LoadInfo *info);

static void st_config_load_handler_field_begin	     (LoadInfo *info);
static void st_config_load_handler_field_end	     (LoadInfo *info);

static void st_config_load_command		     (LoadInfo *info);
static void st_config_load_program		     (LoadInfo *info);
static void st_config_load_handler_field_width	     (LoadInfo *info);

static void st_config_load_handler_key_begin         (LoadInfo *info);
static void st_config_load_handler_key_value         (LoadInfo *info);
static void st_config_load_handler_key_end           (LoadInfo *info);

/*** implementation **********************************************************/

gboolean
st_config_load (const char *filename, GError **err)
{ 
  LoadInfo info = { NULL, };

  g_return_val_if_fail(filename != NULL, FALSE);

  info.parser = sg_parser_new(filename, err);
  if (! info.parser)
    return FALSE;
  
  info.parser->scanner->msg_handler = st_parser_msg_cb;
  sg_parser_definev(info.parser, config_definitions);

  while ((info.statement = sg_parser_get_statement(info.parser)))
    {
      if (SG_PARSER_STATEMENT_IS_END(info.statement))
	{
	  switch (SG_PARSER_SCOPE(info.parser))
	    {
	    case STATEMENT_HANDLER:
	      st_config_load_handler_end(&info);
	      break;

	    case STATEMENT_HANDLER_FIELD:
	      st_config_load_handler_field_end(&info);
	      break;

	    case STATEMENT_HANDLER_KEY:
	      st_config_load_handler_key_end(&info);
	      break;
	    }
	}
    
      if (info.statement->definition)
	{
	  switch (info.statement->definition->id)
	    {
	    case STATEMENT_OPTIONS_VIEW_TOOLBAR:
	      st_settings.view_toolbar = g_value_get_boolean(&info.statement->value);
	      break;
	
	    case STATEMENT_OPTIONS_VIEW_TABS:
	      st_settings.view_tabs = g_value_get_boolean(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_VIEW_TAB_ICONS:
	      st_settings.view_tab_icons = g_value_get_boolean(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_VIEW_STATUSBAR:
	      st_settings.view_statusbar = g_value_get_boolean(&info.statement->value);
	      break;
	      
	    case STATEMENT_OPTIONS_TOOLBAR_STYLE:
	      st_settings.toolbar_style = g_value_get_int(&info.statement->value);
	      break;
	      
	    case STATEMENT_OPTIONS_TOOLBAR_SIZE:
	      st_settings.toolbar_size = g_value_get_int(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_WINDOW_WIDTH:	/* deprecated */
	    case STATEMENT_OPTIONS_MAIN_WINDOW_WIDTH:
	      st_settings.main_window_width = g_value_get_int(&info.statement->value);
	      break;
	      
	    case STATEMENT_OPTIONS_WINDOW_HEIGHT:	/* deprecated */
	    case STATEMENT_OPTIONS_MAIN_WINDOW_HEIGHT:
	      st_settings.main_window_height = g_value_get_int(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_PREFERENCES_WINDOW_WIDTH:
	      st_settings.preferences_window_width = g_value_get_int(&info.statement->value);
	      break;
	      
	    case STATEMENT_OPTIONS_PREFERENCES_WINDOW_HEIGHT:
	      st_settings.preferences_window_height = g_value_get_int(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_PREFERENCES_SELECTED_PAGE:
	      st_settings.preferences_selected_page = g_value_dup_string(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_PREFERENCES_PLUGINS_EXPANDED:
	      st_settings.preferences_plugins_expanded = g_value_get_boolean(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_STREAM_PROPERTIES_WINDOW_WIDTH:
	      st_settings.stream_properties_window_width = g_value_get_int(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_STREAM_COLUMNS_WINDOW_WIDTH:
	      st_settings.stream_columns_window_width = g_value_get_int(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_STREAM_COLUMNS_WINDOW_HEIGHT:
	      st_settings.stream_columns_window_height = g_value_get_int(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_PROXY_ENABLED:
	      st_settings.proxy_enabled = g_value_get_boolean(&info.statement->value);
	      break;
	      
	    case STATEMENT_OPTIONS_PROXY_TYPE:
	      st_settings.proxy_type = g_value_get_int(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_PROXY_URL:
	      {
		const char *proxy_url;
		char *server = NULL;
		int port = 0;
		
		proxy_url = g_value_get_string(&info.statement->value);
		if (proxy_url)
		  {
		    const char *protocol;
		    const char *portstr;

		    /* skip protocol */

		    protocol = strstr(proxy_url, "://");
		    if (protocol)
		      proxy_url = protocol + 3;

		    /* extract port */

		    portstr = strrchr(proxy_url, ':');
		    if (portstr)
		      {
			server = g_strndup(proxy_url, portstr - proxy_url);
			port = atoi(portstr + 1);
		      }
		    else
		      server = g_strdup(proxy_url);
		  }

		g_free(st_settings.proxy_server);
		st_settings.proxy_server = server;
		st_settings.proxy_port = port;

		break;
	      }

	    case STATEMENT_OPTIONS_PROXY_SERVER:
	      g_free(st_settings.proxy_server);
	      st_settings.proxy_server = g_value_dup_string(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_PROXY_PORT:
	      st_settings.proxy_port = g_value_get_int(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_PROXY_AUTH_ENABLED:
	      st_settings.proxy_auth_enabled = g_value_get_boolean(&info.statement->value);
	      break;
	      
	    case STATEMENT_OPTIONS_PROXY_AUTH_NAME:
	      g_free(st_settings.proxy_auth_name);
	      st_settings.proxy_auth_name = g_value_dup_string(&info.statement->value);
	      break;
	      
	    case STATEMENT_OPTIONS_PROXY_AUTH_PASSWORD:
	      g_free(st_settings.proxy_auth_password);
	      st_settings.proxy_auth_password = g_value_dup_string(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_VIEW_MENUBAR:
	    case STATEMENT_OPTIONS_TOOLBAR_THEME:
	    case STATEMENT_OPTIONS_GALEON_THEMES_ENABLED:
	    case STATEMENT_OPTIONS_GALEON_THEMES_SYSTEM_DIR:
	    case STATEMENT_OPTIONS_GALEON_THEMES_USER_DIR:
	    case STATEMENT_HANDLER_SELECTED_CATEGORY:
	    case STATEMENT_HANDLER_SELECTED_STREAM:
	    case STATEMENT_HANDLER_EXPANDED_CATEGORIES:
	    case STATEMENT_HANDLER_SELECTED_STREAMS:
	    case STATEMENT_OPTIONS_SAVE_AUTOMATIC:
	    case STATEMENT_OPTIONS_SELECTED_PREFERENCES_PAGE:
	      g_scanner_warn(info.parser->scanner, _("obsolete statement, ignored"));
	      break;

	    case STATEMENT_OPTIONS_SELECTED_HANDLER:
	      g_free(st_settings.selected_handler_name);
	      st_settings.selected_handler_name = g_value_dup_string(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_ALWAYS_REFRESH: /* deprecated */
	    case STATEMENT_OPTIONS_ALWAYS_RELOAD:
	      st_settings.always_reload = g_value_get_boolean(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_FIND_TOKEN:
	      g_free(st_settings.find_token);
	      st_settings.find_token = g_value_dup_string(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_FIND_CASE_SENSITIVE:
	      st_settings.find_case_sensitive = g_value_get_boolean(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_FIND_WRAP_AROUND:
	      st_settings.find_wrap_around = g_value_get_boolean(&info.statement->value);
	      break;

	    case STATEMENT_OPTIONS_MUSIC_DIR:
	      g_free(st_settings.music_dir);
	      st_settings.music_dir = g_value_dup_string(&info.statement->value);
	      break;

	    case STATEMENT_ACTION:
	      g_free(info.action);
	      info.action = g_value_dup_string(&info.statement->value);
	      if (! info.action)
		g_scanner_warn(info.parser->scanner, _("action name missing"));
	      break;

	    case STATEMENT_ACTION_COMMAND:
	      st_config_load_command(&info);
	      break;

	    case STATEMENT_ACTION_PROGRAM:
	      st_config_load_program(&info);
	      break;
	      
	    case STATEMENT_HANDLER:
	      st_config_load_handler_begin(&info);
	      break;
	      
	    case STATEMENT_HANDLER_FIELDS_SORT_INDEX:
	      if (info.handler)
		{
		  int sort_index = g_value_get_int(&info.statement->value);
		  
		  if (! st_handler_is_dummy(info.handler) && sort_index > st_handler_count_fields(info.handler, ST_HANDLER_FIELD_VISIBLE))
		    g_scanner_warn(info.parser->scanner, _("index too big, ignored"));
		  else
		    st_handler_set_fields_sort_index(info.handler, sort_index);
		}
	      break;
	      
	    case STATEMENT_HANDLER_FIELDS_SORT_ORDER:
	      if (info.handler)
		st_handler_set_fields_sort_order(info.handler, g_value_get_int(&info.statement->value));
	      break;
	      
	    case STATEMENT_HANDLER_PANED_POSITION:
	      if (info.handler)
		st_handler_set_paned_position(info.handler, g_value_get_int(&info.statement->value));
	      break;

	    case STATEMENT_HANDLER_FIELD:
	      st_config_load_handler_field_begin(&info);
	      break;

	    case STATEMENT_HANDLER_FIELD_VISIBLE:
	      if (info.handler_field)
		st_handler_field_set_user_visible(info.handler_field, g_value_get_boolean(&info.statement->value));
	      break;

	    case STATEMENT_HANDLER_FIELD_WIDTH:
	      if (info.handler_field)
		st_handler_field_set_width(info.handler_field, g_value_get_int(&info.statement->value));
	      break;

	    case STATEMENT_HANDLER_FIELD_POSITION:
	      if (info.handler_field)
		st_handler_field_set_position(info.handler_field, g_value_get_int(&info.statement->value));
	      break;

	    case STATEMENT_HANDLER_KEY:
	      st_config_load_handler_key_begin(&info);
	      break;
	    }
	}
      else if (G_IS_VALUE(&info.statement->value))
	{
	  switch (SG_PARSER_SCOPE(info.parser))
	    {
	    case STATEMENT_OPTIONS_FIND_HISTORY:
	      st_config_load_find_history(&info);
	      break;

	    case STATEMENT_HANDLER_FIELDS_WIDTH:
	      st_config_load_handler_field_width(&info);
	      break;
	      
	    case STATEMENT_HANDLER_EXPANDED_CATEGORIES:
	    case STATEMENT_HANDLER_SELECTED_STREAMS:
	      /* obsolete */
	      break;

	    case STATEMENT_HANDLER_KEY:
	      st_config_load_handler_key_value(&info);
	      break;

	    default:
	      g_scanner_warn(info.parser->scanner, _("unexpected value"));
	    }
	}

      sg_parser_statement_evaluate(info.statement);
      sg_parser_statement_free(info.statement);
    }
  
  sg_parser_free(info.parser);
  g_free(info.action);
  st_handlers_reorder(info.handlers);
  g_free(info.handler_config_key);
  if (info.handler_config_values)
    g_value_array_free(info.handler_config_values);
  
  return TRUE;
}

static void
st_config_load_find_history (LoadInfo *info)
{
  if (! G_VALUE_HOLDS_STRING(&info->statement->value))
    {
      g_scanner_warn(info->parser->scanner, _("expected string value"));
      return;
    }

  if (g_slist_length(st_settings.find_history) < ST_SETTINGS_FIND_HISTORY_MAX_LENGTH)
    st_settings.find_history = g_slist_append(st_settings.find_history, g_value_dup_string(&info->statement->value));
  else
    g_scanner_warn(info->parser->scanner, _("too many find history tokens"));
}

static void
st_config_load_handler_begin (LoadInfo *info)
{
  const char *name;

  name = g_value_get_string(&info->statement->value);
  if (name)
    {
      info->handler = st_handlers_find_by_name(name);
  
      if (info->handler)
	info->handler_field_iter = st_handler_get_fields(info->handler);
      else
	{
	  info->handler = st_handler_new_dummy(name);
	  g_scanner_warn(info->parser->scanner, _("%s: no such handler"), name);
	}

      info->handlers = g_slist_append(info->handlers, info->handler);
    }
  else
    {
      info->handler = NULL;
      g_scanner_warn(info->parser->scanner, _("handler name missing"));
    }
}

static void
st_config_load_handler_end (LoadInfo *info)
{
  info->handler = NULL;
  info->handler_field_iter = NULL;
  info->handler_field = NULL;
}

static void
st_config_load_handler_field_begin (LoadInfo *info)
{
  if (! info->handler)
    return;
  
  if (st_handler_is_dummy(info->handler))
    {
      info->handler_field = st_handler_field_new_dummy();
      st_handler_add_field(info->handler, info->handler_field);
    }
  else
    {
      while (info->handler_field_iter)
	{
	  STHandlerField *field = info->handler_field_iter->data;
      
	  if (ST_HANDLER_FIELD_IS_VISIBLE(field))
	    break;
      
	  info->handler_field_iter = info->handler_field_iter->next;
	}
		  
      if (info->handler_field_iter)
	info->handler_field = info->handler_field_iter->data;
      else
	{
	  info->handler_field = NULL;
	  g_scanner_warn(info->parser->scanner, _("too many fields"));
	}
    }
}

static void
st_config_load_handler_field_end (LoadInfo *info)
{
  if (info->handler_field_iter)
    info->handler_field_iter = info->handler_field_iter->next;
  /* otherwise it's a dummy handler, or there has been a "too many fields" error -> nop */
}

static void
st_config_load_command (LoadInfo *info)
{
  if (! info->action)
    return;

  st_action_associate(info->action, g_value_get_string(&info->statement->value));
}

static void
st_config_load_program (LoadInfo *info)
{
  const char *command;
  char *new_command;

  if (! info->action)
    return;

  command = g_value_get_string(&info->statement->value);
  new_command = st_action_convert_old_style_command(command);

  st_action_associate(info->action, new_command);
  g_free(new_command);
}

static void
st_config_load_handler_field_width (LoadInfo *info)
{
  STHandlerField *field = NULL;

  if (! info->handler)
    return;

  if (! G_VALUE_HOLDS_INT(&info->statement->value))
    {
      g_scanner_warn(info->parser->scanner, _("expected integer value"));
      return;
    }

  if (st_handler_is_dummy(info->handler))
    {
      field = st_handler_field_new_dummy();
      st_handler_add_field(info->handler, field);
    }
  else
    while (info->handler_field_iter)
      {
	field = info->handler_field_iter->data;

	info->handler_field_iter = info->handler_field_iter->next;
	
	if (ST_HANDLER_FIELD_IS_VISIBLE(field))
	  break;
	else
	  field = NULL;
      }
  
  if (field)
    st_handler_field_set_width(field, g_value_get_int(&info->statement->value));
  else
    g_scanner_warn(info->parser->scanner, _("too many fields"));
}

static void
st_config_load_handler_key_begin (LoadInfo *info)
{
  if (! info->handler)
    return;

  if (st_handler_is_dummy(info->handler))
    {
      g_free(info->handler_config_key);
      info->handler_config_key = g_value_dup_string(&info->statement->value);
    }
  else
    {
      info->handler_config_pspec = st_handler_config_lookup(info->handler, g_value_get_string(&info->statement->value));
      if (! info->handler_config_pspec)
	{
	  g_scanner_warn(info->parser->scanner, _("unknown configuration key"));
	  return;
	}
    }
  
  if (info->handler_config_values)
    g_value_array_free(info->handler_config_values);
  info->handler_config_values = g_value_array_new(0);
}

static void
st_config_load_handler_key_value (LoadInfo *info)
{
  if (! info->handler)
    return;

  g_value_array_append(info->handler_config_values, &info->statement->value);
}

static void
st_config_load_handler_key_end (LoadInfo *info)
{
  GParamSpec *pspec;

  if (! info->handler)
    return;

  if (st_handler_is_dummy(info->handler))
    {
      if (info->handler_config_values->n_values == 0
	  || info->handler_config_values->n_values > 1)
	pspec = g_param_spec_value_array(info->handler_config_key,
					 NULL,
					 NULL,
					 NULL,
					 G_PARAM_READWRITE);
      else
	{
	  GValue *value = g_value_array_get_nth(info->handler_config_values, 0);

	  /* keep in sync with st_handler_config_register() */
	  if (G_VALUE_HOLDS_BOOLEAN(value))
	    pspec = g_param_spec_boolean(info->handler_config_key,
					 NULL,
					 NULL,
					 FALSE,
					 G_PARAM_READWRITE);
	  else if (G_VALUE_HOLDS_INT(value))
	    pspec = g_param_spec_int(info->handler_config_key,
				     NULL,
				     NULL,
				     G_MININT,
				     G_MAXINT,
				     0,
				     G_PARAM_READWRITE);
	  else if (G_VALUE_HOLDS_UINT(value))
	    pspec = g_param_spec_uint(info->handler_config_key,
				      NULL,
				      NULL,
				      0,
				      G_MAXUINT,
				      0,
				      G_PARAM_READWRITE);
	  else if (G_VALUE_HOLDS_DOUBLE(value))
	    pspec = g_param_spec_double(info->handler_config_key,
					NULL,
					NULL,
					G_MINDOUBLE,
					G_MAXDOUBLE,
					0,
					G_PARAM_READWRITE);
	  else if (G_VALUE_HOLDS_STRING(value))
	    pspec = g_param_spec_string(info->handler_config_key,
					NULL,
					NULL,
					NULL,
					G_PARAM_READWRITE);
	  else
	    {
	      g_scanner_warn(info->parser->scanner, _("invalid value type"));
	      return;
	    }
	}

      st_handler_config_register(info->handler, pspec);
    }
  else
    {
      pspec = info->handler_config_pspec;
      if (! pspec)
	return;
    }

  if (G_PARAM_SPEC_VALUE_TYPE(pspec) == G_TYPE_VALUE_ARRAY)
    {
      GValue value = { 0, };

      g_value_init(&value, G_TYPE_VALUE_ARRAY);
      g_value_set_boxed(&value, info->handler_config_values);
      st_handler_config_set_value(info->handler, g_param_spec_get_name(pspec), &value);
      g_value_unset(&value);
    }
  else
    {
      GValue *value;

      if (info->handler_config_values->n_values != 1)
	{
	  g_scanner_warn(info->parser->scanner, _("wrong number of values"));
	  return;
	}
      
      value = g_value_array_get_nth(info->handler_config_values, 0);
      
      if (G_VALUE_HOLDS(value, G_PARAM_SPEC_VALUE_TYPE(pspec)))
	st_handler_config_set_value(info->handler, g_param_spec_get_name(pspec), value);
      else
	g_scanner_warn(info->parser->scanner, _("invalid value type"));
    }
}
