/*
 * Copyright (c) 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 <glib.h>
#include <glib/gi18n.h>
#include "sg-util.h"
#include "st-action.h"
#include "st-handler.h"
#include "st-handlers-api.h"
#include "st-stream-store.h"
#include "st-util.h"
#include "st-util-api.h"

/*** cpp *********************************************************************/

#define ST_PRESELECTIONS_STREAM(bag) \
  ((STPreselectionsStream *) ST_STREAM((bag)))

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

typedef struct
{
  STStream	stream;

  char		*name;
  char		*genre;
  char		*homepage;
  char		*url;
} STPreselectionsStream;

enum {
  FIELD_NAME,
  FIELD_GENRE,
  FIELD_HOMEPAGE,
  FIELD_URL
};

typedef struct
{
  STStreamBag	*bag;
  gboolean	taken;
} MakeUniqueInfo;

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

STHandler *st_preselections_handler = NULL;

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

static STPreselectionsStream *st_preselections_stream_new_cb (gpointer data);
static void st_preselections_stream_field_get_cb (STPreselectionsStream *stream,
						  STHandlerField *field,
						  GValue *value,
						  gpointer data);
static void st_preselections_stream_field_set_cb (STPreselectionsStream *stream,
						  STHandlerField *field,
						  const GValue *value,
						  gpointer data);
static void st_preselections_stream_stock_field_get_cb (STPreselectionsStream *stream,
							STHandlerStockField stock_field,
							GValue *value,
							gpointer data);
static gboolean st_preselections_stream_modify_cb (STPreselectionsStream *stream,
						   GSList *fields,
						   GSList *values,
						   gpointer data,
						   GError **err);
static gboolean st_preselections_stream_delete_cb (STPreselectionsStream *stream,
						   gpointer data,
						   GError **err);
static void st_preselections_stream_free_cb (STPreselectionsStream *stream,
					     gpointer data);

static gboolean st_preselections_stream_tune_in_cb (STPreselectionsStream *stream,
						    gpointer data,
						    GError **err);
static gboolean st_preselections_stream_record_cb (STPreselectionsStream *stream,
						   gpointer data,
						   GError **err);
static gboolean st_preselections_stream_browse_cb (STPreselectionsStream *stream,
						   gpointer data,
						   GError **err);

static void st_preselections_init_handler (void);

static void st_preselections_make_unique_names (STStreamBag *bag,
						STStreamStore *preselections);
static gboolean st_preselections_make_unique_name_cb (STStreamStore *store,
						      STStreamBag *bag,
						      gpointer data);
static gboolean st_preselections_make_unique_visible_name_cb (STStreamStore *store,
							      STStreamBag *bag,
							      gpointer data);

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

static STPreselectionsStream *
st_preselections_stream_new_cb (gpointer data)
{
  return g_new0(STPreselectionsStream, 1);
}

static void
st_preselections_stream_field_get_cb (STPreselectionsStream *stream,
				      STHandlerField *field,
				      GValue *value,
				      gpointer data)
{
  switch (field->id)
    {
    case FIELD_NAME:
      g_value_set_string(value, stream->name);
      break;

    case FIELD_GENRE:
      g_value_set_string(value, stream->genre);
      break;

    case FIELD_HOMEPAGE:
      g_value_set_string(value, stream->homepage);
      break;

    case FIELD_URL:
      g_value_set_string(value, stream->url);
      break;

    default:
      g_return_if_reached();
    }
}

static void
st_preselections_stream_field_set_cb (STPreselectionsStream *stream,
				      STHandlerField *field,
				      const GValue *value,
				      gpointer data)
{
  switch (field->id)
    {
    case FIELD_NAME:
      g_free(stream->name);
      stream->name = g_value_dup_string(value);
      break;

    case FIELD_GENRE:
      g_free(stream->genre);
      stream->genre = g_value_dup_string(value);
      break;

    case FIELD_HOMEPAGE:
      g_free(stream->homepage);
      stream->homepage = g_value_dup_string(value);
      break;

    case FIELD_URL:
      g_free(stream->url);
      stream->url = g_value_dup_string(value);
      break;

    default:
      g_return_if_reached();
    }
}

static void
st_preselections_stream_stock_field_get_cb (STPreselectionsStream *stream,
					    STHandlerStockField stock_field,
					    GValue *value,
					    gpointer data)
{
  switch (stock_field)
    {
    case ST_HANDLER_STOCK_FIELD_NAME:
      g_value_set_string(value, stream->name);
      break;

    case ST_HANDLER_STOCK_FIELD_GENRE:
      g_value_set_string(value, stream->genre);
      break;

    case ST_HANDLER_STOCK_FIELD_DESCRIPTION:
      /* nop */
      break;

    case ST_HANDLER_STOCK_FIELD_HOMEPAGE:
      g_value_set_string(value, stream->homepage);
      break;

    case ST_HANDLER_STOCK_FIELD_URI_LIST:
      {
	GValueArray *value_array;
	GValue uri_value = { 0, };

	value_array = g_value_array_new(1);

	g_value_init(&uri_value, G_TYPE_STRING);
	g_value_set_string(&uri_value, stream->url);
	g_value_array_append(value_array, &uri_value);
	g_value_unset(&uri_value);

	g_value_take_boxed(value, value_array);

	break;
      }
    }
}

static gboolean
st_preselections_stream_modify_cb (STPreselectionsStream *stream,
				   GSList *fields,
				   GSList *values,
				   gpointer data,
				   GError **err)
{
  GSList *field = fields;
  GSList *value = values;

  while (field && value)
    {
      st_preselections_stream_field_set_cb(stream, field->data, value->data, NULL);

      field = field->next;
      value = value->next;
    }

  return TRUE;
}

static gboolean
st_preselections_stream_delete_cb (STPreselectionsStream *stream,
				   gpointer data,
				   GError **err)
{
  return TRUE;			/* nop */
}

static void
st_preselections_stream_free_cb (STPreselectionsStream *stream, gpointer data)
{
  g_free(stream->name);
  g_free(stream->homepage);
  g_free(stream->url);

  st_stream_free((STStream *) stream);
}

static gboolean
st_preselections_stream_tune_in_cb (STPreselectionsStream *stream,
				    gpointer data,
				    GError **err)
{
  if (! stream->url)
    {
      g_set_error(err, 0, 0, _("the stream URL is not set"));
      return FALSE;
    }

  return st_action_run("play-stream", stream->url, err);
}

static gboolean
st_preselections_stream_record_cb (STPreselectionsStream *stream,
				   gpointer data,
				   GError **err)
{
  if (! stream->url)
    {
      g_set_error(err, 0, 0, _("the stream URL is not set"));
      return FALSE;
    }

  return st_action_run("record-stream", stream->url, err);
}

static gboolean
st_preselections_stream_browse_cb (STPreselectionsStream *stream,
				   gpointer data,
				   GError **err)
{
  if (! stream->homepage)
    {
      g_set_error(err, 0, 0, _("the stream homepage is not set"));
      return FALSE;
    }

  return st_action_run("view-web", stream->homepage, err);
}

static void
st_preselections_init_handler (void)
{
  GdkPixbuf *pixbuf;
  STHandlerField *field;

  st_preselections_handler = st_handler_new("preselections");

  st_handler_set_label(st_preselections_handler, _("Preselections"));
  st_handler_set_copyright(st_preselections_handler, "Copyright \302\251 2004 Jean-Yves Lefort");
  st_handler_set_description(st_preselections_handler, _("Manually Edited Streams"));
  st_handler_set_flags(st_preselections_handler, ST_HANDLER_NO_CATEGORIES);

  pixbuf = st_pixbuf_new_from_file(ST_PNG("preselections"));
  if (pixbuf)
    {
      st_handler_set_icon_from_pixbuf(st_preselections_handler, pixbuf);
      g_object_unref(pixbuf);
    }
  
  st_handler_bind(st_preselections_handler, ST_HANDLER_EVENT_STREAM_NEW, st_preselections_stream_new_cb, NULL);
  st_handler_bind(st_preselections_handler, ST_HANDLER_EVENT_STREAM_FIELD_GET, st_preselections_stream_field_get_cb, NULL);
  st_handler_bind(st_preselections_handler, ST_HANDLER_EVENT_STREAM_FIELD_SET, st_preselections_stream_field_set_cb, NULL);
  st_handler_bind(st_preselections_handler, ST_HANDLER_EVENT_STREAM_STOCK_FIELD_GET, st_preselections_stream_stock_field_get_cb, NULL);
  st_handler_bind(st_preselections_handler, ST_HANDLER_EVENT_STREAM_MODIFY, st_preselections_stream_modify_cb, NULL);
  st_handler_bind(st_preselections_handler, ST_HANDLER_EVENT_STREAM_DELETE, st_preselections_stream_delete_cb, NULL);
  st_handler_bind(st_preselections_handler, ST_HANDLER_EVENT_STREAM_FREE, st_preselections_stream_free_cb, NULL);

  st_handler_bind(st_preselections_handler, ST_HANDLER_EVENT_STREAM_TUNE_IN, st_preselections_stream_tune_in_cb, NULL);
  st_handler_bind(st_preselections_handler, ST_HANDLER_EVENT_STREAM_RECORD, st_preselections_stream_record_cb, NULL);
  st_handler_bind(st_preselections_handler, ST_HANDLER_EVENT_STREAM_BROWSE, st_preselections_stream_browse_cb, NULL);

  field = st_handler_field_new(FIELD_NAME,
			       _("Name"),
			       G_TYPE_STRING,
			       ST_HANDLER_FIELD_VISIBLE
			       | ST_HANDLER_FIELD_EDITABLE);
  st_handler_field_set_description(field, _("The preselection name"));
  st_handler_add_field(st_preselections_handler, field);

  field = st_handler_field_new(FIELD_GENRE,
			       _("Genre"),
			       G_TYPE_STRING,
			       ST_HANDLER_FIELD_VISIBLE
			       | ST_HANDLER_FIELD_EDITABLE);
  st_handler_field_set_description(field, _("The preselection genre"));
  st_handler_add_field(st_preselections_handler, field);

  field = st_handler_field_new(FIELD_HOMEPAGE,
			       _("Homepage"),
			       G_TYPE_STRING,
			       ST_HANDLER_FIELD_VISIBLE
			       | ST_HANDLER_FIELD_EDITABLE
			       | ST_HANDLER_FIELD_START_HIDDEN);
  st_handler_field_set_description(field, _("The preselection homepage URL"));
  st_handler_add_field(st_preselections_handler, field);

  field = st_handler_field_new(FIELD_URL,
			       _("URL"),
			       G_TYPE_STRING,
			       ST_HANDLER_FIELD_VISIBLE
			       | ST_HANDLER_FIELD_EDITABLE
			       | ST_HANDLER_FIELD_START_HIDDEN);
  st_handler_field_set_description(field, _("The preselection listen URL"));
  st_handler_add_field(st_preselections_handler, field);

  st_handlers_add(st_preselections_handler);
}

void
st_preselections_init (void)
{
  st_preselections_init_handler();
  
  st_action_register("play-stream", _("Listen to a stream"), "xmms %q");
  st_action_register("record-stream", _("Record a stream"), "xterm -e streamripper %q");
  st_action_register("view-web", _("Open a web page"), "epiphany %q");
}

void
st_preselections_add_stock (void)
{
  STStreamStore *preselections;

  preselections = st_handler_get_streams(st_preselections_handler, ST_CATEGORY_BAG_MAIN);
  if (! preselections)
    {
      int i;
      const struct 
      {
	const char	*name;
	const char	*genre;
	const char	*homepage;
	const char	*url;
      } entries[] = {
	{
	  "Virgin Radio 1215AM",
	  "Eclectic",
	  "http://www.virginradio.co.uk/",
	  "http://ogg.smgradio.com/vr160.ogg"
	},
	{
	  "Virgin Radio Classic Rock",
	  "Classic Rock",
	  "http://www.virginradio.co.uk/",
	  "http://ogg.smgradio.com/vc160.ogg"
	},
	{
	  "The Virgin Radio Groove",
	  "Soul",
	  "http://www.virginradio.co.uk/",
	  "http://ogg.smgradio.com/gr160.ogg"
	},
	{
	  "Kill Radio",
	  "Eclectic",
	  "http://www.killradio.org/",
	  "http://slaptech.net:8000/killradio64.mp3"
	},
	{
	  "DFM Radio Television International",
	  "Alternative, Experimental",
	  "http://dfm.nu/",
	  "http://194.109.209.34:8000/dfm_1"
	},
	{
	  "Blazeradio",
	  "Eclectic",
	  "http://blazeradio.newsroom.uab.edu/",
	  "http://blazeradio.newsroom.uab.edu:8000"
	}
      };
	    
      preselections = st_stream_store_new(st_preselections_handler);

      for (i = 0; i < G_N_ELEMENTS(entries); i++)
	{
	  STStreamBag *stream_bag = st_stream_bag_new(st_preselections_handler);

	  ST_PRESELECTIONS_STREAM(stream_bag)->name = g_strdup(entries[i].name);
	  ST_PRESELECTIONS_STREAM(stream_bag)->genre = g_strdup(entries[i].genre);
	  ST_PRESELECTIONS_STREAM(stream_bag)->homepage = g_strdup(entries[i].homepage);
	  ST_PRESELECTIONS_STREAM(stream_bag)->url = g_strdup(entries[i].url);
	  ST_STREAM(stream_bag)->name = g_strdup_printf("%i", i);

	  st_stream_store_append(preselections, stream_bag);
	  g_object_unref(stream_bag);
	}

      st_handler_set_streams(st_preselections_handler, ST_CATEGORY_BAG_MAIN, preselections);
    }

  g_object_unref(preselections);
}

static void
st_preselections_make_unique_names (STStreamBag *bag, STStreamStore *preselections)
{
  MakeUniqueInfo info = { bag };
  int i;

  g_return_if_fail(ST_IS_STREAM_BAG(bag));
  g_return_if_fail(ST_IS_STREAM_STORE(preselections));

  i = 0;
  do
    {
      g_free(ST_STREAM(bag)->name);
      ST_STREAM(bag)->name = g_strdup_printf("%i", i++);

      info.taken = FALSE;
      st_stream_store_foreach(preselections, st_preselections_make_unique_name_cb, &info);
    }
  while (info.taken);

  i = 0;
  do
    {
      g_free(ST_PRESELECTIONS_STREAM(bag)->name);
      ST_PRESELECTIONS_STREAM(bag)->name = ++i == 1
	? g_strdup(_("Unnamed preselection"))
	: g_strdup_printf(_("Unnamed preselection #%i"), i);

      info.taken = FALSE;
      st_stream_store_foreach(preselections, st_preselections_make_unique_visible_name_cb, &info);
    }
  while (info.taken);
}

static gboolean
st_preselections_make_unique_name_cb (STStreamStore *store,
				      STStreamBag *bag,
				      gpointer data)
{
  MakeUniqueInfo *info = data;
  return info->taken = ! strcmp(ST_STREAM(bag)->name, ST_STREAM(info->bag)->name);
}

static gboolean
st_preselections_make_unique_visible_name_cb (STStreamStore *store,
					      STStreamBag *bag,
					      gpointer data)
{
  MakeUniqueInfo *info = data;
  return info->taken = ! strcmp(ST_PRESELECTIONS_STREAM(bag)->name, ST_PRESELECTIONS_STREAM(info->bag)->name);
}

STStreamBag *
st_preselections_new (void)
{
  STStreamStore *preselections;
  STStreamBag *stream_bag;
  
  preselections = st_handler_get_streams(st_preselections_handler, ST_CATEGORY_BAG_MAIN);
  g_return_val_if_fail(preselections != NULL, NULL);

  stream_bag = st_stream_bag_new(st_preselections_handler);
  st_preselections_make_unique_names(stream_bag, preselections);

  st_stream_store_append(preselections, stream_bag);
  st_stream_store_touch(preselections);
  g_object_unref(preselections);

  /* we don't unref stream_bag, that's caller's responsability */
  return stream_bag;
}
