/*
 *  tvheadend, AJAX / HTML user interface
 *  Copyright (C) 2008 Andreas Ă–man
 *
 *  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 3 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, see <http://www.gnu.org/licenses/>.
 */

#include <pthread.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include "tvhead.h"
#include "http.h"
#include "ajaxui.h"
#include "channels.h"



/**
 * Render a channel group widget
 */
static void
ajax_chgroup_build(htsbuf_queue_t *tq, channel_group_t *tcg)
{
  htsbuf_qprintf(tq, "<li id=\"chgrp_%d\">", tcg->tcg_tag);
  
  ajax_box_begin(tq, AJAX_BOX_BORDER, NULL, NULL, NULL);
  
  htsbuf_qprintf(tq, "<div style=\"overflow: auto; width: 100%\">");
  
  htsbuf_qprintf(tq,
	      "<div style=\"float: left; width: 60%\">"
	      "<a href=\"javascript:void(0)\" "
	      "onClick=\"$('cheditortab').innerHTML=''; "
	      "new Ajax.Updater('groupeditortab', "
	      "'/ajax/chgroup_editor/%d', "
	      "{method: 'get', evalScripts: true})\" >"
	      "%s</a></div>",
	      tcg->tcg_tag, tcg->tcg_name);


  if(tcg != defgroup) {
    htsbuf_qprintf(tq,
		"<div style=\"float: left; width: 40%\" "
		"class=\"chgroupaction\">"
		"<a href=\"javascript:void(0)\" "
		"onClick=\"dellistentry('/ajax/chgroup_del','%d', '%s');\""
		">Delete</a></div>",
		tcg->tcg_tag, tcg->tcg_name);
  }
  

  htsbuf_qprintf(tq, "</div>");
  ajax_box_end(tq, AJAX_BOX_BORDER);
  htsbuf_qprintf(tq, "</li>");
}

/**
 * Update order of channel groups
 */
static int
ajax_chgroup_updateorder(http_connection_t *hc, http_reply_t *hr, 
			 const char *remain, void *opaque)
{
  channel_group_t *tcg;
  htsbuf_queue_t *tq = &hr->hr_q;
  http_arg_t *ra;

  TAILQ_FOREACH(ra, &hc->hc_req_args, link) {
    if(strcmp(ra->key, "channelgrouplist[]") ||
       (tcg = channel_group_by_tag(atoi(ra->val))) == NULL)
      continue;
    
    TAILQ_REMOVE(&all_channel_groups, tcg, tcg_global_link);
    TAILQ_INSERT_TAIL(&all_channel_groups, tcg, tcg_global_link);
  }

  channel_group_settings_write();

  htsbuf_qprintf(tq, "<span id=\"updatedok\">Updated on server</span>");
  ajax_js(tq, "Effect.Fade('updatedok')");
  http_output_html(hc, hr);
  return 0;
}



/**
 * Add a new channel group
 */
static int
ajax_chgroup_add(http_connection_t *hc, http_reply_t *hr, 
		 const char *remain, void *opaque)
{
  channel_group_t *tcg;
  htsbuf_queue_t *tq = &hr->hr_q;
  const char *name;
  
  if((name = http_arg_get(&hc->hc_req_args, "name")) != NULL) {

    TAILQ_FOREACH(tcg, &all_channel_groups, tcg_global_link)
      if(!strcmp(name, tcg->tcg_name))
	break;

    if(tcg == NULL) {
      tcg = channel_group_find(name, 1);

      ajax_chgroup_build(tq, tcg);

      /* We must recreate the Sortable object */

      ajax_js(tq, "Sortable.destroy(\"channelgrouplist\")");

      ajax_js(tq, "Sortable.create(\"channelgrouplist\", "
	      "{onUpdate:function(){updatelistonserver("
	      "'channelgrouplist', "
	      "'/ajax/chgroup_updateorder', "
	      "'list-info'"
	      ")}});");
    }
  }
  http_output_html(hc, hr);
  return 0;
}



/**
 * Delete a channel group
 */
static int
ajax_chgroup_del(http_connection_t *hc, http_reply_t *hr, 
		 const char *remain, void *opaque)
{
  channel_group_t *tcg;
  htsbuf_queue_t *tq = &hr->hr_q;
  const char *id;
  
  if((id = http_arg_get(&hc->hc_req_args, "id")) == NULL)
    return HTTP_STATUS_BAD_REQUEST;

  if((tcg = channel_group_by_tag(atoi(id))) == NULL)
    return HTTP_STATUS_BAD_REQUEST;

  htsbuf_qprintf(tq, "$('chgrp_%d').remove();", tcg->tcg_tag);
  http_output(hc, hr, "text/javascript; charset=UTF-8", NULL, 0);

  channel_group_destroy(tcg);
  return 0;
}



/**
 * Channel group & channel configuration
 */
int
ajax_config_channels_tab(http_connection_t *hc, http_reply_t *hr)
{
  htsbuf_queue_t *tq = &hr->hr_q;
  channel_group_t *tcg;

  htsbuf_qprintf(tq, "<div style=\"float: left; width: 30%\">");

  ajax_box_begin(tq, AJAX_BOX_SIDEBOX, "channelgroups",
		 NULL, "Channel groups");

  htsbuf_qprintf(tq, "<div style=\"height:15px; text-align:center\" "
	      "id=\"list-info\"></div>");
   
  htsbuf_qprintf(tq, "<ul id=\"channelgrouplist\" class=\"draglist\">");

  TAILQ_FOREACH(tcg, &all_channel_groups, tcg_global_link) {
    if(tcg->tcg_hidden)
      continue;
    ajax_chgroup_build(tq, tcg);
  }

  htsbuf_qprintf(tq, "</ul>");
 
  ajax_js(tq, "Sortable.create(\"channelgrouplist\", "
	  "{onUpdate:function(){updatelistonserver("
	  "'channelgrouplist', "
	  "'/ajax/chgroup_updateorder', "
	  "'list-info'"
	  ")}});");

  /**
   * Add new group
   */

  htsbuf_qprintf(tq, "<hr>");

  ajax_box_begin(tq, AJAX_BOX_BORDER, NULL, NULL, NULL);

  htsbuf_qprintf(tq,
	      "<div style=\"height: 25px\">"
	      "<div style=\"float: left\">"
	      "<input type=\"text\" id=\"newchgrp\">"
	      "</div>"
	      "<div style=\"float: right\">"
	      "<input type=\"button\" value=\"Add\" "
	      "onClick=\"javascript:addlistentry_by_widget("
	      "'channelgrouplist', 'chgroup_add', 'newchgrp');\">"
	      "</div></div>");
    
  ajax_box_end(tq, AJAX_BOX_BORDER);
    
  ajax_box_end(tq, AJAX_BOX_SIDEBOX);
  htsbuf_qprintf(tq, "</div>");

  htsbuf_qprintf(tq, 
	      "<div id=\"groupeditortab\" "
	      "style=\"overflow: auto; float: left; width: 30%\"></div>");

  htsbuf_qprintf(tq, 
	      "<div id=\"cheditortab\" "
	      "style=\"overflow: auto; float: left; width: 40%\"></div>");

  http_output_html(hc, hr);
  return 0;
}

/**
 * Display all channels within the group
 */
static int
ajax_chgroup_editor(http_connection_t *hc, http_reply_t *hr,
		    const char *remain, void *opaque)
{
  htsbuf_queue_t *tq = &hr->hr_q;
  channel_t *ch;
  channel_group_t *tcg, *tcg2;
  th_transport_t *t;
  char buf[10];
  int nsources;
  ajax_table_t ta;

  if(remain == NULL || (tcg = channel_group_by_tag(atoi(remain))) == NULL)
    return HTTP_STATUS_BAD_REQUEST;

  htsbuf_qprintf(tq, "<script type=\"text/javascript\">\r\n"
	      "//<![CDATA[\r\n");
  
  /* Select all */
  htsbuf_qprintf(tq, "select_all = function() {\r\n");
  TAILQ_FOREACH(ch, &tcg->tcg_channels, ch_group_link) {
    htsbuf_qprintf(tq, 
		"$('sel_%d').checked = true;\r\n",
		ch->ch_tag);
  }
  htsbuf_qprintf(tq, "}\r\n");

  /* Select none */
  htsbuf_qprintf(tq, "select_none = function() {\r\n");
  TAILQ_FOREACH(ch, &tcg->tcg_channels, ch_group_link) {
    htsbuf_qprintf(tq, 
		"$('sel_%d').checked = false;\r\n",
		ch->ch_tag);
  }
  htsbuf_qprintf(tq, "}\r\n");

  /* Invert selection */
  htsbuf_qprintf(tq, "select_invert = function() {\r\n");
  TAILQ_FOREACH(ch, &tcg->tcg_channels, ch_group_link) {
    htsbuf_qprintf(tq, 
		"$('sel_%d').checked = !$('sel_%d').checked;\r\n",
		ch->ch_tag, ch->ch_tag);
  }
  htsbuf_qprintf(tq, "}\r\n");

  /* Invert selection */
  htsbuf_qprintf(tq, "select_sources = function() {\r\n");
  TAILQ_FOREACH(ch, &tcg->tcg_channels, ch_group_link) {
    htsbuf_qprintf(tq, 
		"$('sel_%d').checked = %s;\r\n",
		ch->ch_tag, LIST_FIRST(&ch->ch_transports) ? "true" : "false");
  }
  htsbuf_qprintf(tq, "}\r\n");



  /* Invoke AJAX call containing all selected elements */
  htsbuf_qprintf(tq, 
	      "select_do = function(op, arg1, arg2, check) {\r\n"
	      "if(check == true && !confirm(\"Are you sure?\")) {return;}\r\n"
	      "var h = new Hash();\r\n"
	      "h.set('arg1', arg1);\r\n"
	      "h.set('arg2', arg2);\r\n"
	      );
  
  TAILQ_FOREACH(ch, &tcg->tcg_channels, ch_group_link) {
    htsbuf_qprintf(tq, 
		"if($('sel_%d').checked) {h.set('%d', 'selected') }\r\n",
		ch->ch_tag, ch->ch_tag);
  }

  htsbuf_qprintf(tq, " new Ajax.Request('/ajax/chop/' + op, "
	      "{parameters: h});\r\n");
  htsbuf_qprintf(tq, "}\r\n");


  htsbuf_qprintf(tq, 
	      "\r\n//]]>\r\n"
	      "</script>\r\n");


  ajax_box_begin(tq, AJAX_BOX_SIDEBOX, NULL, NULL, tcg->tcg_name);

  ajax_table_top(&ta, hc, tq, (const char *[])
		 {"Channelname", "Sources", "", NULL},
		 (int[]){8,2,1});

  TAILQ_FOREACH(ch, &tcg->tcg_channels, ch_group_link) {
    snprintf(buf, sizeof(buf), "%d", ch->ch_tag);
    ajax_table_row_start(&ta, buf);

    nsources = 0;

    LIST_FOREACH(t, &ch->ch_transports, tht_ch_link)
      nsources++;

    ajax_table_cell(&ta, NULL,
		    "<a href=\"javascript:void(0)\" "
		    "onclick=\"new Ajax.Updater('cheditortab', "
		    "'/ajax/cheditor/%d', {method: 'get'})\""
		    ">%s</a>", ch->ch_tag, ch->ch_name);

    ajax_table_cell(&ta, NULL, "%d", nsources);
    ajax_table_cell_checkbox(&ta);
  }
  ajax_table_bottom(&ta);

  htsbuf_qprintf(tq, "<hr>\r\n");
  htsbuf_qprintf(tq, "<div style=\"text-align: center; "
	      "overflow: auto; width: 100%\">");

  ajax_button(tq, "Select all", "select_all()");
  ajax_button(tq, "Select none", "select_none()");
  ajax_button(tq, "Invert selection", "select_invert()");
  ajax_button(tq, "Select channels with sources", "select_sources()");
  htsbuf_qprintf(tq, "</div>\r\n");

  htsbuf_qprintf(tq, "<hr>\r\n");

  htsbuf_qprintf(tq, "<div style=\"text-align: center; "
	      "overflow: auto; width: 100%\">");
  
  ajax_button(tq,
	      "Delete all selected...", 
	      "select_do('delete', '%d', 0, true);", tcg->tcg_tag);
  
  htsbuf_qprintf(tq,
	      "<select id=\"movetarget\" "
	      "onChange=\"select_do('changegroup', "
	      "$('movetarget').value, '%d', false)\">", tcg->tcg_tag);
  htsbuf_qprintf(tq, 
	      "<option value="">Move selected channels to group:</option>");

  TAILQ_FOREACH(tcg2, &all_channel_groups, tcg_global_link) {
    if(tcg2->tcg_hidden || tcg == tcg2)
      continue;
    htsbuf_qprintf(tq, "<option value=\"%d\">%s</option>",
		tcg2->tcg_tag, tcg2->tcg_name);
  }
  htsbuf_qprintf(tq, "</select></div>");
  htsbuf_qprintf(tq, "</div>");
  htsbuf_qprintf(tq, "</div>");
  ajax_box_end(tq, AJAX_BOX_SIDEBOX);


  http_output_html(hc, hr);
  return 0;
}


/**
 *
 */
static struct strtab sourcetypetab[] = {
  { "DVB",        TRANSPORT_DVB },
  { "V4L",        TRANSPORT_V4L },
  { "IPTV",       TRANSPORT_IPTV },
  { "AVgen",      TRANSPORT_AVGEN },
  { "File",       TRANSPORT_STREAMEDFILE },
};


static struct strtab cdlongname[] = {
  { "None",                  COMMERCIAL_DETECT_NONE },
  { "Swedish TV4 Teletext",  COMMERCIAL_DETECT_TTP192 },
};

/**
 * Display all channels within the group
 */
static int
ajax_cheditor(http_connection_t *hc, http_reply_t *hr,
	      const char *remain, void *opaque)
{
  htsbuf_queue_t *tq = &hr->hr_q;
  channel_t *ch, *ch2;
  channel_group_t *chg;
  th_transport_t *t;
  const char *s;
  int i;

  if(remain == NULL || (ch = channel_by_tag(atoi(remain))) == NULL)
    return HTTP_STATUS_BAD_REQUEST;

  ajax_box_begin(tq, AJAX_BOX_SIDEBOX, NULL, NULL, ch->ch_name);
  
  if(ch->ch_icon != NULL) {
    htsbuf_qprintf(tq, 
		"<div style=\"width: 100%; text-align:center\">"
		"<img src=\"%s\"></div>", ch->ch_icon);
  }
  
  htsbuf_qprintf(tq, "<div>Sources:</div>");

  LIST_FOREACH(t, &ch->ch_transports, tht_ch_link) {
    ajax_box_begin(tq, AJAX_BOX_BORDER, NULL, NULL, NULL);
    htsbuf_qprintf(tq, "<div style=\"overflow: auto; width: 100%\">");
    htsbuf_qprintf(tq, "<div style=\"float: left; width: 13%%\">%s</div>",
		val2str(t->tht_type, sourcetypetab) ?: "???");
    htsbuf_qprintf(tq, "<div style=\"float: left; width: 87%%\">\"%s\"%s</div>",
		t->tht_svcname, t->tht_scrambled ? " - (scrambled)" : "");
    s = t->tht_sourcename ? t->tht_sourcename(t) : NULL;

    htsbuf_qprintf(tq, "</div><div style=\"overflow: auto; width: 100%\">");

    htsbuf_qprintf(tq,
		"<div style=\"float: left; width: 13%%\">"
		"<input %stype=\"checkbox\" class=\"nicebox\" "
		"onClick=\"new Ajax.Request('/ajax/transport_chdisable/%s', "
		"{parameters: {enabled: this.checked}});\">"
		"</div>", t->tht_disabled ? "" : "checked ",
		t->tht_identifier);

    if(s != NULL)
      htsbuf_qprintf(tq, "<div style=\"float: left; width: 87%%\">%s</div>",
		  s);

    htsbuf_qprintf(tq, "</div>");

    ajax_box_end(tq, AJAX_BOX_BORDER);
  }

  htsbuf_qprintf(tq, "<hr>\r\n");

  htsbuf_qprintf(tq, "<div style=\"overflow: auto; width:100%%\">");

  htsbuf_qprintf(tq, 
	      "<input type=\"button\" value=\"Rename...\" "
	      "onClick=\"channel_rename('%d', '%s')\">",
	      ch->ch_tag, ch->ch_name);

  htsbuf_qprintf(tq, 
	      "<input type=\"button\" value=\"Delete...\" "
	      "onClick=\"channel_delete('%d', '%s')\">",
	      ch->ch_tag, ch->ch_name);

  htsbuf_qprintf(tq,
	      "<select "
	      "onChange=\"channel_merge('%d', this.value);\">",
	      ch->ch_tag);
  
  htsbuf_qprintf(tq, "<option value=\"n\">Merge to channel:</option>");

  
  TAILQ_FOREACH(chg, &all_channel_groups, tcg_global_link) {
    TAILQ_FOREACH(ch2, &chg->tcg_channels, ch_group_link) {
      if(ch2 != ch)
	htsbuf_qprintf(tq, "<option value=\"%d\">%s</option>",
		    ch2->ch_tag, ch2->ch_name);
    }
  }
  
  htsbuf_qprintf(tq, "</select>");
  htsbuf_qprintf(tq, "</div>");
  htsbuf_qprintf(tq, "<hr>\r\n");

  htsbuf_qprintf(tq,
	      "<div class=\"infoprefixwidewidefat\">"
	      "Commercial detection:</div>"
	      "<div>"
	      "<select "
	      "onChange=\"new Ajax.Request('/ajax/chsetcomdetect/%d', "
	      "{parameters: {how: this.value}});\">",
	      ch->ch_tag);

  for(i = 0; i < sizeof(cdlongname) / sizeof(cdlongname[0]); i++) {
    htsbuf_qprintf(tq, "<option %svalue=%d>%s</option>",
		cdlongname[i].val == ch->ch_commercial_detection ? 
		"selected " : "",
		cdlongname[i].val, cdlongname[i].str);
  }
  htsbuf_qprintf(tq, "</select></div>");
  htsbuf_qprintf(tq, "</div>");


  ajax_box_end(tq, AJAX_BOX_SIDEBOX);
  http_output_html(hc, hr);
  return 0;
}

/**
 * Change group for channel(s)
 */
static int
ajax_changegroup(http_connection_t *hc, http_reply_t *hr,
		 const char *remain, void *opaque)
{
  htsbuf_queue_t *tq = &hr->hr_q;
  channel_t *ch;
  channel_group_t *tcg;
  http_arg_t *ra;
  const char *s;
  const char *curgrp;

  if((s = http_arg_get(&hc->hc_req_args, "arg1")) == NULL)
    return HTTP_STATUS_BAD_REQUEST;

  if((curgrp = http_arg_get(&hc->hc_req_args, "arg2")) == NULL)
    return HTTP_STATUS_BAD_REQUEST;

  tcg = channel_group_by_tag(atoi(s));
  if(tcg == NULL)
    return HTTP_STATUS_BAD_REQUEST;

  TAILQ_FOREACH(ra, &hc->hc_req_args, link) {
    if(strcmp(ra->val, "selected"))
      continue;
    
    if((ch = channel_by_tag(atoi(ra->key))) != NULL)
      channel_set_group(ch, tcg);
  }

  htsbuf_qprintf(tq,
	      "$('cheditortab').innerHTML=''; "
	      "new Ajax.Updater('groupeditortab', "
	      "'/ajax/chgroup_editor/%s', "
	      "{method: 'get', evalScripts: true});", curgrp);
  
  http_output(hc, hr, "text/javascript; charset=UTF-8", NULL, 0);
  return 0;
}

/**
 * Change commercial detection type for channel(s)
 */
static int
ajax_chsetcomdetect(http_connection_t *hc, http_reply_t *hr,
		    const char *remain, void *opaque)
{
  channel_t *ch;
  const char *s;

  if(remain == NULL || (ch = channel_by_tag(atoi(remain))) == NULL)
    return HTTP_STATUS_BAD_REQUEST;
  
  if((s = http_arg_get(&hc->hc_req_args, "how")) == NULL)
    return HTTP_STATUS_BAD_REQUEST;
  
  ch->ch_commercial_detection = atoi(s);

  channel_settings_write(ch);
  http_output(hc, hr, "text/javascript; charset=UTF-8", NULL, 0);
  return 0;
}


/**
 * Rename a channel
 */
static int
ajax_chrename(http_connection_t *hc, http_reply_t *hr,
	      const char *remain, void *opaque)
{
  htsbuf_queue_t *tq = &hr->hr_q;
  channel_t *ch;
  const char *s;

  if(remain == NULL || (ch = channel_by_tag(atoi(remain))) == NULL)
    return HTTP_STATUS_BAD_REQUEST;
  
  if((s = http_arg_get(&hc->hc_req_args, "newname")) == NULL)
    return HTTP_STATUS_BAD_REQUEST;

  if(channel_rename(ch, s)) {
    htsbuf_qprintf(tq, "alert('Channel already exist');");
  } else {
    htsbuf_qprintf(tq, 
		"new Ajax.Updater('groupeditortab', "
		"'/ajax/chgroup_editor/%d', "
		"{method: 'get', evalScripts: true});\r\n",
		ch->ch_group->tcg_tag);
 
    htsbuf_qprintf(tq, 
		"new Ajax.Updater('cheditortab', "
		"'/ajax/cheditor/%d', "
		"{method: 'get', evalScripts: true});\r\n",
		ch->ch_tag);
  }

  http_output(hc, hr, "text/javascript; charset=UTF-8", NULL, 0);
  return 0;
}


/**
 * Delete channel
 */
static int
ajax_chdelete(http_connection_t *hc, http_reply_t *hr,
	      const char *remain, void *opaque)
{
  htsbuf_queue_t *tq = &hr->hr_q;
  channel_t *ch;
  channel_group_t *tcg;

  if(remain == NULL || (ch = channel_by_tag(atoi(remain))) == NULL)
    return HTTP_STATUS_BAD_REQUEST;
  
  tcg = ch->ch_group;
  
  channel_delete(ch);

  htsbuf_qprintf(tq, 
	      "new Ajax.Updater('groupeditortab', "
	      "'/ajax/chgroup_editor/%d', "
	      "{method: 'get', evalScripts: true});\r\n",
	      tcg->tcg_tag);
 
  htsbuf_qprintf(tq, "$('cheditortab').innerHTML='';\r\n");

  http_output(hc, hr, "text/javascript; charset=UTF-8", NULL, 0);
  return 0;
}

/**
 * Merge channel
 */
static int
ajax_chmerge(http_connection_t *hc, http_reply_t *hr,
	     const char *remain, void *opaque)
{
  htsbuf_queue_t *tq = &hr->hr_q;
  channel_t *src, *dst;
  channel_group_t *tcg;
  const char *s;

  if(remain == NULL || (src = channel_by_tag(atoi(remain))) == NULL)
    return HTTP_STATUS_NOT_FOUND;
  
  if((s = http_arg_get(&hc->hc_req_args, "dst")) == NULL)
    return HTTP_STATUS_BAD_REQUEST;
  
  if((dst = channel_by_tag(atoi(s))) == NULL)
    return HTTP_STATUS_BAD_REQUEST;

  tcg = src->ch_group;
  channel_merge(dst, src);

  htsbuf_qprintf(tq, 
	      "new Ajax.Updater('groupeditortab', "
	      "'/ajax/chgroup_editor/%d', "
	      "{method: 'get', evalScripts: true});\r\n",
	      tcg->tcg_tag);
 
  htsbuf_qprintf(tq, "$('cheditortab').innerHTML='';\r\n");

  http_output(hc, hr, "text/javascript; charset=UTF-8", NULL, 0);
  return 0;
}

/**
 * Change group for channel(s)
 */
static int
ajax_chdeletemulti(http_connection_t *hc, http_reply_t *hr,
		   const char *remain, void *opaque)
{
  htsbuf_queue_t *tq = &hr->hr_q;
  channel_t *ch;
  http_arg_t *ra;
  const char *curgrp;

  if((curgrp = http_arg_get(&hc->hc_req_args, "arg1")) == NULL)
    return HTTP_STATUS_BAD_REQUEST;

  TAILQ_FOREACH(ra, &hc->hc_req_args, link) {
    if(strcmp(ra->val, "selected"))
      continue;
    
    if((ch = channel_by_tag(atoi(ra->key))) != NULL)
      channel_delete(ch);
  }

  htsbuf_qprintf(tq,
	      "$('cheditortab').innerHTML=''; "
	      "new Ajax.Updater('groupeditortab', "
	      "'/ajax/chgroup_editor/%s', "
	      "{method: 'get', evalScripts: true});", curgrp);
  
  http_output(hc, hr, "text/javascript; charset=UTF-8", NULL, 0);
  return 0;
}



/**
 *
 */
void
ajax_config_channels_init(void)
{
  http_path_add("/ajax/chgroup_add"        , NULL, ajax_chgroup_add,
		AJAX_ACCESS_CONFIG);
  http_path_add("/ajax/chgroup_del"        , NULL, ajax_chgroup_del,
		AJAX_ACCESS_CONFIG);
  http_path_add("/ajax/chgroup_updateorder", NULL, ajax_chgroup_updateorder,
		AJAX_ACCESS_CONFIG);
  http_path_add("/ajax/chgroup_editor",      NULL, ajax_chgroup_editor,
		AJAX_ACCESS_CONFIG);
  http_path_add("/ajax/cheditor",            NULL, ajax_cheditor,
		AJAX_ACCESS_CONFIG);
  http_path_add("/ajax/chop/changegroup",    NULL, ajax_changegroup,
		AJAX_ACCESS_CONFIG);
  http_path_add("/ajax/chsetcomdetect",      NULL, ajax_chsetcomdetect,
		AJAX_ACCESS_CONFIG);
  http_path_add("/ajax/chrename",            NULL, ajax_chrename,
		AJAX_ACCESS_CONFIG);
  http_path_add("/ajax/chdelete",            NULL, ajax_chdelete,
		AJAX_ACCESS_CONFIG);
  http_path_add("/ajax/chmerge",             NULL, ajax_chmerge,
		AJAX_ACCESS_CONFIG);
 http_path_add("/ajax/chop/delete",          NULL, ajax_chdeletemulti,
		AJAX_ACCESS_CONFIG);

}