/*
 * 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 <glib.h>
#include <glib/gi18n.h>
#include "sg-dumper.h"
#include "sg-util.h"
#include "st-handler.h"
#include "st-handler-field.h"
#include "st-handlers.h"
#include "st-action.h"
#include "st-settings.h"

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

typedef struct
{
  gboolean	status;
  SGDumper	*dumper;
  GError	**err;
} SaveHandlerConfigInfo;

/*** function declarations ***************************************************/

static gboolean st_config_save_find_history	(SGDumper	*dumper,
						 GError		**err);
static gboolean st_config_save_actions		(SGDumper	*dumper,
						 GError		**err);
static gboolean st_config_save_handler		(SGDumper	*dumper,
						 STHandler	*handler,
						 GError		**err);
static void	st_config_save_handler_config_cb (GParamSpec *pspec,
						  const GValue     *value,
						  gpointer         data);

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

gboolean
st_config_save (const char *filename, GError **err)
{
  GSList *l;
  SGDumper *dumper;

  g_return_val_if_fail(filename != NULL, FALSE);

  dumper = sg_dumper_new(filename, err);
  if (! dumper)
    return FALSE;

  if (! sg_dumper_write(dumper, err,
			"#", _("streamtuner configuration file, automatically generated"),
			"",
			"k{", "options",
			"kb", "view_toolbar", st_settings.view_toolbar,
			"kb", "view_tabs", st_settings.view_tabs,
			"kb", "view_tab_icons", st_settings.view_tab_icons,
			"kb", "view_statusbar", st_settings.view_statusbar,
			"ki", "toolbar_style", st_settings.toolbar_style,
			"ki", "toolbar_size", st_settings.toolbar_size,
			"kb", "always_reload", st_settings.always_reload,
			"ki", "main_window_width", st_settings.main_window_width,
			"ki", "main_window_height", st_settings.main_window_height,
			"ki", "preferences_window_width", st_settings.preferences_window_width,
			"ki", "preferences_window_height", st_settings.preferences_window_height,
			"ks", "preferences_selected_page", st_settings.preferences_selected_page,
			"kb", "preferences_plugins_expanded", st_settings.preferences_plugins_expanded,
			"ki", "stream_properties_window_width", st_settings.stream_properties_window_width,
			"ki", "stream_columns_window_width", st_settings.stream_columns_window_width,
			"ki", "stream_columns_window_height", st_settings.stream_columns_window_height,
			"kb", "proxy_enabled", st_settings.proxy_enabled,
			"ki", "proxy_type", st_settings.proxy_type,
			"ks", "proxy_server", st_settings.proxy_server,
			"ki", "proxy_port", st_settings.proxy_port,
			"kb", "proxy_auth_enabled", st_settings.proxy_auth_enabled,
			"ks", "proxy_auth_name", st_settings.proxy_auth_name,
			"ks", "proxy_auth_password", st_settings.proxy_auth_password,
			"ks", "find_token", st_settings.find_token,
			"kb", "find_case_sensitive", st_settings.find_case_sensitive,
			"kb", "find_wrap_around", st_settings.find_wrap_around,
			"ks", "music_dir", st_settings.music_dir,
			NULL))
    goto error;

  if (! st_config_save_find_history(dumper, err))
    goto error;

  if (st_settings.selected_handler_name)
    {
      if (! sg_dumper_write(dumper, err,
			    "ks", "selected_handler", st_settings.selected_handler_name,
			    NULL))
	goto error;
    }
  
  if (! sg_dumper_write(dumper, err, "}", NULL))
    goto error;
  
  if (! st_config_save_actions(dumper, err))
    goto error;

  SG_LIST_FOREACH(l, st_handlers_full_list)
    if (! st_config_save_handler(dumper, l->data, err))
      goto error;

  /* everything went fine */

  return sg_dumper_free(dumper, err);

 error:
  sg_dumper_free(dumper, NULL);	/* ignore errors since ERR is already set */
  return FALSE;
}

static gboolean
st_config_save_find_history (SGDumper *dumper, GError **err)
{
  GSList *l;

  if (! sg_dumper_write(dumper, err, "k{", "find_history", NULL))
    return FALSE;

  SG_LIST_FOREACH(l, st_settings.find_history)
    {
      const char *token = l->data;

      if (! sg_dumper_write(dumper, err, "s", token, NULL))
	return FALSE;
    }

  return sg_dumper_write(dumper, err, "}", NULL);
}

static gboolean
st_config_save_actions (SGDumper *dumper, GError **err)
{
  GSList *actions;
  GSList *l;
  gboolean status = TRUE;

  actions = st_action_list();
  SG_LIST_FOREACH(l, actions)
    {
      const char *id = l->data;
      STAction *action;

      action = st_action_get(id);
      g_return_val_if_fail(action != NULL, FALSE);

      if (! sg_dumper_write(dumper, err,
			    "ks{", "action", id,
			    "ks", "command", action->command,
			    "}",
			    NULL))
	{
	  status = FALSE;
	  goto end;
	}
    }

 end:
  g_slist_free(actions);
  return status;
}

static gboolean
st_config_save_handler (SGDumper *dumper, STHandler *handler, GError **err)
{
  GSList *l;
  SaveHandlerConfigInfo info;

  g_return_val_if_fail(dumper != NULL, FALSE);
  g_return_val_if_fail(ST_IS_HANDLER(handler), FALSE);

  if (! sg_dumper_write(dumper, err,
			"ks{", "handler", st_handler_get_name(handler),
			"ki", "fields_sort_index", st_handler_get_fields_sort_index(handler),
			"ki", "fields_sort_order", st_handler_get_fields_sort_order(handler),
			"ki", "paned_position", st_handler_get_paned_position(handler),
			NULL))
    return FALSE;

  SG_LIST_FOREACH(l, st_handler_get_fields(handler))
    {
      STHandlerField *field = l->data;

      if (ST_HANDLER_FIELD_IS_VISIBLE(field))
	{
	  if (! sg_dumper_write(dumper, err,
				"k{", "field",
				"kb", "visible", st_handler_field_get_user_visible(field),
				"ki", "width", st_handler_field_get_width(field),
				"ki", "position", st_handler_field_get_position(field),
				"}",
				NULL))
	    return FALSE;
	}
    }

  info.status = TRUE;
  info.dumper = dumper;
  info.err = err;

  st_handler_config_foreach(handler, st_config_save_handler_config_cb, &info);

  if (! info.status)
    return FALSE;

  return sg_dumper_write(dumper, err, "}", NULL);
}

static void
st_config_save_handler_config_cb (GParamSpec *pspec,
				  const GValue *value,
				  gpointer data)
{
  SaveHandlerConfigInfo *info = data;

  if (info->status)
    {
      if (! sg_dumper_write(info->dumper, info->err,
			    "ks{", "key", g_param_spec_get_name(pspec),
			    NULL))
	goto error;
      
      if (G_VALUE_HOLDS(value, G_TYPE_VALUE_ARRAY))
	{
	  GValueArray *value_array;
	  int i;

	  value_array = g_value_get_boxed(value);

	  for (i = 0; i < value_array->n_values; i++)
	    if (! sg_dumper_write(info->dumper, info->err,
				  "v", g_value_array_get_nth(value_array, i),
				  NULL))
	      goto error;
	}
      else
	{
	  if (! sg_dumper_write(info->dumper, info->err,
				"v", value,
				NULL))
	    goto error;
	}

      if (! sg_dumper_write(info->dumper, info->err, "}", NULL))
	goto error;
    }

  return;			/* success */

 error:
  info->status = FALSE;
}
