#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "base_includes.h"
#include "on.h"
#include "split.h"
#include "mycompare.h"
#include "ff.h"

#ifdef ENABLE_ON
/* ------------------------------------------------------------------------ */

#define ON_SILENT   0
#define ON_QUIET    1
#define ON_NORMAL   2
#define ON_NOISY    3

static char ON_LEVELS[4][10] =
  {{"[silent]"}, {"[quiet]"}, {"[normal]"}, {"[noisy]"}};

typedef struct onlist_struct *on_list;
struct onlist_struct
{
  int number;
  char *event;
  int style, serial;
  char *effects;
  on_list next;
};
static on_list xx[o_max];

extern server_ptr gsrv;

/* ------------------------------------------------------------------------ */

static struct command_node list_ons[] =                  
{
  {{"ACTION"},         NULL},
  {{"CHANNEL_SIGNOFF"},NULL},
  {{"CONNECT"},        NULL},
  {{"CTCP"},           NULL},
  {{"CTCP_REPLY"},     NULL},
  {{"DCC_CHAT"},       NULL},
  {{"DCC_RAW"},        NULL},
  {{"DISCONNECT"},     NULL},
  {{"EXEC"},           NULL},
  {{"EXEC_ERRORS"},    NULL},
  {{"EXEC_EXIT"},      NULL},
  {{"EXEC_PROMPT"},    NULL},
  {{"FLOOD"},          NULL},
  {{"HOOK"},           NULL},
  {{"IDLE"},           NULL},
  {{"INPUT"},          NULL},
  {{"INVITE"},         NULL},
  {{"JOIN"},           NULL},
  {{"LEAVE"},          NULL},
  {{"MODE"},           NULL},
  {{"MSG"},            NULL},
  {{"NAMES"},          NULL},
  {{"NICKNAME"},       NULL},
  {{"NOTE"},           NULL},
  {{"NOTICE"},         NULL},
  {{"NOTIFY_SIGNON"},  NULL},
  {{"NOTIFY_SIGNOFF"}, NULL},
  {{"PUBLIC"},         NULL},
  {{"PUBLIC_OTHER"},   NULL},
  {{"RAW_IRC"},        NULL},
  {{"SEND_ACTION"},    NULL},
  {{"SEND_DCC_CHAT"},  NULL},
  {{"SEND_MSG"},       NULL},
  {{"SEND_NOTICE"},    NULL},
  {{"SEND_PUBLIC"},    NULL},
  {{"SERVER_NOTICE"},  NULL},
  {{"SIGNOFF"},        NULL},
  {{"TIMER"},          NULL},
  {{"TOPIC"},          NULL},
  {{"WALL"},           NULL},
  {{"WALLOP"},         NULL},
  {{"WHO"},            NULL},
  {{"WINDOW"},         NULL},
  {{"WINDOW_KILL"},    NULL},
  {{"NUMERIC"},        NULL},
  {{NULL},             NULL}
};

/* ------------------------------------------------------------------------ */
int in_on = FALSE;
/* ------------------------------------------------------------------------ */

int handle_on(int list_id, char *string, int num)
{
  on_list list, temp;
  int max, curr, disp, serial, ret = FALSE, last_serial, old_in_on;

  last_serial = -1;
  list = (on_list)(list_ons[list_id].constant);
  while (list)
  {
/* Always start back at the top of the list in case it has changed since
   we were last here --- in handle_command */

    list = (on_list)(list_ons[list_id].constant);
    max = -1;
    temp = NULL;

/* Skip over all serial numbers that we have already handled */
    while (list && (last_serial >= list->serial)) list = list->next;
    if (!list) return ret;

/* Go through, checking each sequence of serial numbers */
    serial = list->serial;
    while (list && (list->serial == serial)) {
      if (list->number == num) {
        if ((curr = wild_match(list->event, string)) > max) {
          max = curr;
          temp = list;
        }
      }
      list = list->next;
    }
/* If we didn't find a node to execute, there must be no more nodes.. */
    if (!temp) return ret;

    last_serial = temp->serial;
    if (temp->style >= ON_NORMAL) {
      if (num)
        say("*** On %d activated by \"%s\" <%d>", num, string, serial);
      else
        say("*** On %s activated by \"%s\" <%d>", list_ons[list_id].command,
            string, serial);
    }
    disp = getivar(VAR_DISPLAY);
    if (temp->style == ON_SILENT) setivar(VAR_DISPLAY, -1);
    old_in_on = in_on;
    in_on = TRUE;
    parse_line(temp->effects, string, FALSE, FALSE);
    in_on = old_in_on;
    if (getivar(VAR_DISPLAY)==-1)  setivar(VAR_DISPLAY, disp);
    if ((temp->style == ON_SILENT) && (serial==0)) ret = TRUE;
  }
  return ret;
}

/* ------------------------------------------------------------------------ */
static void print_on(int indx, on_list list, int num)
{
  if (num)
    say("*** ON %d from \"%s\" do %s %s <%d>", num,
        list->event, list->effects, ON_LEVELS[list->style], list->serial);
  else
    say("*** ON %s from \"%s\" do %s %s <%d>", list_ons[indx].command,
        list->event, list->effects, ON_LEVELS[list->style], list->serial);
}

/* ------------------------------------------------------------------------ */
static int show_numeric_list(char *str)
{
  on_list list;
  int count = 0;
  char num[10];
  for (list = (on_list)list_ons[o_numeric].constant; list; list = list->next)
  {
    sprintf(num, "%d", list->number);
    if (strncasecmp(str, num, strlen(str)) == 0)
    {
      print_on(o_numeric, list, list->number);
      count++;
    }
  }
  return count;
}

/* ------------------------------------------------------------------------ */
static int show_on_list(int indx, int num)
{
  int count = 0;
  on_list list;
  if (num) indx = o_numeric;
  list = (on_list)(list_ons[indx].constant);
  while (list!=NULL) {
    if (list->number == num) {
      print_on(indx, list, num);
      count++;
    }
    list = list->next;
  }
  return count;
}

/* ------------------------------------------------------------------------ */
static int add_on(int ret, char *str, char *rest, int level, int num,
                  int serial)
{
  int depth;
  char *line, *save, oldc = 0, *end;
  on_list temp, walk, back = NULL;

  if ((temp = (on_list)mymalloc(sizeof(struct onlist_struct))) == NULL)
    return FALSE;
  walk = (on_list)(list_ons[ret].constant);
  while (walk && (serial>walk->serial)) {
    back = walk;
    walk = walk->next;
  }
  while (isspace(*rest)) rest++;
  if (*rest == '{') {
    end = matching_end(++rest, '{', '}');
    oldc = *end;
    *end = 0;
  }
  temp->next = walk;
  if (back) back->next = temp;
  else list_ons[ret].constant = (char *)temp;
  temp->number = num;
  temp->event = m_strcpy(str);
  temp->effects = m_strcpy(rest);
  if (oldc) *end = oldc;
  temp->style = level;
  temp->serial = serial;
  print_on(ret, temp, num);
  return TRUE;
}

/* ------------------------------------------------------------------------ */
static void delete_on_node(on_list node)
{
  myfree(node->event);
  myfree(node->effects);
  myfree(node);
}

/* ------------------------------------------------------------------------ */
static int delete_on(int indx, char *word, int num, int serial)
{
  on_list list, back = NULL;
  list = (on_list)(list_ons[indx].constant);
  while (list) {
    if ((strcasecmp(word, list->event) == 0) && (list->number == num) &&
        (serial==list->serial)) {
      if (!back) list_ons[indx].constant = (char *)list->next;
      else back->next = list->next;
      delete_on_node(list);
      return TRUE;
    }
    back = list;
    list = list->next;
  }
  return FALSE;
}

/* ------------------------------------------------------------------------ */
static void delete_all_ons(int indx)
{
  on_list walk, save = NULL;
  walk = (on_list)list_ons[indx].constant;
  while (walk) {
    save = walk;
    walk = walk->next;
    delete_on_node(save);
  }
  list_ons[indx].constant = 0;
}

/* ------------------------------------------------------------------------ */
static void search_on_list(int list_num, char *string, int level,
                           int num, int serial)
{
  char *word, tmp[MAXLEN];
  int delete = FALSE, all = FALSE;
  int did;

  if (*string==0) {show_on_list(list_num, num); return;}
  if (*string=='-') {
     delete = TRUE;
     string++;
     if (!*string) {
       delete_all_ons(list_num);
       if (list_num==o_numeric)
         say("*** ON %d list is empty", num);
       else
         say("*** ON %s list is empty", list_ons[list_num].command);
       return;
     }
  }

  if (*string=='"') {string++; word = m_grab_word(&string, '"');}
  else word = m_grab_word(&string, ' ');

  did = delete_on(list_num, word, num, serial);
  if (!delete) (void)add_on(list_num, word, string, level, num, serial);
  else if (did) {
    if (list_num == o_numeric) sprintf(tmp, "%d", num);
    else strcpy(tmp, list_ons[list_num].command);
    say("*** ON %s from \"%s\" deleted", tmp, word);
  } else if (delete) say("*** No matching ON for \"%s\" found.", word);
  myfree(word);
}

/* ------------------------------------------------------------------------ */
static void show_all_ons(void)
{
  int loop;
  show_numeric_list("");
  for (loop=0;loop<o_numeric;loop++)
    show_on_list(loop, 0);
}

/* ------------------------------------------------------------------------ */
void on(char *string, int varpar, char *subparams)
{
  char *temp;
  int dummy_ret;
  char word[20], dummy[80];
  int level = ON_NORMAL, indx, list;
  int num = 0, isnum, new_word = FALSE, serial;

  if (*string == '#')      new_word = TRUE;
  if (new_word) string++;

  if (*string == '+')      level = ON_NOISY;
  else if (*string == '-') level = ON_QUIET;
  else if (*string == '^') level = ON_SILENT;
  if (level != ON_NORMAL) string++;
  temp = string;

  (void)grab_word(&temp, ' ', word);
  if (new_word) {
    (void)grab_word(&temp, ' ', dummy);
    serial = atoi(dummy);
  } else serial = 0;
  isnum = word[0] != 0;
  for (indx=0;word[indx];indx++)
   if (isdigit(word[indx])) num = num * 10 + word[indx] - 48;
   else isnum = FALSE;
  if (!isnum) {
    num = 0;
    list = lookup_command(list_ons, word, &dummy_ret);
  }
  else list = o_numeric;

  if (list == c_none)
    say("*** No such ON function: %s", word);
  else if (!*word)
    show_all_ons();
  else if (list == c_amb)
    say("*** Ambiguous ON function: \"%s\"", word);
  else if (!*temp) {
    int count;
    count = show_numeric_list(word);
    if (!isnum) count += show_on_list(list, num);
    if (!count)
      if (isnum) say("*** The %d list is empty", num);
      else say("** The %s list is empty", list_ons[list].command);
  } else
   search_on_list(list, temp, level, num, serial);
}

/* ------------------------------------------------------------------------ */

#else  /* ENABLE_ON */

int handle_on(int list_id, char *string, int num)
{
  return FALSE;
}

#endif /* ENABLE_ON */

