#include <string.h>
#include <stdlib.h>
#ifdef __GNUC__
#else
#include <ctype.h>
#endif
#include "base_includes.h"
#include "window_i.h"
#include "chan.h"
#include "level.h"
#include "winfunc.h"
#include "split.h"
#include "inp.h"
#include "sock.h"
#include "mode.h"

extern server_ptr gsrv, server_list;
extern win_ptr gwin, awins;

win_ptr back_win = NW;

/* ---------------------------------------------------------------------- */ 
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* ---------------------------------------------------------------------- */ 
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)
{
  if (!gwin) return;
  gwin->hold_curr = 0;
  gwin->is_holding = FALSE;
  gwin->hold_count = 0;
}

/* ---------------------------------------------------------------------- */ 
static void fix_level(server_ptr serv)
{
  win_ptr walk;
  int total = ALL;
  for (walk = awins; walk; walk = walk->next)
     if (walk->server == serv) {
       walk->wlevel = walk->wlevel & total;
       total -= walk->wlevel;
     }
}
/* ---------------------------------------------------------------------- */ 
extern char def_nick[];

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, chan, status, count;

  temp = server_line = next_arg(&str);
#ifndef ENABLE_SERVER
  win_say(gwin, "*** The SERVER command has been disabled by the admin.");
  return str;
#endif
#define NEXT_CHAR(__a, __b, __c, __d) \
 __d = 0; while (*__a && (*__a != __b)) __c[__d++] = *__a++; \
 __c[__d] = 0; if (*__a == __b) __a++;

  NEXT_CHAR(temp, ':', ser, count);
  NEXT_CHAR(temp, ':', port_s, count);
  NEXT_CHAR(temp, ':', pword, count);
  NEXT_CHAR(temp, ':', tn, count);
/*
  grab_word(&temp, ':', ser);
  grab_word(&temp, ':', port_s);
  grab_word(&temp, ':', pword);
  grab_word(&temp, ':', tn);
*/
  port = atoi(port_s);
  tserv = lookup_server(ser, (*tn)?tn:NULL, TRUE, port);
  if (tserv) {
    win_serverset(tserv);
    win_say(gwin, "*** Server reset");
/*    fix_level(tserv); */
    return str;
  }
  tserv = lookup_server(ser, (*tn)?tn:NULL, FALSE, port);
  add_server(ser, tn, port, pword, serv, tserv);
  return str;
}

/* ---------------------------------------------------------------------- */ 
static void win_notimp(char *word, char **rest, int opt)
{
  win_say(gwin, "*** WINDOW %s has not been implemented yet", word);
}
/* ---------------------------------------------------------------------- */ 
static void win_list(char *word, char **rest, int opt)
{
  win_ptr walk, oldwin;
  char nick[MAXLEN], 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(get_server_chanlist(walk->server), walk->channel, chan)))
      (void)strcpy(chan, "<none>");
    show_level(NULL, walk->wlevel, 0, level); /* space first */
    say("*** %3.3d %c %-9.9s %-9.9s %-10.10s %-9.9s %-10.10s %-6s",
        walk->num, (gwin==walk)?'*':(walk->hidden?'h':' '), nick,
        *walk->name?walk->name:"<none>",
        chan, walk->query?walk->query:"",
        walk->server?get_server_name(walk->server):"<none>",
        level);
    walk = walk->next;
  }
  set_outwin(oldwin);
}

/* ---------------------------------------------------------------------- */ 
static 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 win_say(gwin, "*** GOTO: Illegal value.");
}
/* ---------------------------------------------------------------------- */ 
static 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);
}
/* ---------------------------------------------------------------------- */ 
static void win_killswap(char *orig, char *rest, int opt)
{
  win_ptr rm = gwin;
  window_swap(last_invisible());
  (void)window_delete(rm);
}
/* ---------------------------------------------------------------------- */ 
static 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);
}
/* ---------------------------------------------------------------------- */ 
static 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_say(gwin, "*** SHOW: no such window %d", atoi(word));
  else if (!win->hidden)
    win_say(gwin, "*** SHOW: window %d is not hidden", atoi(word));
  else {
    y = gwin->p_y;
    size = gwin->m_line;
    update_off();
    window_resize(gwin, size/2, TRUE, TRUE);
    window_setsize(win, win->m_column, size/2);
    window_rawshow(win, win->p_x, y);
    window_reorder();
    update_on();
    gwin = win;
  }
}
/* ---------------------------------------------------------------------- */ 
int server_has_window(server_ptr srv)
{
  win_ptr walk;
  for (walk=awins;walk;walk=walk->next)
    if (walk->server == srv) return 1;
  return 0;
}
/* ---------------------------------------------------------------------- */ 
extern server_ptr server_list;
static void win_remove_old_servers(void)
{
  server_ptr swalk, save;
  int has_window;
  save = swalk = server_list;
  while (swalk) {
    save = swalk;
    swalk = get_server_next(swalk);
    if (!server_has_window(save))
      remove_server(save, 1);
  }
}
/* ---------------------------------------------------------------------- */ 
static void win_server(char *orig, char **rest, int opt)
{
  server_ptr save;
  save = gwin->server;
  *rest = win_newserver(*rest, NULL);
  win_remove_old_servers();
}
/* ---------------------------------------------------------------------- */ 
static struct command_node onoff_options[] =
{ {{"off"}, 0}, {{"on"}, (char *)1}, {{"toggle"}, (char *)2}, {{NULL}, NULL} };

int onoff_toggle(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) win_say(gwin, "*** Bad option -- %s.", word);
  else if (new != old)
  {
    if (new == 1) {
      if ((gwin->file = fopen(gwin->logfile, "a")))
        win_say(gwin, "*** Currently logging to [%s]", gwin->logfile);
      else
        win_say(gwin, "*** Couldn't open [%s]", gwin->logfile);
    } else {
      if (gwin->file) (void)fclose(gwin->file);
      win_say(gwin, "*** 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;

  word = next_arg(str);
  if (gwin->file)
    win_say(gwin, "*** Currently logging to another file [%s].", gwin->logfile);
  else {
    (void)strcpy(gwin->logfile, word);
    win_say(gwin, "*** New logfile: %s", gwin->logfile);
  }
}
/* ---------------------------------------------------------------------- */ 
static 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?get_server_name(win->server):"<none>");
  if (!get_chan_name(get_server_chanlist(gsrv), 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 ? "ON" : "OFF");
  say("***    Logging: %s", win->file ? "ON" : "OFF");
  say("***    Logfile: %s", win->logfile);
  show_level(NULL, win->notify_level, 0, name);
  say("***    Notify level: %s", name);
  say("***    Notify times: %d", win->notify_count);
  say("***    Hold mode: %s", win->hold_mode?"ON":"OFF");
  say("***    Hold mode max: %d", win->hold_mode_max);
  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("***    Major Mode: %s", win->mode?win->mode->overall_name:"");
  set_outwin(oldwin);
}
/* ---------------------------------------------------------------------- */ 
static void win_back(char *str, char **rest, int opt)
{
  if (!is_window(back_win))
    win_say(gwin, "*** 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;

static 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);
}

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

/* ---------------------------------------------------------------------- */ 
static 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)) win_say(gwin, "*** SWAP: No such window %d", atoi(word));
  else if (!win->hidden)
    win_say(gwin, "*** SWAP: Window %d is not hidden", atoi(word));
  else window_swap(win);
}

/* ---------------------------------------------------------------------- */ 
/* opt = 1 -> shrink  opt = 0 -> grow                                     */
/* ---------------------------------------------------------------------- */
static void win_size(char *str, char **rest, int opt)
{
  char *word;
  int num, good, next = 0;
  win_ptr other;
  word = m_grab_word(rest, ' ');
  num = atoi(word);
  if (num<=0) {
    win_say(gwin, "*** Illegal number of lines (%d)", num);
    return;
  }
  if (opt)
    if ((gwin->m_line-num) <= 4) {
      win_say(gwin, "*** Don't you think this window is small enough?!");
      return;
    }
  other = next_visible(gwin);
  if ((other==NW) || (!opt && ((other->m_line-num) <= 4))) other = NW;
  if (other==NW) {
    other = prev_visible(gwin);
    if ((other==NW) || (!opt && ((other->m_line-num) <= 4))) other = NW;
  } else next = 1;
  if (other==NW) {
    win_say(gwin, "*** There is no space to %s this window",
            opt?"shrink":"expand");
    return;
  }
  update_off();
  (void)window_resize(gwin, num, opt, !next);
  (void)window_resize(other, num, !opt, next);
  update_on();
}
/* ---------------------------------------------------------------------- */ 
static void win_notify_level(char *str, char **rest, int opt)
{
  char *old, *word, *word2;
  int dummy = 0, level = 0;
  win_ptr walk;
  old = word = m_grab_word(rest, ' ');
  while (*word) {
    word2 = m_grab_word(&word, ',');
    (void)lookup_level(word2, &level, &dummy);
    myfree(word2);
  }
  gwin->notify_level = level;
  show_level("Notify level:", gwin->notify_level, 0, NULL);
  myfree(old);
}
/* ---------------------------------------------------------------------- */ 
static 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);
    myfree(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;
    }
#ifdef ANAL_LEVEL
  gwin->wlevel |= (ALL-total);
#endif
  show_level("Window level:", gwin->wlevel, 0, NULL);
  myfree(old);
}
/* ---------------------------------------------------------------------- */ 
static void win_next(char *str, char **rest, int opt)
{
  int large, sl = -1;
  win_ptr walk, save = NW;
  large = gwin->p_y;
  save = next_visible(gwin);
  if (save==NW) save = awins;
  if (is_window(save) && !hidden(save)) {
    back_win = gwin;
    gwin = save;
  }
}

/* ---------------------------------------------------------------------- */ 
static 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;
}
/* ---------------------------------------------------------------------- */ 
static 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();
}
/* ---------------------------------------------------------------------- */ 
static void win_notify_count(char *str, char **rest, int opt)
{
  gwin->notify_count = atoi(next_arg(rest));
  win_say(gwin, "*** The window notify count is now: %d", gwin->notify_count);
}
/* ---------------------------------------------------------------------- */ 
static void win_setmode_f(char *str, char **rest, int opt)
{
  set_major_mode(next_arg(rest));
  if (gwin->mode)
  {
    win_say(gwin, "*** The window's mode is: %s", gwin->mode->overall_name);
  } else
    win_say(gwin, "*** This window has no mode");
}
/* ---------------------------------------------------------------------- */ 
static void win_hold_mode_max(char *str, char **rest, int opt)
{
  gwin->hold_mode_max = atoi(next_arg(rest));
  win_say(gwin, "*** The maximum number of lines to hold is : %d",
          gwin->hold_mode_max);
}
/* ---------------------------------------------------------------------- */ 
win_ptr get_win_by_name(char *str)
{
  win_ptr w;
  int num;
  for (w=awins; w; w = w->next)
    if (strcasecmp(w->name, str) == 0) return w;
  num = atoi(str);
  for (w=awins; w; w = w->next)
    if (num == w->num) return w;
  return NW;
}
/* ---------------------------------------------------------------------- */ 
static 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 },
  { "mode",          win_setmode_f,      0 },
  { "move",          win_notimp,         0 },
  { "name",          win_name,           0 },
  { "new",           split_window,       0 },
  { "next",          win_next,           0 },
  { "notify_count",  win_notify_count,   0 },
  { "notify_level",  win_notify_level,   0 },
  { "previous",      win_notimp,         0 },
  { "prompt",        win_notimp,         0 },
  { "refnum",        win_refnum,         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, int varpar, char *subparams)
{
  char *word, *str, saved[MAXLEN];
  int cmd_cnt;
  CmdList *command;

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