/*
 * Copyright (c) 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-util.h"
#include "st-thread.h"
#include "st-action.h"

/*** variable declarations ***************************************************/

static GHashTable *actions;

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

static void st_action_register_real (const char *id,
				     const char *label,
				     const char *command);

static void st_action_list_cb (gpointer key,
			       gpointer value,
			       gpointer user_data);

static char *st_action_subst (const char *command, const char *uri);

/*** API implementation ******************************************************/

/**
 * st_action_register:
 * @id: an unique id for the action.
 * @label: a human-readable label for the action.
 * @command: the default command for the action, or %NULL.
 *
 * Registers a new action. The action will appear in the applications
 * page of the streamtuner preferences, and the user will be able to
 * modify its associated command.
 **/
void
st_action_register (const char *id, const char *label, const char *command)
{
  g_return_if_fail(id != NULL);
  g_return_if_fail(label != NULL);

  st_action_register_real(id, label, command);
}

/**
 * st_action_run:
 * @id: the id of the action to run.
 * @uri: the URI to run the action on.
 * @err: a location to report errors, or %NULL.
 *
 * Runs action @id with @uri as parameter. The action must have been
 * previously registered with st_action_register().
 *
 * Return value: %TRUE on success, %FALSE on failure.
 **/
gboolean
st_action_run (const char *id, const char *uri, GError **err)
{
  STAction *action;
  gboolean status;
  char *subst;

  g_return_val_if_fail(id != NULL, FALSE);
  g_return_val_if_fail(uri != NULL, FALSE);

  action = g_hash_table_lookup(actions, id);
  g_return_val_if_fail(action != NULL, FALSE);
  g_return_val_if_fail(action->label != NULL, FALSE);

  g_mutex_lock(action->mutex);
  subst = action->command ? st_action_subst(action->command, uri) : NULL;
  g_mutex_unlock(action->mutex);

  if (subst)
    {
      status = g_spawn_command_line_async(subst, err);
      g_free(subst);
    }
  else
    {
      status = FALSE;
      g_set_error(err, 0, 0, _("no command is set for \"%s\""), action->label);
    }

  return status;
}

/*** private implementation **************************************************/

void
st_action_init (void)
{
  actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);

  st_action_register("view-web", _("Open a web page"), "epiphany %q");
  st_action_register("view-ghelp", _("Open a GNOME help document"), "yelp %q");
}

static void
st_action_register_real (const char *id,
			 const char *label,
			 const char *command)
{
  g_return_if_fail(id != NULL);

  if (! g_hash_table_lookup(actions, id))
    {				/* don't register an action twice */
      STAction *action;

      action = g_new(STAction, 1);
      action->mutex = g_mutex_new();
      action->label = g_strdup(label);
      action->command = g_strdup(command);

      g_hash_table_insert(actions, g_strdup(id), action);
    }
}

void
st_action_associate (const char *id, const char *command)
{
  STAction *action;

  g_return_if_fail(id != NULL);

  action = g_hash_table_lookup(actions, id);
  if (action)
    {
      g_mutex_lock(action->mutex);
      g_free(action->command);
      action->command = g_strdup(command);
      g_mutex_unlock(action->mutex);
    }
  else
    st_action_register_real(id, NULL, command);	/* dummy action */
}

GSList *
st_action_list (void)
{
  GSList *list = NULL;

  g_hash_table_foreach(actions, st_action_list_cb, &list);

  return list;
}

static void
st_action_list_cb (gpointer key, gpointer value, gpointer user_data)
{
  GSList **list = user_data;

  *list = g_slist_append(*list, key);
}

STAction *
st_action_get (const char *id)
{
  g_return_val_if_fail(id != NULL, NULL);

  return g_hash_table_lookup(actions, id);
}

static char *
st_action_subst (const char *command, const char *uri)
{
  GString *string;
  gboolean attn = FALSE;
  int i;
  char *quoted_uri = NULL;

  g_return_val_if_fail(command != NULL, NULL);
  g_return_val_if_fail(uri != NULL, NULL);

  string = g_string_new(NULL);

  for (i = 0; command[i]; i++)
    if (attn)
      {
	attn = FALSE;
	switch (command[i])
	  {
	  case 'u':
	    g_string_append(string, uri);
	    break;
	    
	  case 'q':
	    if (! quoted_uri)
	      quoted_uri = g_shell_quote(uri);
	    g_string_append(string, quoted_uri);
	    break;

	  case '%':
	    g_string_append_c(string, '%');
	    break;
	    
	  default:
	    g_string_append_printf(string, "%%%c", command[i]);
	  }
      }
    else if (command[i] == '%')
      attn = TRUE;
    else
      g_string_append_c(string, command[i]);

  g_free(quoted_uri);

  return g_string_free(string, FALSE);
}

char *
st_action_convert_old_style_command (const char *command)
{
  GString *string;
  int i;
  gboolean quoted = FALSE;

  if (! command)
    return NULL;

  string = g_string_new(NULL);
  
  for (i = 0; command[i]; i++)
    {
      if (! quoted && command[i] == '\\')
	quoted = TRUE;
      else if (! quoted && command[i] == '%')
	g_string_append(string, "%u");
      else
	{
	  quoted = FALSE;
	  g_string_append_c(string, command[i]);
	}
    }

  return g_string_free(string, FALSE);
}
