#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "global.h"
#include "constants.h"
#include "window.h"
#include "chan.h"

/* ---------------------------------------------------------------------- */
void recreate_chan_mode(chan_ptr list, int chan, char *mode)
{
  chan_ptr walk;
  char lame[MAXLEN];
  for (walk = list; walk && (walk->index!=chan); walk = walk->next);
  if (!walk) return;
  *mode = 0;
  if (walk->mode & C_INVITE) *mode++ = 'i';
  if (walk->mode & C_MODERATED) *mode++ = 'm';
  if (walk->mode & C_NOOUTSIDE) *mode++ = 'n';
  if (walk->mode & C_PRIVATE) *mode++ = 'p';
  if (walk->mode & C_SECRET) *mode++ = 's';
  if (walk->mode & C_TOPIC) *mode++ = 't';
  if (walk->mode & C_KEY) *mode++ = 'k';
  if (walk->mode & C_LIMIT) *mode++ = 'l';
  *mode = 0;
  if (walk->mode & C_KEY) {
    sprintf(lame, " %s", walk->key);
    strcat(mode, lame);
  }
  if (walk->mode & C_LIMIT) {
    sprintf(lame, " %d", walk->limit);
    strcat(mode, lame);
  }
}

/* ---------------------------------------------------------------------- */
void raw_chan_mode(chan_ptr ww, int it, int add, char **rest)
{
  int change = 0;
  char word[MAXLEN];
  if (it == 'i') change = C_INVITE;
  else if (it == 'k') {
    change = C_KEY;
    grab_word(rest, ' ', word);
    if (ww->key) free(ww->key);
    ww->key = ms_strcpy(word);
  } else if (it == 'l') {
    change = C_LIMIT;
    grab_word(rest, ' ', word);
    ww->limit = atoi(word);
  } else if (it == 'm') change = C_MODERATED;
  else if (it == 'n') change = C_NOOUTSIDE;
  else if (it == 'p') change = C_PRIVATE;
  else if (it == 's') change = C_SECRET;
  else if (it == 't') change = C_TOPIC;
  else if (it == 'o') {
    user_ptr walk;
    grab_word(rest, ' ', word);
    for (walk=ww->user_list; walk && strcasecmp(walk->nick, word)!=0;
         walk = walk->next);
    if (walk) walk->chanop = add;
  }
  if (change) 
    if (add) ww->mode |= change;
    else ww->mode ^= change;
}

/* ---------------------------------------------------------------------- */
void set_chan_mode(chan_ptr list, char *chan, char *mode)
{
  chan_ptr walk;
  char word[MAXLEN];
  int add = TRUE, ind = 0;
  for (walk = list; walk && strcasecmp(walk->str, chan); walk = walk->next);
  if (!walk) return;
  grab_word(&mode, ' ', word);
  while (word[ind]) {
    if ((word[ind] == '+') || (word[ind] == '-')) add = word[ind++] == '+';
    raw_chan_mode(walk, word[ind], add, &mode);
    ind++;
  }
}

/* ---------------------------------------------------------------------- */
void raw_add_user(user_ptr *list, char *nick, char chop)
{
  user_ptr f, b, new;
  int n;
  f = *list;
  b = NULL;
  while (f && ((n = strcasecmp(nick, f->nick))<0) )
  {
    b = f;
    f = f->next;
  }
  if ((new = (user_ptr)malloc(sizeof(struct user_struct))) == NULL) return;
  strcpy(new->nick, nick);
  new->chanop = chop;
  new->next = f;
  if (!b) *list = new;
  else b->next = new;
}

/* ---------------------------------------------------------------------- */
int raw_remove_user(user_ptr *list, char *nick, char *chop)
{
  user_ptr f, b;
  int n;
  f = *list;
  b = NULL;
  while (f && ((n = strcasecmp(nick, f->nick))<0) )
  {
    b = f;
    f = f->next;
  }
  if (n==0)
  {
    if (b) b->next = f->next;
    else *list = f->next;
    *chop = f->chanop;
    free(f);
    return TRUE;
  }
  else *chop = FALSE;
  return FALSE;
}

/* ---------------------------------------------------------------------- */
int user_in_chan_list(user_ptr list, char *nick)
{
  int n = -1;
  while (list && ((n = strcasecmp(nick, list->nick)) < 0))
    list = list->next;
  if (n!=0) return FALSE;
  return TRUE;
}

/* ----------------------------------------------------------------------
 * if nick == 1: then a user on a channel is changing his/her nick
 * if nick == 2: user has /quit and we have to remove him from all chan's
 *               and return one of the channels that s/he was on
 * if nick == 3: we just want to find a channel that the user was on
 */
void user_chan_nick(chan_ptr list, char *old, char *new, int nick)
{
  chan_ptr walk;
  char did, chop;

  if (nick==3)
  {
    *new = 0;
    for (walk = list; walk; walk = walk->next)
      if (user_in_chan_list(walk->user_list, old))
      {
        strcpy(new, walk->str);
        return;
      }
    return;
  }
  if (nick!=1) *new = 0;
  for (walk = list; walk; walk = walk->next)
  {
    did = raw_remove_user(&(walk->user_list), old, &chop);
    if (did)
    {
      if (nick==1) raw_add_user(&(walk->user_list), new, chop);
      if (nick==2) strcpy(new, walk->str);
    }
  }
}

/* ---------------------------------------------------------------------- */
int user_on_chan(chan_ptr list, char *chan, char *nick)
{
  chan_ptr walk;
  user_ptr f;
  int n;

  for (walk = list; walk && strcasecmp(walk->str, chan); walk = walk->next);
  if (!walk) return FALSE;
  f = walk->user_list;
  while (f && (n = strcasecmp(nick, f->nick)) < 0)
    f = f->next;
  if (n==0) return TRUE;
  return FALSE;
}

/* ---------------------------------------------------------------------- */
void remove_from_chan(chan_ptr list, char *chan, char *nick)
{
  chan_ptr walk;
  char ischop;

  for (walk = list; walk && strcasecmp(walk->str, chan); walk = walk->next);
  if (!walk) return;
  raw_remove_user(&(walk->user_list), nick, &ischop);
}

/* ---------------------------------------------------------------------- */
void add_users_to_chan(chan_ptr list, char *chan, char *users)
{
  chan_ptr walk;
  char word[MAXNICKLEN+1];
  int ischop;

  for (walk = list; walk && strcasecmp(walk->str, chan); walk = walk->next);
  if (!walk) return;                   
  while (grab_word(&users, ' ', word)) {
    ischop = (*word=='@')?1:0;
    raw_add_user(&(walk->user_list), &word[ischop], ischop);
  }
}

/* ----------------------------------------------------------------------
 * Marks a channel (designated by the channel index) as being free to be
 * used by another window.
 * ---------------------------------------------------------------------- */

void free_chan(list, chan_index)
  chan_ptr list;
  int chan_index;
{
  chan_ptr walk;

  if (!chan_index) return;
  for (walk = list; walk && (walk->index != chan_index); walk = walk->next);
  if (walk) walk->window = NW;
}

/* ----------------------------------------------------------------------
 * Finds the first channel that is not already allocated to another window.
 * It returns -1 if there were no free channels
 * ---------------------------------------------------------------------- */

int find_chan_free(list, win)
  chan_ptr list;
  win_ptr win;
{
  chan_ptr walk = list;
  for (walk=list; walk && (walk->window!=NW); walk = walk->next);
  if (!walk) return -1;
  walk->window = win;
  return walk->index;
}

/* ----------------------------------------------------------------------
 * mark a channel as being used by a given window
 * ---------------------------------------------------------------------- */

int set_chan_used(list, chan_index, win)
  chan_ptr list;
  int chan_index;
  win_ptr win;
{
  chan_ptr walk = list;
  for (walk = list; walk && (walk->index != chan_index); walk = walk->next);
  if (!walk) return FALSE;
  walk->window = win;
  return TRUE;
}

/* ----------------------------------------------------------------------
 * get the name associated with a given channel index
 * ---------------------------------------------------------------------- */

int get_chan_name(list, index, name)
  chan_ptr list;
  int index;
  char *name;
{
  chan_ptr walk;
  for (walk = list; walk && (walk->index != index); walk = walk->next);
  if (!walk) return FALSE;
  (void)strcpy(name, walk->str);
  return TRUE;
}

/* ----------------------------------------------------------------------
 * returns the index and window that a channel is allocated to
 * ---------------------------------------------------------------------- */

int get_chan_num(list, name, win)
  chan_ptr list;
  char *name;
  win_ptr *win;
{
  chan_ptr walk;
  *win = NW;
  for (walk = list; walk && strcasecmp(walk->str, name); walk = walk->next);
  if (!walk) return 0;
  *win = walk->window;
  return walk->index;
}

/* ---------------------------------------------------------------------- *
 * returns the channel index that was allocated for the channel
 * ----------------------------------------------------------------------- */

int push_chan(list, chan_name, win)
  chan_ptr *list;
  char *chan_name;
  win_ptr win;
{
  chan_ptr ptr;
  char msg[MAXLEN];

  if ((ptr = (chan_ptr)malloc(sizeof(struct chan_node))) != NULL)
  {
    (void)memset(ptr, 0, sizeof(struct chan_node));
    (void)strcpy(ptr->str, chan_name);
    ptr->next = *list;
    ptr->window = win;
    if (*list == NULL)
      ptr->index = 1;
    else
      ptr->index = (*list)->index + 1;
    *list = ptr;
    return ptr->index;
  }
  return 0;
}

/* ----------------------------------------------------------------------
 * returns the window that the channel was allocated to
 * ---------------------------------------------------------------------- */

win_ptr delete_channel(list, chan)
  chan_ptr *list;
  char *chan;
{
  chan_ptr walk, back;
  back = NULL;

  walk = *list;
  while (walk) {
    if (strcasecmp(chan, walk->str) != 0) {
      back = walk;
      walk = walk->next;
    }
    else break;
  }
  if (!walk) return (win_ptr)NULL;
  if (back==NULL)
    *list = walk->next;
  else
    back->next = walk->next;
  free(walk);

  return walk->window;
}
