#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "base_includes.h"
#include "notify.h"
#include "qio.h"
#include "server_i.h"
#include "on.h"
#include "sock.h"
#include "system.h"

typedef struct notify_node_data *notify_entry;
struct notify_node_data {
  char *nick;
  int present, sent, delete;
  notify_entry next;
};
struct notify_node {
  int last_sent, last_received;
  notify_entry list;
};

extern server_ptr gsrv;
static notify_ptr master_notify = NULL;

void reset_notify(notify_ptr lst)
{
  notify_entry tmp;
  if (!lst) return;
  for (tmp = lst->list; tmp; tmp = tmp->next)
  {
    tmp->sent = -1;
    tmp->delete = FALSE;
    tmp->present = FALSE;
  }
  lst->last_sent = lst->last_received = -1;
}

static notify_ptr make_notify_header(void)
{
  notify_ptr tmp;
  tmp = (notify_ptr)malloc(sizeof(struct notify_node));
  if (!tmp) return NULL;
  tmp->last_sent = tmp->last_received = -1;
  tmp->list = NULL;
  return tmp;
}

static notify_entry make_notify_node(char *str)
{
  notify_entry temp = (notify_entry)malloc(sizeof(struct notify_node_data));
  if (!temp) return NULL;
  if (!(temp->nick = m_strcpy(str))) {
    free(temp);
    return NULL;
  }
  temp->delete = FALSE;
  temp->present = FALSE;
  temp->sent = -1;
  temp->next = NULL;
  return temp;
}

notify_ptr copy_notify_list(notify_ptr list)
{
  notify_ptr top = NULL;
  notify_entry temp, walk, curr = NULL;

  if (!list) list = master_notify;
  top = make_notify_header();
  walk = list->list;
  while (walk) {
    if (!(temp = make_notify_node(walk->nick))) return top;
    if (curr) curr->next = temp;
    else top->list = temp;
    curr = temp;
    walk = walk->next;
  }
  return top;
}

static notify_entry insert_notify(notify_entry lst, notify_entry new)
{
  notify_entry f, b = NULL;
  int res = -1;
  if (!new || !new->nick) {
    yell("*** Can't insert an empty element into the notify list!");
    return;
  }
  f = lst;
  while (f && ((res = strcasecmp(new->nick, f->nick)) > 0)) {
    b = f;
    f = f->next;
  }
  if (res == 0) {
    f->delete = FALSE;
    return lst;
  }
  new->next = f;
  if (!b) return new;
  else b->next = new;
  return lst;
}

static void add_notify(server_ptr serv, char *str)
{
  notify_entry temp;

  if (!(temp = make_notify_node(str))) return;
  if (serv == NULL) {
    if (!master_notify) master_notify = make_notify_header();
    if (!master_notify) return;
    master_notify->list = insert_notify(master_notify->list, temp);
  } else {
    if (!serv->notify_list) serv->notify_list = make_notify_header();
    if (!serv->notify_list) return;
    serv->notify_list->list = insert_notify(serv->notify_list->list, temp);
  }
  say("*** %s has been added to the notification list.", str);
}

static void delete_notify(server_ptr serv, char *str)
{
  notify_entry front, back;

  if (!serv) return;
  front = serv->notify_list->list;
/*
  back = NULL;
  while ((front) && (strcasecmp(front->nick, str) != 0)) {
    back = front;
    front=front->next;
  }
  if (!front) return;
  if (!back)  serv->notify_list->list = front->next;
  else        back->next = front->next;
  myfree(front);
*/
  while ((front) && (strcasecmp(front->nick, str) != 0)) front = front->next;
  if (front) {
    say("*** %s has been removed from the notification list.", str);
    front->delete = TRUE;
  } else
    say("*** %s was not in the notification list.", str);
}

static void show_notify(server_ptr serv, int only_pres)
{
  notify_entry walk;
  char present[MAXLEN*5], absent[MAXLEN*5], *str;

  if (!serv) return;
  if (!serv->notify_list) return;
  present[0] = absent[0] = 0;

  walk = serv->notify_list->list;

  while (walk) {
    if (!walk->delete)
    {
      if (walk->present) str = present;
      else str = absent;
      (void)strcat(str, walk->nick);
      (void)strcat(str, " ");
    }
    walk = walk->next;
  }
  say("*** Currently present: %s", present);
  if (!only_pres) say("*** Currently absent: %s", absent);
}

void notify(server_ptr serv, char *str)
{
  char word[MAXLEN];

  if (!*str)
    show_notify(serv, FALSE);
  else if (strcmp(str, "+") == 0)
    show_notify(serv, TRUE);
  else
    while(*str) {
      (void)grab_word(&str, ' ', word);
      if (*word=='-') delete_notify(serv, &word[1]);
      else add_notify(serv, word);
    }
}

#define PRESTRING "ISON :"

void send_notify(server_ptr serv)
{
  notify_entry walk;
  char msg[512], errmsg[80];
  int len;

  (void)strcpy(msg, PRESTRING);
  len = strlen(msg);
  if (!serv) return;
  if (!serv->notify_list) return;
  walk = serv->notify_list->list;
  if (serv->notify_list->last_sent == -1)
    serv->notify_list->last_sent = 0;
  while (walk) {
    if (walk->delete)
    {
    }
    else if (walk->sent == -1)
    {
      if (len>(sizeof(msg)-33)) { /* maxnicklen of 30 HACK */
        serv->notify_list->last_sent++;
        new_send(serv, "%s\n", msg);
        walk->sent = serv->notify_list->last_sent;
        (void)sprintf(msg, "%s%s ", PRESTRING, walk->nick);
        len = strlen(msg);
      } else {
        walk->sent = serv->notify_list->last_sent;
        (void)strcat(msg, walk->nick);
        if (walk->next) (void)strcat(msg, " ");
        len = len + strlen(walk->nick) + 1;
      }
    }
    walk = walk->next;
  }
  if (len!=strlen(PRESTRING)) {
    serv->notify_list->last_sent++;
    new_send(serv, "%s\n", msg);
  }
}

void han_notify(server_ptr serv, char *string)
{
  int len, this_set;
  char *temp, *test, nick[MAXLEN];
  notify_ptr old;
  notify_entry walk;
  if (!serv) return;
  if (!serv->notify_list) return;
  temp = string;
  len = grab_word(&temp, ' ', nick);

  this_set = ++serv->notify_list->last_received;
  for (walk = serv->notify_list->list; walk && len; walk = walk->next)
  {
    if (getivar(VAR_DEBUG) & DEBUG$NOTIFY)
      yell("Checking %s[%d] (loop) against %s[%d] (line)", walk->nick,
           walk->sent, nick, this_set);
    if ((walk->sent <= this_set) && (walk->sent != -1))
    {
      walk->sent = -1;
      if (strcasecmp(walk->nick, nick) != 0)
      {
        if (walk->present == TRUE)
          if (!handle_on(o_notify_signoff, walk->nick, 0))
            say("*** Signoff by %s detected", walk->nick);
        walk->present = FALSE;
      }
      else
      {
        if (walk->present == FALSE)
          if (!handle_on(o_notify_signon, walk->nick, 0))
            say("*** Signon by %s detected", walk->nick);
        walk->present = TRUE;
        len = grab_word(&temp, ' ', nick);
      }
    }
  }
  for (walk = serv->notify_list->list; walk; walk = walk->next)
  {
    if (getivar(VAR_DEBUG) & DEBUG$NOTIFY)
      yell("Checking %s[%d] (loop) [%d]", walk->nick, walk->sent, this_set);
    if ((walk->sent <= this_set) && (walk->sent != -1))
    {
      walk->sent = -1;
      if (walk->present == TRUE)
        if (!handle_on(o_notify_signoff, walk->nick, 0))
          say("*** Signoff by %s detected", walk->nick);
      walk->present = FALSE;
    }
  }
}

void save_notify(FILE *fp)
{
  int count = 0, old;
  notify_entry temp;

  if (!master_notify) return;
  noast(&old);
  temp = master_notify->list;
  while (temp)
  {
    if (!count)
      fprintf(fp, "NOTIFY");
    fprintf(fp, " %s", temp->nick);
    if (++count == 10) {
      count = 0;
      fprintf(fp, "\n");
    }
    temp = temp->next;
  }
  fprintf(fp, "\n");
  oldast(&old);
}
