#include <string.h>
#include <stdlib.h>
#include <ctype>
#include "global.h"
#include "window.h"
#include "command.h"
#include "constants.h"

extern server_ptr gsrv, server_list,
       change_server(char *ser, int port, char *nick, server_ptr old);
extern win_ptr gwin, awins, previous_window(win_ptr, int);
extern int window_swap(win_ptr win), hidden(win_ptr);
extern void update_off(), update_on();
extern int window_resize(win_ptr win, int change, int shrink, int top);
extern win_ptr window_exists(int num);

win_ptr back_win = NW;

/* ----------------------------------------------------------------------
 * This routing is highly destructive. It will modify whatever string is
 * passed to it.
 * assumes: str is not null
 */
char *next_arg(char **str)
{
  char *saved, *walk;
  walk = *str;
  while (isspace(*walk)) walk++;
  saved = walk;
  while (!isspace(*walk) && *walk) walk++;
  if (*walk) *walk++ = 0;
  *str = walk;
  return saved;
}

/* ---------------------------------------------------------------------- */ 
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* ---------------------------------------------------------------------- */ 
int is_window(win_ptr win)
{
  win_ptr walk;
  for (walk=awins; walk; walk = walk->next)
    if (walk==win)
      if (win->id) return TRUE;
      else return FALSE;
  return FALSE;
}

/* ---------------------------------------------------------------------- */ 
void init_hold_mode(void)
{
  gwin->hold_curr = 0;
  gwin->is_holding = FALSE;
  gwin->hold_count = 0;
}

/* ---------------------------------------------------------------------- */ 
void win_fixlevels(void)
{
  server_ptr serv;
  win_ptr walk;
  int total, needed;
  total = NONE;

  for (serv=server_list;serv;serv=serv->next) {
    total = NONE;
    for (walk=awins;walk;walk=walk->next)
      if (walk->server==serv) total |= walk->wlevel;
    needed = ALL - total;
    for (walk=awins;walk;walk=walk->next)
      if (walk->server==serv) {
        walk->wlevel |= needed;
        break;
      }
  }
  reset_outwin();
}

/* ---------------------------------------------------------------------- */ 
extern char def_nick[];
extern server_ptr lookup_server(char *server, char *nick);
extern void win_serverset(server_ptr srv);

char *win_newserver(char *str, server_ptr serv)
{
  char *temp, *server_line, ser[MAXLEN], tn[MAXLEN];
  char port_s[MAXLEN], pword[MAXLEN];
  server_ptr tserv;
  int port = 0;
  win_ptr old;

  old = set_outwin(gwin);
  temp = server_line = next_arg(&str);
  (void)grab_word(&temp, ':', ser);
  (void)grab_word(&temp, ':', tn);
  if (!*tn) {
    tserv = lookup_server(ser, NULL);
    if (!gsrv) (void)strcpy(tn, def_nick);
    else (void)strcpy(tn, gsrv->nick);
  } else tserv = lookup_server(ser, tn);
  if ((tserv) && (tserv->connected)) {
    win_serverset(tserv);
    say("*** Server reset");
    win_fixlevels();
  } else {
    (void)grab_word(&temp, ':', port_s);
    if (!(port = atoi(port_s))) port = 6667;
    (void)grab_word(&temp, ':', pword);
    if (tserv = change_server(ser, port, tn, serv)) {
      win_serverset(tserv);
      win_fixlevels();
      say("*** New server set.");
    }
  }
  set_outwin(old);
  return str;
}

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

void win_notimp(char *word, char **rest, int opt)
{
  say("*** WINDOW %s has not been implemented yet", word);
}

/* ---------------------------------------------------------------------- */ 
void win_list(char *word, char **rest, int opt)
{
  win_ptr walk, oldwin;
  char nick[MAXNICKLEN+1], chan[MAXLEN], level[MAXLEN];
  walk = awins;
  oldwin = set_outwin(gwin);
  say("*** Ref C Nick      Name  Channel    Query     Server     Level");
  while (walk) {
    sl_nick(walk, nick);
    if (!(walk->server &&
          get_chan_name(walk->server->chan_list, walk->channel, chan)))
      (void)strcpy(chan, "<none>");
    show_level(NULL, walk->wlevel, 0, level); /* space first */
    say("*** %3.3d %c %-9.9s %-5.5s %-10.10s %-9.9s %-10.10s %-5s %s",
        walk->num, (gwin==walk)?'*':' ', nick, *walk->name?walk->name:"<none>",
        chan, walk->query, walk->server->server_name, level,
        walk->hidden?"hidden":"");
    walk = walk->next;
  }
  set_outwin(oldwin);
}

/* ---------------------------------------------------------------------- */ 
void win_goto(char *orig, char **rest, int opt)
{
  char *word;
  win_ptr walk;
  word = next_arg(rest);
  if ((walk = (win_ptr)window_num(atoi(word)) )) window_swap(walk);
  else say("*** GOTO: Illegal value.");
}

/* ---------------------------------------------------------------------- */ 
void win_hide(char *orig, char **rest, int opt)
{
  win_ptr walk;
  if (!opt) (void)window_hide(gwin);
  else for (walk=awins; walk; walk=walk->next)
         if (walk!=gwin) (void)window_hide(walk);
}

/* ---------------------------------------------------------------------- */ 
extern win_ptr last_invisible();
/* ---------------------------------------------------------------------- */ 
void win_killswap(char *orig, char *rest, int opt)
{
  win_ptr rm = gwin;
  window_swap(last_invisible());
  (void)window_delete(rm);
}

/* ---------------------------------------------------------------------- */ 
void win_kill(char *orig, char **rest, int opt)
{
  win_ptr walk;
  if (opt==0) (void)window_delete(gwin);
  else for (walk=awins; walk && !walk->hidden; walk= walk->next)
         if (walk!=gwin) (void)window_delete(walk);
}

/* ---------------------------------------------------------------------- */ 
extern void window_setsize(), window_rawshow();
/* ---------------------------------------------------------------------- */ 
void insert_window(win_ptr targ, win_ptr win, int before)
{
  win_ptr f, p;
  for (p = NW, f = awins; f; p = f, f = f->next)
    if (before && (f == targ)) break;
    else if (!before && (p == targ)) break;
  if (is_window(p)) p->next = win;
  else awins = win;
  win->next = f;
}

/* ---------------------------------------------------------------------- */ 
void remove_window(win_ptr win)
{
  win_ptr f, p;
  for (p = NW, f = awins; f && (f != win); p = f, f = f->next);
  if (!f) return;
  if (is_window(p)) p->next = f->next;
  else awins = f->next;
}

/* ---------------------------------------------------------------------- */ 
void win_show(char *orig, char **rest, int opt)
{
  int y, size;
  char *word;
  win_ptr win;
  word = next_arg(rest);
  win = window_exists(atoi(word));
  if (is_window(win) && win->hidden) {
    y = gwin->p_y;
    size = gwin->m_line;
    window_resize(gwin, size/2, TRUE, TRUE);
    remove_window(win);
    window_setsize(win, win->m_column, size/2);
    window_rawshow(win, win->p_x, y);
    insert_window(gwin, win, TRUE);
    gwin = win;
  }
}

/* ---------------------------------------------------------------------- */ 
void win_server(char *orig, char **rest, int opt)
{
  *rest = win_newserver(*rest, NULL);
}

/* ---------------------------------------------------------------------- */ 
struct command_node onoff_options[] =
{ {{"off"}, 0}, {{"on"}, (char *)1}, {{"toggle"}, (char *)2}, {{NULL}, NULL} };

int onoff_toggle(orig, str, good)
  int orig;
  char *str;
  int *good;
{                                                  
  int dummy, ind;
  if (good) *good = 1;
  ind = lookup_command(onoff_options, str, &dummy);
  if ((ind == 0) || (ind == 1)) return dummy;
  if (ind==2) return (!orig);
  if (good) *good = 0;
  return orig;
}

/* ---------------------------------------------------------------------- */ 
void win_set_log(char *orig, char **str, int opt)
{
  char *word;
  int onoff, old, new, good;
  win_ptr oldwin;

  oldwin = set_outwin(gwin);
  old = gwin->file != NULL;
  word = next_arg(str);
  new = onoff_toggle(old, word, &good);
  if (!good) say("*** Bad option -- %s.", word);
  else if (new != old)
  {
    if (new == 1) {
      if ((gwin->file = fopen(gwin->logfile, "a")))
        say("*** Currently logging to [%s]", gwin->logfile);
      else
        say("*** Couldn't open [%s]", gwin->logfile);
    } else {
      if (gwin->file) (void)fclose(gwin->file);
      say("*** Logging has been turned off for [%s]", gwin->logfile);
      gwin->file = NULL;
    }
  }
  set_outwin(oldwin);
  return;
}

/* ---------------------------------------------------------------------- */ 
void win_set_logfile(char *orig, char **str, int opt)
{
  char *word;
  win_ptr oldwin;

  oldwin = set_outwin(gwin);
  word = next_arg(str);
  if (gwin->file)
    say("*** Currently logging to another file [%s].", gwin->logfile);
  else {
    (void)strcpy(gwin->logfile, word);
    say("*** New logfile: %s", gwin->logfile);
  }
  set_outwin(oldwin);
}

/* ---------------------------------------------------------------------- */ 
void win_stats(char *word, char **str, int opt)
{
  win_ptr win = gwin, oldwin;
  char name[MAXLEN];

  win = gwin;
  oldwin = set_outwin(win);
  if (*win->name) say("*** Window [%s] (%d)", win->name, win->num);
  else say("*** Window %d", win->num);
  say("***    Size: %dx%d at (%d,%d)", win->m_line, win->m_column,
      win->p_x, win->p_y);
  say("***    Server: %s", win->server ? win->server->server_name : "<none>");
  if (!get_chan_name(gsrv->chan_list, win->channel, name))
    (void)strcpy(name, "<none>");
  say("***    Current channel: %s", name);
  say("***    Query User: %s", *(win->query) ? win->query : "<none>");
  say("***    Scrolling: %s", win->scroll ? "YES" : "NO");
  say("***    Logging: %s", win->file ? "YES" : "NO");
  say("***    Logfile: %s", win->logfile);
  say("***    Notification: unimplemented");
  say("***    Hold mode: %s", win->hold_mode?"YES":"NO");
  show_level(NULL, win->wlevel, 0, name);
  say("***    Window level: %s", name);
  show_level(NULL, win->lastloglevel, 0, name);
  say("***    Lastlog level: %s", name);
  say("***    Notify level: unimplemented");
  set_outwin(oldwin);
}

/* ---------------------------------------------------------------------- */ 
void win_back(char *str, char **rest, int opt)
{
  if (!is_window(back_win)) say("*** Something happenned to the old window.");
  else window_swap(back_win);
}

/* ---------------------------------------------------------------------- */ 
/* For some reason i am not sure if this routine will work properly       */
extern int max_win_num;

void win_forward(char *str, char **rest, int opt)
{
  int winnum, old;
  win_ptr newwin = NW;
  old = gwin->num;
  winnum = gwin->num + 1;
  do {
    if (winnum>max_win_num) winnum = 1;
    if (newwin = window_exists(winnum))
      window_swap(newwin);
  } while ((++winnum != old) && !newwin);
}

/* ---------------------------------------------------------------------- */ 
void win_refnum(char *str, char **rest, int opt)
{
  char *word;
  win_ptr newwin, oldwin;
  oldwin = set_outwin(gwin);
  word = m_grab_word(rest, ' ');
  newwin = window_exists(atoi(word));
  if (!(newwin && !newwin->hidden)) {
    say("*** Window is hidden or could not be found.");
    return;
  }
  window_swap(newwin);
  free(word);
  set_outwin(oldwin);
}

/* ---------------------------------------------------------------------- */ 
void win_swap(char *str, char **rest, int opt)
{
  char *word;
  win_ptr win = NW;
  word = next_arg(rest);
  if (strcasecmp(word, "LAST")==0) win = back_win;
  else win = window_exists(atoi(word));
  if (!is_window(win)) say("*** No such window %d", atoi(word));
  else window_swap(win);
}

/* ---------------------------------------------------------------------- */ 
/* opt = 1 -> shrink  opt = 0 -> grow                                     */
/* ---------------------------------------------------------------------- */
void win_size(char *str, char **rest, int opt)
{
  char *word;
  int num, good;
  win_ptr other, oldwin;
  oldwin = set_outwin(gwin);
  word = m_grab_word(rest, ' ');
  num = atoi(word);
  if (num<=0) {
    say("*** Illegal number of lines (%d)", num);
    set_outwin(oldwin);
    return;
  }
  other = previous_window(gwin, FALSE);
  if (!other) {
    other = previous_window(gwin, TRUE);
    num = -num;
  }
  if (!other) {
    say("*** You're allready full screen");
    set_outwin(oldwin);
    return;
  }
  if (opt) good = ((gwin->m_line - num) >= 4);
  else good = ((other->m_line - num) >= 4);
  if (!good) {
    say("*** Not enough lines!");
    set_outwin(oldwin);
    return;
  }
  (void)window_resize(gwin, num, opt, FALSE);
  (void)window_resize(other, num, !opt, TRUE);
}

/* ---------------------------------------------------------------------- */ 
void win_level(char *str, char **rest, int opt)
{
  char *old, *word, *word2;
  int dummy = 0, level, total;
  win_ptr walk;
  old = word = m_grab_word(rest, ' ');
  while (*word) {
    word2 = m_grab_word(&word, ',');
    (void)lookup_level(word2, &gwin->wlevel, &dummy);
    free(word2);
  }
  level = ALL-gwin->wlevel;
  total = gwin->wlevel;
  for (walk=awins;walk;walk=walk->next)
    if (walk->server==gsrv) {
      if (walk!=gwin) walk->wlevel &= level;
      total |= walk->wlevel;
    }
  gwin->wlevel |= (ALL-total);
  show_level("Window level:", gwin->wlevel, 0, NULL);
  free(old);
}

/* ---------------------------------------------------------------------- */ 
void win_next(char *str, char **rest, int opt)
{
  int large, sl = -1;
  win_ptr walk, save = NW;
  large = gwin->p_y;
  save = previous_window(gwin, FALSE);
  if (!is_window(save) || hidden(save)) save = awins;
  if (is_window(save) && !hidden(save)) {
    back_win = gwin;
    gwin = save;
  }
}

/* ---------------------------------------------------------------------- */ 
extern void window_setsize(win_ptr, int, int);
/* ---------------------------------------------------------------------- */ 
void win_resize(char *str, char **rest, int opt)
{
  char word[MAXLEN];

  if (!gwin) return;
  grab_word(rest, ' ', word);
  window_setsize(gwin, atoi(word), gwin->m_line);
  win_size(str, rest, opt);
}

/* ---------------------------------------------------------------------- */ 
void win_name(char *str, char **rest, int opt)
{
  char *word;
  word = next_arg(rest);
  (void)strncpy(gwin->name, word, sizeof(gwin->name));
  gwin->name[sizeof(gwin->name)] = 0;
}

/* ---------------------------------------------------------------------- */ 
void win_hold_mode(char *str, char **rest, int opt)
{
  int old;
  old = gwin->hold_mode;
  gwin->hold_mode = onoff_toggle(old, next_arg(rest), NULL);
  if (gwin->hold_mode && !old) init_hold_mode();
}
/* ---------------------------------------------------------------------- */ 
void win_hold_mode_max(char *str, char **rest, int opt)
{
  gwin->hold_mode_max = atoi(next_arg(rest));
}
/* ---------------------------------------------------------------------- */ 
win_ptr get_win_by_name(char *str)
{
  win_ptr w;
  for (w=awins; w; w = w->next)
    if (strcasecmp(w->name, str) == 0) return w;
  return NW;
}

/* ---------------------------------------------------------------------- */ 
CmdList window_list[] =
{
  { "",              win_stats,          0 },
  { "back",          win_back,           0 },
  { "balance",       win_notimp,         0 },
  { "forward",       win_forward,        0 },
  { "goto",          win_goto,           0 },
  { "grow",          win_size,           0 },
  { "hide",          win_hide,           0 },
  { "hide_other",    win_hide,           1 },
  { "hold_mode",     win_hold_mode,      0 },
  { "hold_mode_max", win_hold_mode_max,  0 },
  { "kill",          win_kill,           0 },
  { "kill_other" ,   win_kill,           1 },
  { "killswap",      win_killswap,       0 },
  { "lastlog",       win_notimp,         0 },
  { "level",         win_level,          0 },
  { "list",          win_list,           0 },
  { "log",           win_set_log,        0 },
  { "logfile",       win_set_logfile,    0 },
  { "move",          win_notimp,         0 },
  { "name",          win_name,           0 },
  { "new",           split_window,       0 },
  { "next",          win_next,           0 },
  { "previous",      win_notimp,         0 },
  { "prompt",        win_notimp,         0 },
  { "refnum",        win_refnum,         0 },
  { "resize",        win_notimp,         0 },
  { "scroll",        win_notimp,         0 },
  { "server",        win_server,         0 },
  { "show",          win_show,           0 },
  { "shrink",        win_size,           1 },
  { "swap",          win_swap,           0 }
};
 
/* ---------------------------------------------------------------------- */ 
#define NUMBER_OF_WINCOMMANDS (sizeof(window_list) / sizeof(CmdList)) - 1
/* ---------------------------------------------------------------------- */ 

void user_window(char *real_str)
{
  char *word, *str, saved[MAXLEN];
  int cmd_cnt;
  CmdList *command;

  strcpy(saved, real_str);
  update_off();
  str = saved;
  do {
    if (!is_window(gwin))  {
      say ("we got problems");
      break;
    }
    word = next_arg(&str);
    command = find_command(window_list, NUMBER_OF_WINCOMMANDS, word, &cmd_cnt);

    if (cmd_cnt == 0) say("*** Option %s is not known.", word);
    else if (((cmd_cnt < 0) || (cmd_cnt == 1)) && command) {
      command->cmdfcn(word, &str, command->varpar);
    } else say("*** Option %s is ambiguous.", word);
  } while (*str);
  gsrv = gwin->server;
  updatestatwin();
  find_input_prompt(FALSE);
  reset_outwin();
  update_on(); 
}
