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

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

/* ---------------------------------------------------------------------- */
void raw_dump_channel(chan_ptr chan)
{
  user_ptr xx;
  say("Channel information:");
  if (!chan) {
    say("Nonexistant channel");
    return;
  }
  say("Name: %s", chan->str);
  say("Index: %d", chan->index);
  say("Window: %d", chan->window?chan->window->num:-1);
  say("MODE: %d", chan->mode);
  say("Key: %s", chan->key?chan->key:"-1");
  say("Limit: %d", chan->limit);
  say("User list:");
  for (xx = chan->user_list; xx; xx = xx->next)
    say("%s is %san op", xx->nick, xx->chanop?"":"not ");
  if (chan->next) raw_dump_channel(chan->next);
}

/* ---------------------------------------------------------------------- */
void recreate_chan_mode(chan_ptr list, int chan, char *mode)
{
  chan_ptr walk;
  char lame[MAXLEN];
  *mode = 0;
  for (walk = list; walk && (walk->index!=chan); walk = walk->next);
  if (!walk) return;
  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) {
    if (walk->key) {
      sprintf(lame, " %s", walk->key);
      strcat(mode, lame);
    }
  }
  if (walk->mode & C_LIMIT) {
    sprintf(lame, " %d", walk->limit);
    strcat(mode, lame);
  }
}

/* ---------------------------------------------------------------------- */
static 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 (*word) {
      if (ww->key) myfree(ww->key);
      ww->key = m_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++] == '+';
    if (word[ind]) raw_chan_mode(walk, word[ind++], add, &mode);
  }
}

/* ---------------------------------------------------------------------- */
static 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)mymalloc(sizeof(struct user_struct))) == NULL) return;
  new->nick = m_strcpy(nick);
  new->chanop = chop;
  new->next = f;
  if (!b) *list = new;
  else b->next = new;
}

/* ---------------------------------------------------------------------- */
static 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;
    myfree(f);
    return TRUE;
  }
  else *chop = FALSE;
  return FALSE;
}

/* ---------------------------------------------------------------------- */
static 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;
}

/* ---------------------------------------------------------------------- */
int is_chanop(chan_ptr list, char *chan, char *nick)
{
  chan_ptr walk;
  user_ptr f;
  int n = -1;
  for (walk = list; walk && strcasecmp(walk->str, chan); walk = walk->next);
  if (!walk) return 0;
  f = walk->user_list;
  while (f && (n = strcasecmp(nick, f->nick)) < 0)
    f = f->next;
  if (n==0) return f->chanop;
  return 0;
}

/* ---------------------------------------------------------------------- */
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[MAXLEN];
  int ischop, isv;

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

void get_channels_from_server(server_ptr serv, char *str, int len)
{
  chan_ptr walk;
  int need_space = FALSE;
  if (len>0) *str = 0;
  if (!serv) return;
  for (walk = (chan_ptr)get_server_chanlist(serv); walk; walk = walk->next) {
    if (need_space) {
      if (len>1) {
        strcat(str, " ");
        len--;
      }
    } else need_space = TRUE;
    if (len > strlen(walk->str)) {
      len -= strlen(walk->str);
      strcat(str, walk->str);
    }
  }
}

/* ----------------------------------------------------------------------
 * 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) {
    if (walk->window) walk->window->channel = 0;
    walk->window = NW;
  }
}

/* ----------------------------------------------------------------------
 * Finds the first channel that is not already allocated to another window.
 * It returns 0 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 0;
  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;
  free_chan(list, chan_index);
  walk->window = win;
  return TRUE;
}

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

char *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 NULL;
  if (name) strcpy(name, walk->str);
  return walk->str;
}

/* ----------------------------------------------------------------------
 * 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)mymalloc(sizeof(struct chan_node))) != NULL)
  {
    (void)memset(ptr, 0, sizeof(struct chan_node));
    ptr->str = m_strcpy(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;
}

chan_ptr kill_server_channels(chan_ptr list, char **to_join)
{
  char rejoin_list[1024], count = 0;
  chan_ptr tmp;
  user_ptr ll, tmp_user;
  rejoin_list[0] = 0;
  while (list)
  {
    tmp = list;
    list = list->next;
    if (count++) strcat(rejoin_list, ",");
    strcat(rejoin_list, tmp->str);
    myfree(tmp->str);
    if (tmp->window) tmp->window->channel = 0;
    if (tmp->key) myfree(tmp->key);
    ll = tmp->user_list;
    while (ll) {
      tmp_user = ll;
      ll = ll->next;
      myfree(tmp_user->nick);
      myfree(tmp_user);
    }
  }
  if (count) *to_join = m_strcpy(rejoin_list);
  else *to_join = NULL;
  return NULL;
}

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

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

  walk = *list;
  while (walk && (strcasecmp(chan, walk->str) != 0)) {
    back = walk;
    walk = walk->next;
  }
  if (!walk) return (win_ptr)NULL;
  if (back==NULL) *list = walk->next;
  else back->next = walk->next;
  myfree(walk->str);
  if (walk->key) myfree (walk->key);
  ll = walk->user_list;
  while (ll) {
    xx = ll;
    ll = ll->next;
    myfree(xx);
  }
  save = walk->window;
  myfree(walk);
  return save;
}
