/* Tell any include files that we are in WINDOW */
#define IN_WINDOW

#include <stdio.h>
#include <descrip.h>
#ifdef __GNUC__
#define SMG$M_ERASE_PBD    1
#define SMG$M_ERASE_LINE   1
#define SMG$M_ERASE_TO_EOL 2
#define SMG$M_BOLD         1
#define SMG$M_REVERSE      2
#define SMG$M_UNDERLINE    8
#define SMG$M_WRAP_CHAR    1
#define SMG$M_UP           1
#else
#include <smgdef.h>
#endif
#include <ssdef.h>
#include <string.h>
#include <stdlib.h>
#include <starlet.h>
#include <smg$routines.h>
#include <lib$routines.h>

#include "base_includes.h"
#include "list.h"
#include <stdarg.h>
#include "server_i.h"
#include "chan.h"
#include "alias.h"
#include "window_i.h"
#include "system.h"
#include "sock.h"
#include "winfunc.h"
#include "inp.h"
#include "queue.h"
#include "on.h"
#include "list.h"

static void sl_away(), sl_window(), sl_channel(), sl_time();
static void sl_query(), sl_chanmode(), sl_hold_lines(), sl_user();
static void sl_insert(), sl_overwrite(), sl_activity();

extern int log_level;
extern server_ptr gsrv;
extern win_ptr outwin, back_win;

struct hold_list {
  hold_ptr next;    /* Next one in the list */
  int len;          /* How long is this string? */
  char *str;        /* This must be at the end of the structure */
  char *att;
};

struct pasteboard {
  int id, maxrows, maxcols;
} pb;

struct replace{
  char trigger[4];
  void (*replace)();
};

win_ptr awins = NW;
extern win_ptr gwin;

static unsigned inwin = 0;
static int pl;
int max_win_num = 1;
int DUMB = FALSE;
static int start_row = 1;
extern int INPUT_DEFINED;

/* -------------------------------------------------------------------------*/
win_ptr get_curr_win(void)
{
  return gwin;
}

/* -------------------------------------------------------------------------*/
win_mode win_getmode(win_ptr win)
{
  return win?win->mode:NULL;
}
void win_setmode(win_ptr win, win_mode newmode)
{
  if (win) win->mode = newmode;
}
/* -------------------------------------------------------------------------*/
win_ptr next_window(win_ptr win)
{
  if (!win) return awins;
  return win->next;
}
head_ptr get_window_lastlog(win_ptr win)
{
  if (!win) return NULL;
  return win->lastlog;
}
server_ptr get_window_server(win_ptr win)
{
  if (!win) return NULL;
  return win->server;
}
void set_window_server(win_ptr win, server_ptr srv)
{
  if (win) win->server = srv;
}

/* -------------------------------------------------------------------------*/
static void getx(int *r)
{
  if (!inwin) *r = 0;
  else *r = smg$cursor_column(&inwin);
}

/* -------------------------------------------------------------------------*/
void win_serverset(server_ptr s)
{
  if (gwin) gwin->server = s;
  gsrv = s;
  reset_outwin();
}

/* -------------------------------------------------------------------------*/
/*  "%D", NULL,         /* May contain %D (user) */
/*  "%H", NULL,         /* Hold mode */
/*  "%M", NULL,         /* May contain %M (number of mail) */
/*  "%S", NULL,         /* May contain %S (servername) */
/*  "%@", NULL,         /* chanop */
/*  "%*", NULL,         /* Are you oper? */
/*  "%#", NULL,         /* May contain %# (user mode) */
/* -------------------------------------------------------------------------*/
static struct replace status_list[] =
{
  { {"%+"}, sl_chanmode} ,  /* May contation %+ */
  { {"%A"}, sl_away} ,
  { {"%B"}, sl_hold_lines}, /* May contain %B (held lines) */
  { {"%C"}, sl_channel} ,   /* May contain %C (channel) */
  { {"%F"}, sl_activity} ,  /* May contain %F (activity) */
  { {"%I"}, sl_insert} ,    /* insert mode */
  { {"%N"}, sl_nick} ,      /* current nick */
  { {"%O"}, sl_overwrite} , /* overwrite mode */
  { {"%Q"}, sl_query} ,     /* May contain %Q (query user) */
  { {"%R"}, sl_window2},    /* chanop */
  { {"%T"}, sl_time} ,
  { {"%U"}, sl_user} ,
  { {"%W"}, sl_window} ,
  { {""} ,  NULL} 
};

/* -------------------------------------------------------------------------
 * scan through the windows and look for the window that has it's window
 * level set to type.
 * ------------------------------------------------------------------------- */

win_ptr type_winget(server, type)
  server_ptr server;
  int type;
{
  win_ptr walk, serv = NW;
  for (walk=awins; walk; walk = walk->next)
    if (walk->server == server)
      if (walk->wlevel & type) return walk;
      else serv = walk;
  if (gwin && gwin->server == server) return gwin;
  if (outwin && outwin->server == server) return outwin;
  return serv;
}

void reset_outwin(void)
{
  outwin = type_winget(gsrv, CRAP);
}

/* -------------------------------------------------------------------------
 * Delete all of the windows and the pasteboard that we have created 
 * ------------------------------------------------------------------------- */
static void delete_a_window(win_ptr win)
{
  int status;
  if (win->id) status = smg$unpaste_virtual_display(&win->id, &pb.id);
  if (win->id) status = smg$delete_virtual_display(&win->id);
  if (win->status) status = smg$unpaste_virtual_display(&win->status, &pb.id);
  if (win->status) status = smg$delete_virtual_display(&win->status);
}

void delete_windows(void)
{
  win_ptr walk;
  int flags = SMG$M_ERASE_PBD, status;

  smg$unpaste_virtual_display(&inwin, &pb.id);
  smg$delete_virtual_display(&inwin);
  inwin = 0;
  for (walk=awins; walk; walk = walk->next) delete_a_window(walk);
  awins = NULL;
  if (pb.id!=-1) status = smg$delete_pasteboard(&pb.id, &flags);
}

/* ------------------------------------------------------------------------- */
/* This routine must never be called from ast level. It disables ast's       */
/* before calling a non-reetrant routine.                                    */
/* ------------------------------------------------------------------------- */
void putcursor(x)
  int x;
{
  unsigned long current;
  int status, old, row = 1;
  if (!inwin) return;
  old = sys$setast(0);
/*
  status = smg$find_cursor_display(&pb.id, &current, 0, 0);
  if (current != inwin) {
    if (gwin) status = smg$set_cursor_abs(&gwin->id, &row, &row);
*/
    status = smg$set_cursor_abs(&inwin, &row, &x);
/*  } */
  if (old == SS$_WASSET) (void)sys$setast(1);
}

/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* Following are the functions that are called when a user uses /window      */
/* ------------------------------------------------------------------------- */
void window_reorder(void)
{
  win_ptr top = NW, walk, f, b, temp;
  for (walk = awins; walk; walk = temp) {
    temp = walk->next;
    if (walk->hidden)
      for (b = NW, f = top; f ; b = f, f = f->next) ;
    else 
      for (b = NW, f = top; f && (f->p_y < walk->p_y); b = f, f = f->next) ;
    walk->next = f;
    if (b) b->next = walk;
    else top = walk;
  }
  awins = top;
}

/* ------------------------------------------------------------------------- */
static int w_s;
static int curr_scroll = 0;
void scroll_with_recall(int lines)
{
  char *str;
  unsigned long flag = SMG$M_UP, loop;
  if ((curr_scroll + lines) > 0) lines = -curr_scroll;
  if (lines < 0) { lines = -lines; flag = SMG$M_DOWN; }
  w_s = smg$scroll_display_area(&gwin->id, 0, 0, 0, 0, &flag, &lines);
  if (!(w_s & 1)) lib$signal(w_s);
  if (flag == SMG$M_DOWN) {
    for (loop=0;loop<lines;loop++)
    {
      str = get_prev_list(gwin->lastlog, TRUE, FALSE);
      if (str) write_raw_to_screen(lines - loop, 1, TRUE, str);
      else lines = loop - 1;
    }
    curr_scroll -= lines;
    gwin->line += lines;
  } else {
    curr_scroll += lines;
    gwin->line -= lines;
  }
}
/* ------------------------------------------------------------------------- */
void window_do_scroll(win_ptr win, int lines)
{
  unsigned long flag = SMG$M_UP;
  if (lines < 0) { lines = -lines; flag = SMG$M_DOWN; }
  win->line -= lines;
  w_s = smg$scroll_display_area(&win->id, 0, 0, 0, 0, &flag, &lines);
  if (!(w_s & 1)) lib$signal(w_s);
}
/* ------------------------------------------------------------------------- */
static void window_change_size(win_ptr win, int lines, int columns)
{
  win->m_line = lines;
  win->m_column = columns;
  w_s = smg$change_virtual_display(&win->id, &win->m_line,
                                   &win->m_column, 0, 0, 0);
  if (!(w_s & 1)) lib$signal(w_s);
}
/* ------------------------------------------------------------------------- */
static void window_move_window(win_ptr win, int x, int y)
{
  win->p_x = x;
  win->p_y = y;
  w_s = smg$move_virtual_display(&win->id, &pb.id, &win->p_y, &win->p_x, 0);
  if (!(w_s & 1)) lib$signal(w_s);
}
/* ------------------------------------------------------------------------- */
static void window_check_scroll(win_ptr win)
{
  int scroll;
/*  say("window check scroll %d %d", win->line, win->m_line); */
  scroll = win->line - win->m_line;
  if (scroll>0)
   {
     win->line = win->m_line;
     w_s = smg$scroll_display_area(&win->id, 0, 0, 0, 0, 0, &scroll);
     if (!(w_s & 1)) lib$signal(w_s);
     w_s = smg$set_display_scroll_region(&win->id, &start_row,
                                         &win->m_line);
     if (!(w_s & 1)) lib$signal(w_s);
   }
}
/* ------------------------------------------------------------------------- */
static void window_move_status(win_ptr win)
{
  int dummy;
  dummy = win->p_y + win->m_line - 1;
  w_s = smg$move_virtual_display(&win->status, &pb.id, &dummy, &win->p_x, 0);
  if (!(w_s & 1)) lib$signal(w_s);
}
/* ------------------------------------------------------------------------- */
static int off_counter = 0;

void update_off(void)
{
  if (off_counter++) return;
  smg$begin_pasteboard_update(&pb.id);
}
void update_on(void)
{
  off_counter--;
  if (off_counter>0) return;
  off_counter = 0;
  smg$end_pasteboard_update(&pb.id);
}

/* ------------------------------------------------------------------------- */
win_ptr window_exists(int num)
{
  win_ptr loop;
  for (loop=awins;loop;loop=loop->next)
    if (loop->num == num) return loop;
  return (win_ptr)NW;
}

/* ------------------------------------------------------------------------- */
void window_rawshow(win_ptr win, int x, int y)
{
  int loop, count;
  win->p_x = x;
  win->p_y = y;
  w_s = smg$paste_virtual_display(&win->id, &pb.id, &y, &x, 0);
  if (!(w_s & 1)) lib$signal(w_s);
  count = y + win->m_line - 1;
  win->hidden = FALSE;
  updatestatwin();
  w_s = smg$paste_virtual_display(&win->status, &pb.id, &count, &x, 0);
  if (!(w_s & 1)) lib$signal(w_s);
  win->activity = win->notified = 0;
}

/* ------------------------------------------------------------------------- */
static void window_rawhide(win_ptr win)
{
  w_s = smg$unpaste_virtual_display(&win->id, &pb.id);
  if (!(w_s & 1)) lib$signal(w_s);
  if (win->status) (void)smg$unpaste_virtual_display(&win->status, &pb.id);
  win->hidden = TRUE;
}

/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
int hidden(win_ptr win)
{
  return (win->hidden);
}

/* ------------------------------------------------------------------------- */
void window_setsize(win_ptr win, int x, int y)
{
  int status, scroll, dummy, col = 1;
  if (!is_window(win)) return;
  update_off();
  window_change_size(win, y, x);
  window_move_status(win);
  window_check_scroll(win);
  update_on();
}
/* ------------------------------------------------------------------------- */
int window_swap(win_ptr win)
{
  int old;
  win_ptr walk, prev, pg = NW, pw = NW, sn;
  if (!is_window(win)) return FALSE;
  if (!win->hidden) {
    if (gwin != win) back_win = gwin;
    gwin = win;
    return TRUE;
  }
  back_win = gwin;
  update_off();
  old = sys$setast(0);
  window_rawhide(gwin);
  window_setsize(win, gwin->m_column, gwin->m_line);
  window_rawshow(win, gwin->p_x, gwin->p_y);

  prev = NULL;
  walk = awins;
  for (walk = awins, prev = NULL; walk; prev = walk, walk = walk->next)
    if (walk==gwin) pg = prev;
    else if (walk==win) pw = prev;
  sn = gwin->next;
  gwin->next = win->next;
  if (win==sn) win->next = gwin;
  else {
    pw->next = gwin;
    win->next = sn;
  }
  if (is_window(pg)) pg->next = win;
  else awins = win;
  gwin = win;
  if (old==SS$_WASSET) sys$setast(1);
  update_on();
  return TRUE;
}

/* ------------------------------------------------------------------------- */
win_ptr window_num(ind)
  int ind;
{
  win_ptr walk = awins;
  int count = 1;
  if (ind==0) return 0;
  while ((walk) && (count<ind)) {
    count++;
    walk = walk->next;
  }
  if (walk && !walk->hidden) return walk;
  return NULL;
}

/* -------------------------------------------------------------------------
 * If change is < 0 then it means the change is to occur at the top of the
 * window. Else the change is to occur at the bottom of the window
 * ------------------------------------------------------------------------- */
void window_resize(win_ptr win, int change, int shrink, int top)
{
  int new_y;
  if (!(win && win->id)) return;

  update_off();
  win->m_line += shrink?-change:change;
  if (top)
    window_move_window(win, win->p_x, win->p_y + (shrink?change:-change));
  if (shrink) window_check_scroll(win);
  window_change_size(win, win->m_line, win->m_column);
  window_move_status(win);
  update_on();
}

/* Returns the next visible window. If there is no next window, then
 * we try to get the window before the window.
 * If we can't do either, we return NW
 */

win_ptr prev_visible(win_ptr win)
{
  win_ptr walk;
  if (!is_window(win)) return NW;
  for(walk=awins; walk && (walk->next != win); walk = walk->next)
    if (walk->hidden) return NW;
  return walk;
}
win_ptr next_visible(win_ptr win)
{
  if (!is_window(win)) return NW;
  if (win->next && !win->next->hidden) return win->next;
  return NW;
}

win_ptr previous_window(win_ptr win, int prev)
{
  int sl = -1, min;
  win_ptr save = NW, walk;
  if (!is_window(win)) return NW;
  min = win->p_y;
  for (walk = awins; walk; walk = walk->next)
  {
    if (( (prev && (walk->p_y > sl) && (walk->p_y < min)) ||
          (!prev && (walk->p_y > min) && ((sl==-1) || (walk->p_y < sl))) ) &&
        !walk->hidden)
    {
      sl = walk->p_y;
      save = walk;
    }
  }
  return save;
}

static int count_visible_windows(void)
{
  int count=0;
  win_ptr win;
  for (win=awins;win;win=win->next) if (!win->hidden) count++;
  return count;
}

int window_hide(win_ptr win)
{
  unsigned status;
  int count = 0, loop;
  win_ptr p, n, temp, walk;

  if (!(win && win->id)) {
    say("Hiding nothing?!");
    return FALSE;
  }
  for (walk=awins;walk;walk=walk->next)
    if (!walk->hidden) count++;
  if (count<=1) {
    say("*** You can not hide your last window.");
    return FALSE;
  }
  update_off();
  back_win = win;
  window_rawhide(win);
  temp = previous_window(win, FALSE);
  if (temp)
    window_resize(temp, win->m_line, FALSE, TRUE);
  else {
    temp = previous_window(win, TRUE);
    if (temp) window_resize(temp, win->m_line, FALSE, FALSE);
    else say("Oh no!!! No window... after hiding one");
  }
  for(p = NW, n = awins; n != win; p = n, n = n->next);
  if (!p) awins = win->next;
  else p->next = win->next;
  for(n = awins; n->next; n = n->next);
  n->next = win;
  win->next = NW;
  gwin = temp;
  updatestatwin();
  update_on();
  return TRUE;
}

/* ------------------------------------------------------------------------- */
win_ptr last_invisible(void)
{
  win_ptr walk;
  for (walk=awins;walk && walk->next; walk = walk->next);
  if (!walk || !walk->hidden) return NW;
  return walk;
}

/* ------------------------------------------------------------------------- */
int window_delete(win)
  win_ptr win;
{
  int count = 0, is_hidden;
  win_ptr walk, p, n, w, temp = NW;
  server_ptr serv;

  if (!win) return FALSE;
  serv = win->server;
  is_hidden = win->hidden;
  for (walk=awins;walk;walk=walk->next) if (!walk->hidden) count++;

  if ((count+is_hidden) < 2) {
    say("*** You can not delete your last visible window.");
    return FALSE;
  }
  update_off();
  if (gsrv) free_chan(gsrv->chan_list, win->channel);
  delete_a_window(win);
  if (!is_hidden)
  {
    temp = next_visible(win);
    if (temp)
      window_resize(temp, win->m_line, FALSE, TRUE);
    else {
      temp = prev_visible(win);
      if (temp) window_resize(temp, win->m_line, FALSE, FALSE);
    }
    outwin = gwin = temp;
  }
  for (p=NW,n=awins; n && n!=win; p=n,n=n->next) ;
  if (!n) yell("Really fucked up window list - deleteing noexistant node");
  else if (p) p->next = n->next;
       else awins = n->next;
  if (!is_window(gwin)) gwin = awins;
  update_on();
  if (!server_has_window(serv)) remove_server(serv, 1);
  return TRUE;
}

/* ------------------------------------------------------------------------- */
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
static void replace_new(char *str, char it, char *insert)
{
  while (*str)
  {
    if (*str != '%') str++;
    else
    {
      str++;
      if (*str != it) str++;
      else {
        char save[MAXLEN];
        strcpy(save, &str[1]);
        str--; /* go back to the % sign */
        strcpy(str, insert);
        while (*str) str++;
        strcpy(str, save);
        return; /* only replace the first occurance */
     }
    }
  }
}

/* Status hold_lines ------------------------------------------------------- */
static void sl_hold_lines(win_ptr win, char *str)
{
  char dummy[MAXLEN];
  *str = 0;
  if (win->hold_curr)
  {
    sprintf(dummy, "%d", win->hold_curr);
    strcpy(str, getvar(VAR_STATUS_HOLD_LINES));
    replace_new(str, 'B', dummy);
  }
}
/* ------------------------------------------------------------------------- */
static void sl_chanmode(win, str)
  win_ptr win;
  char *str;
{
  char mode[MAXLEN];
  (void)strcpy(str, getvar(VAR_STATUS_MODE));
  if (win->channel && win->server && win->server->connected) {
    recreate_chan_mode(win->server->chan_list, win->channel, mode);
    if (*mode) replace_new(str, '+', mode);
    else *str = 0;
  } else *str = 0;
}
/* ------------------------------------------------------------------------- */
static void sl_away(win, str)
  win_ptr win;
  char *str;
{
  if (win->server && win->server->connected && is_away(win->server)) 
  (void)strcpy(str, getvar(VAR_STATUS_AWAY));
  else str[0] = 0;
}
/* ------------------------------------------------------------------------- */
static void sl_activity(win, str)
  win_ptr win;
  char *str;
{
  win_ptr wins;
  int first = 1;
  char repl[255], *end;
  repl[0] = 0;
  for (wins=awins;wins;wins=wins->next)
    if (wins->activity) {
      end = &(repl[strlen(repl)]);
      sprintf(end, "%s%d", first?"":",", wins->num);
      first = 0;
    }
  if (!first) {
    strcpy(str, getvar(VAR_STATUS_ACTIVITY));
    replace_new(str, 'F', repl);
  } else str[0] = 0;
}
/* ------------------------------------------------------------------------- */
static void sl_insert(win, str)
  win_ptr win;
  char *str;
{
  char *insert;
  insert = getvar(VAR_STATUS_INSERT);
  if (getbvar(VAR_INSERT_MODE) && insert) strcpy(str, insert);
  else str[0] = 0;
}
/* ------------------------------------------------------------------------- */
static void sl_overwrite(win, str)
  win_ptr win;
  char *str;
{
  char *overwrite;
  overwrite = getvar(VAR_STATUS_OVERWRITE_MODE);
  if (!getbvar(VAR_INSERT_MODE) && overwrite) strcpy(str, overwrite);
  else str[0] = 0;
}
/* ------------------------------------------------------------------------- */
void sl_nick(win_ptr win, char *str)
{
  if (win->server && win->server->nick) (void)strcpy(str, win->server->nick);
  else (void)strcpy(str, "NONE");
}
/* ------------------------------------------------------------------------- */
extern char curr_time[30];
static void sl_time(win_ptr win, char *str)
{
  (void)strcpy(str, curr_time);
}
/* ------------------------------------------------------------------------- */
static void sl_user(win_ptr win, char *str)
{
   get_username(str, 0);
}
/* ------------------------------------------------------------------------- */
static void sl_channel(win_ptr win, char *str)
{
  char chan[MAXLEN];

  (void)strcpy(str, getvar(VAR_STATUS_CHANNEL));
  if (win->server && win->server->connected)
    if (!get_chan_name(win->server->chan_list, win->channel, chan)) str[0] = 0;
    else replace_new(str, 'C', chan);
  else str[0] = 0;
}
/* ------------------------------------------------------------------------- */
static void sl_query(win_ptr win, char *str)
{
  (void)strcpy(str, getvar(VAR_STATUS_QUERY));
  if (win->query)  replace_new(str, 'Q', win->query);
  else str[0] = 0;
}
/* ------------------------------------------------------------------------- */
static void sl_window(win_ptr win, char *str)
{
  if ((win==gwin) && (count_visible_windows() != 1))
    (void)strcpy(str, getvar(VAR_STATUS_WINDOW));
  else str[0] = 0;
}
/* ------------------------------------------------------------------------- */
void sl_window2(win_ptr win, char *str)
{
  if (win) sprintf(str, "%d", win->num);
  else strcpy(str, "-1");
}
/* ------------------------------------------------------------------------- */
void updatestatwin(void)
{
  long int srow = 1, scol = 1, erase = SMG$M_ERASE_LINE, line_advance = 0;
  long unsigned rendition_set = SMG$M_REVERSE | SMG$M_BOLD;
  long unsigned status, wrap = 0; /* Do not wrap */
  char str[MAXLEN], *temp, newfor[MAXLEN], *indx, *ts;
  int loop, loc, col, row = 1;
  win_ptr walk;
  struct dsc$descriptor_s str_d;

  str_d.dsc$b_dtype = DSC$K_DTYPE_T;
  str_d.dsc$b_class = DSC$K_CLASS_S;

  getx(&col);
  for (walk=awins; walk ; walk=walk->next)
  {
    if (!walk->hidden)
    {
      ts = getvar(VAR_STATUS_FORMAT);
      temp = str;
      while (*ts)
      {
        if (*ts != '%')
          *temp++ = *ts++;
        else
        {
          ts++;
          for (loop=0; status_list[loop].trigger[0]; loop++)
            if (status_list[loop].trigger[1] == *ts) break;
          if (status_list[loop].trigger[0])
          {
            (void)status_list[loop].replace(walk, temp);
            while (*temp) temp++;
          }
          ts++;
        }
      }
      *temp= 0;

      for (loop=temp-str;loop<walk->m_column; loop++)
        str[loop] = ' ';
      str[loop] = '\0';
      if (!(walk->status_line && (strcmp(str, walk->status_line) == 0))) {
        myfree(walk->status_line);
        walk->status_line = m_strcpy(str);
        str_d.dsc$w_length = strlen(str);
        str_d.dsc$a_pointer= str;
        status = smg$put_chars(&walk->status, &str_d, &srow, &scol,
                               &erase, &rendition_set, 0, 0);
      }
    }
    putcursor(col);
  }
}

/* ------------------------------------------------------------------------
 * Create a simple window (no status bar associated)
 * ------------------------------------------------------------------------ */
static unsigned simple_window(int lines, int columns, int x, int y, int flags)
{
  unsigned status, win, dummy = 0;
  status = smg$create_virtual_display(&lines, &columns, &win,
                                      &flags, &dummy, &dummy);
  if (ODD(status))
  {
    (void)smg$erase_display(&win);
    (void)smg$paste_virtual_display(&win, &pb.id, &y, &x, 0);
    return win;
  }
  my_sig(status, "Simple window");
  return 0;
}

/* ------------------------------------------------------------------------
 * Create a window and initialize the window statistics.
 * ------------------------------------------------------------------------ */
static win_ptr create_window(lines, columns, x, y, flags)
  int lines, columns, x, y, flags;
{
  unsigned status, id;
  int loop, lev = NONE, dummy = 0, col = 1, num = 1;
  win_ptr temp, f, p;

  if (!DUMB) {
    status = smg$create_virtual_display(&lines, &columns, &id,
                                        &flags, &dummy, &dummy);
    if (!ODD(status)) {
      say("Error creating virtual display (%d)", status);
      return NW;
    }
  } else id = 0;
  temp = (win_ptr)mymalloc(sizeof(struct awin));
  if (!temp) return NW;
  temp->id = id;
  temp->line = 1;
  while (window_exists(num)) num++;
  if (num>max_win_num) max_win_num = num;
  temp->num = num;
  temp->m_line = lines;
  temp->m_column = columns;
  temp->p_x = x;
  temp->p_y = y;
  temp->scroll = TRUE;
  temp->name[0] = 0;
  temp->query = NULL;
  temp->channel = 0;
  temp->hidden = FALSE;
  (void)strcpy(temp->logfile, getvar(VAR_LOGFILE));
  temp->file = NULL;
  for (f=awins; f; f = f->next)
    if (f->server == gsrv) lev |= f->wlevel;
/*  temp->wlevel = ALL - lev; */
  temp->wlevel = NONE;
  temp->activity = 0;
  temp->notified = 0;
  temp->notify_count = 0;
  temp->lastloglevel = getivar(VAR_LASTLOG_LEVEL);
  temp->notify_level = getivar(VAR_NOTIFY_LEVEL);
  temp->status_line = NULL;
  temp->lastlog = create_head(getivar(VAR_LASTLOG));
  temp->server = NULL;
  temp->hold_mode = getbvar(VAR_HOLD_MODE);
  temp->hold_mode_max = getivar(VAR_HOLD_MODE_MAX);
  temp->hold_curr = 0;
  temp->is_holding = FALSE;
  temp->hold_count = 0;
  temp->hold_lines = NULL;
  win_setmode(temp, win_getmode(gwin));

  for (f = awins, p = NW; f && (f->p_y < y); p = f, f = f->next);
  temp->next = f;
  if (is_window(p)) p->next = temp;
  else awins = temp;

  if (temp->id) (void)smg$erase_display(&temp->id);
  if (temp->id) (void)smg$set_display_scroll_region(&temp->id, &start_row, &y);
  if ((temp->id) && (pb.id!=-1))
    (void)smg$paste_virtual_display(&temp->id, &pb.id, &y, &x, 0);
  return temp;
}

/* ------------------------------------------------------------------------
 * Create an IRC window and a status bar
 * ------------------------------------------------------------------------ */
static win_ptr new_window(lines, columns, x, y)
  int lines, columns, x, y;
{
  win_ptr tempwin = NW;

  if (!DUMB || !gwin)
    tempwin = create_window(lines, columns, x, y, 0);
  if (!(DUMB && tempwin))
    tempwin->status = simple_window(1, columns, x, y+lines-1, 0);
  return tempwin;
}

/* -------------------------------------------------------------------------
 * Following is a list of functions for manipulation joining and leaving of
 * channels - these should be called only after the server has informed us
 * of a channel change
 * ------------------------------------------------------------------------- */
void set_channel(indx)
  int indx;
{
  if (!gsrv || !gsrv->connected) return;
  free_chan(gsrv->chan_list, gwin->channel);
  if (set_chan_used(gsrv->chan_list, indx, gwin) == TRUE)
    gwin->channel = indx;
}

/* ------------------------------------------------------------------------- */
void join_channel(server_ptr srv, char *chan, win_ptr towin)
{
  if (!(towin && srv && srv->connected)) return;
  free_chan(srv->chan_list, towin->channel);
  towin->channel = push_chan(&(srv->chan_list), chan, towin);
  updatestatwin();
}

/* ------------------------------------------------------------------------- */
win_ptr leave_channel(server, chan)
  server_ptr server;
  char *chan;
{
  win_ptr win;
  if (!server || !server->connected) return NULL;
  win = delete_channel(&server->chan_list, chan);
  if (win) {
    win->channel = find_chan_free(server->chan_list, win);
    updatestatwin();
  }
  return win;
}

/* ------------------------------------------------------------------------- */
char *get_channel(chan)
  char *chan;
{
  if (!(gwin && gwin->channel)) {
    if (chan) chan[0] = 0;
    return NULL;
  } else if (gwin->server && gwin->server->connected)
    return get_chan_name(gwin->server->chan_list, gwin->channel, chan);
  return NULL;
}

/* ------------------------------------------------------------------------- */
/* Query functions.                                                          */
/* ------------------------------------------------------------------------- */
win_ptr isquery(server_ptr srv, char *str)
{
  win_ptr win = NW, walk;
  for (walk=awins; walk; walk = walk->next)
    if ((!srv || (walk->server == srv)) && (walk->query) &&
        (strcasecmp(walk->query, str)==0))
      win = walk;
  return win;
}

int clearquery(query)
  char *query;
{
  int isq;

  if (!gwin) return FALSE;
  if (gwin->query) {
    (void)strcpy(query, gwin->query);
    free(gwin->query);
    gwin->query = NULL;
  }
  else *query = 0;
  isq = (query[0] != 0);
  return isq;
}

/* ------------------------------------------------------------------------- */
void setquery(query)
  char *query;
{
  if (gwin) {
    if (gwin->query) free(gwin->query);
    gwin->query = m_strcpy(query);
  }
}

/* ------------------------------------------------------------------------- */
char *getquery(void)
{
  if (!gwin) return NULL;
  return gwin->query;
}

/* ------------------------------------------------------------------------- */
void split_window(void)
{
  int half;
  win_ptr newwin;

  if (!gwin) return;
  half = (int)(gwin->m_line/2);
  if (half < 4)
  {
    say("Not enough lines left to split window.");
    return;
  }
  if (getbvar(VAR_NOVICE))
  {
    yell("*** You can not create a new window with NOVICE mode on.");
    return;
  }
  if ((newwin = new_window(half, pb.maxcols, gwin->p_x, gwin->p_y)) != NW)
  {
    window_resize(gwin, half, TRUE, TRUE);
    newwin->server = gwin->server;
    back_win = gwin;
    gwin = newwin;
    updatestatwin();
  }
}

/* ------------------------------------------------------------------------- */
void clear_window(void)
{
  unsigned status;
  gwin->line = 1;
  status = smg$erase_display(&gwin->id, &gwin->line, &gwin->line,
               &gwin->m_line, &gwin->m_column);
}

/* ------------------------------------------------------------------------- */
static void clear_all_window(void)
{
  win_ptr loop;  
  int start = 1;
  for (loop=awins;loop;loop=loop->next)
  {
    (void)smg$erase_display(&loop->id, &start, &start,
                 &loop->m_line, &loop->m_column);
    loop->line = 1;
  }
}

/* ------------------------------------------------------------------------- */
static void ast_rsend(void)
{
  unsigned status;
  char msg[200], send[200];
  unsigned short length, type=0;
  struct dsc$descriptor_s msg_d;

  msg_d.dsc$a_pointer = msg;
  msg_d.dsc$b_dtype = DSC$K_DTYPE_T;
  msg_d.dsc$b_class = DSC$K_CLASS_S;
  msg_d.dsc$w_length = sizeof(msg);

  status = smg$get_broadcast_message(&pb.id, &msg_d, &length, &type);
  msg[(int)length] = 0;
  (void)sprintf(send, "SEND: %s", msg);
  say("%s", send);
}

/* ------------------------------------------------------------------ */
void trap(int on)
{
  unsigned status;
  if (!on)
  {
    stop_input();
    status = smg$disable_broadcast_trapping(&pb.id);
    smg$erase_pasteboard(&pb.id);
  }
  else 
  {
    status = smg$set_broadcast_trapping(&pb.id, ast_rsend, 0);
    start_keybdio();
  }
  if (!(ODD(status)))
    say("Error in broadcast trapping: %d", status);
}

void user_spawn(char *rest, int varpar, char *subparams)
{
  unsigned status;
  trap(FALSE);
  set_pasthru(TRUE);
  status = lib$spawn();
  set_pasthru(FALSE);
  if (!(ODD(status))) say("Error in spawn: %d", status);
  trap(TRUE);
  redraw();
}

/* ------------------------------------------------------------------------- */
void init_window(void)
{
  int loop;
  unsigned status;
  unsigned long device_type;
  win_ptr temp;
  if (!DUMB)  {
    status = smg$create_pasteboard(&pb.id, 0, &pb.maxrows, &pb.maxcols, 0, 
                                   &device_type);
    if (!ODD(status)) (void)lib$signal(status);
    trap(TRUE);
    if (!ODD(status)) (void)lib$signal(status);
  } else {
    pb.maxrows = 23;
    pb.maxcols = 80;
    pb.id = -1;
  }
  if (INPUT_DEFINED) inwin = simple_window(1, pb.maxcols, 1, pb.maxrows, 0);
  else inwin = 0;
  gwin = new_window(pb.maxrows-1, pb.maxcols, 1, 1);
  set_outwin(gwin);
}

/* ------------------------------------------------------------------------- */
/* This is the start of the routines user for handling user input            */
/* ------------------------------------------------------------------------- */
static int echo = 1;
/* ------------------------------------------------------------------------- */
int set_echo(int on)
{
  int old;
  old = echo;
  if (on) echo = 1;
  else echo = 0;
  return old;
}

/* ------------------------------------------------------------------------- */
/* This routine must never be called from ast level. It disables ast's       */
/* before calling a non-reetrant routine.                                    */
/* ------------------------------------------------------------------------- */
int print_at(char *str, int len, int pl)
{
  long int line = 1, col = 1;
  long unsigned flag = SMG$M_ERASE_LINE;
  int status, loop;
  char s[MAXLEN], f[MAXLEN];
  struct dsc$descriptor_s for_d, mvw_d;

  if (len <= 0) return pb.maxcols;
  for_d.dsc$a_pointer = f;
  mvw_d.dsc$a_pointer = s;
  for_d.dsc$b_dtype = mvw_d.dsc$b_dtype = DSC$K_DTYPE_T;
  for_d.dsc$b_class = mvw_d.dsc$b_class = DSC$K_CLASS_S;

  for (loop=0;loop<len;loop++) {
    if (str[loop] < 32) {s[loop] = str[loop] + 64; f[loop] = SMG$M_REVERSE;}
    else {s[loop] = str[loop]; f[loop] = 0;}
  }
  if (!echo && (len>pl)) len = pl;
  for_d.dsc$w_length = len;
  mvw_d.dsc$w_length = len;
  status = smg$put_chars_multi(&inwin, &mvw_d, &line, &col, &flag,
                               &for_d, 0, 0);
  if (!ODD(status)) {
    say("PR len [%d]\n", len);
    my_sig(status, "MVADDSTR");
  }
  return pb.maxcols;
}

/* ------------------------------------------------------------------------- */
/* This routine must never be called from ast level. It disables ast's       */
/* before calling a non-reetrant routine.                                    */
/* ------------------------------------------------------------------------- */
void del_char_from_input(x, num)
  int x;
  int num;
{
  int status, old, col = 1;
  if (!echo) return;
  old = sys$setast(0);
  status = smg$delete_chars(&inwin, &num, &col, &x);
  if (old == SS$_WASSET) (void)sys$setast(1);
}

/* ------------------------------------------------------------------------- */
/* This routine must never be called from ast level. It disables ast's       */
/* before calling a non-reetrant routine.                                    */
/* ------------------------------------------------------------------------- */
static struct dsc$descriptor_s str_d = {1, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL};

void rep_char_in_input(key, x)
  char key;
  int x;
{
  int status, old, col = 1, dummy = 0;

  if (!echo) return;
  str_d.dsc$a_pointer = &key;
  old = sys$setast(0);
  status = smg$put_chars(&inwin, &str_d, &col, &x, &dummy, &dummy,
                         &dummy, &dummy);
  if (old == SS$_WASSET) (void)sys$setast(1);
}

/* ------------------------------------------------------------------------- */
void redraw(void)
{
  int x;
  getx(&x);
  (void)smg$repaint_screen(&pb.id);
  updatestatwin();
  putcursor(x);
}

/* ------------------------------------------------------------------------- */
/* This routine must never be called from ast level. It disables ast's       */
/* before calling a non-reetrant routine.                                    */
/* ------------------------------------------------------------------------- */
void insert_char(key, x)
  char key;
  int x;
{
  int status, old, col = 1;
  unsigned form = 0;

  if (!echo) return;
  if (key<32) {
    form = SMG$M_REVERSE;
    key += 64;
  }
  str_d.dsc$a_pointer = &key;
  old = sys$setast(0);
  status = smg$insert_chars(&inwin, &str_d, &col, &x, &form, 0, 0);
  if (old == SS$_WASSET) (void)sys$setast(1);
}

/* ------------------------------------------------------------------------- */
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* ------------------------------------------------------------------------- */
static char format[1024], string[1024];
static $DESCRIPTOR(for_d, format);
static $DESCRIPTOR(mvw_d, string);

/* ------------------------------------------------------------------------- */
/*
void put_line(win_ptr twin, char *str, int y, int x)
{
  int status, flag = SMG$M_ERASE_TO_EOL;
  mvw_d.dsc$a_pointer = str;
  for_d.dsc$a_pointer = att;
  for_d.dsc$w_length = mvw_d.dsc$w_length = len;
  status = smg$put_chars_multi(&twin->id, &mvw_d, &y, &x, &flag,
                               &for_d, 0, 0);
}
*/

/* ------------------------------------------------------------------------- */
static void display_line(win_ptr twin, char *str, char *att, int len)
{
  int status, col = 1, flag = SMG$M_ERASE_TO_EOL;
  mvw_d.dsc$a_pointer = str;
  for_d.dsc$a_pointer = att;
  for_d.dsc$w_length = mvw_d.dsc$w_length = len;
  status = smg$put_chars_multi(&twin->id, &mvw_d, &twin->line, &col, &flag,
                               &for_d, 0, 0);
  if (++twin->line > twin->m_line)
  {
    if (twin->scroll == TRUE)
      window_do_scroll(twin, 1);
    else
      twin->line = 1;
  }
  if (!ODD(status)) my_sig(status, "MVADDSTR");
}

/* ------------------------------------------------------------------------- */
void kill_hold(void)
{
  int old;
  hold_ptr temp, save;

  if (!gwin) return;
  old = sys$setast(0);
  temp = gwin->hold_lines;
  while (temp)
  {
    save = temp;
    temp = temp->next;
    myfree(save->str);
    myfree(save->att);
    myfree(save);
    gwin->hold_curr--;
  }
  gwin->hold_lines = NULL;
  if (old == SS$_WASSET) sys$setast(1);
}

/* ------------------------------------------------------------------------- */
/* Called when a user requests a flush                                       */
static void flush_hold(int lines)
{
  int loop, old;
  hold_ptr temp, save;
  if (!gwin) return;

  old = sys$setast(0);
  temp = gwin->hold_lines;
  for (loop=0; (loop!=lines) && temp; loop++) 
  {
    display_line(gwin, temp->str, temp->att, temp->len);
    save = temp;
    temp = temp->next;
    myfree(save->str);
    myfree(save->att);
    myfree(save);
    gwin->hold_curr--;
  }
  gwin->hold_lines = temp;
  if (old == SS$_WASSET) sys$setast(1);
}

/* ------------------------------------------------------------------------- */
static void add_to_hold_list(hold_ptr *list, char *str, char *att, int len)
{
  hold_ptr temp, walk, back;
  temp = (hold_ptr)mymalloc(sizeof(struct hold_list));
  if (!temp) return;
  temp->str = m_strcpy(str);
  temp->att = (char *)mymalloc(len);  /* can't use m_strcpy -- not a string */
  if (temp->att) memcpy(temp->att, att, len);
  temp->len = len;
  temp->next = NULL;
  if (!*list) *list = temp;
  else {
    walk = *list;
    while (walk->next) walk = walk->next;
    walk->next = temp;
  }
}

/* ------------------------------------------------------------------------- */
void free_hold(void)
{
  if (gwin) {
    if (gwin->just_held) gwin->just_held = 0;
    else {
      flush_hold(gwin->m_line-1); /* use -1 because of the statusbar */
      if (!gwin->hold_curr) gwin->is_holding = FALSE;
    }
    gwin->hold_count = 0;
  }
}

/* ------------------------------------------------------------------------- */
extern int in_help;
static void write_to_screen(win_ptr twin, char *str, char *att, int len)
{
  if ((twin->is_holding) && !twin->hidden && !in_help)
  {
    str[len] = att[len] = 0;
    add_to_hold_list(&twin->hold_lines, str, att, len);
    twin->hold_curr++;
    if ((twin->hold_curr > twin->hold_mode_max) &&
        twin->hold_mode_max) flush_hold(-1);
    return;
  }
  if (twin->hold_mode)
    if (++twin->hold_count >= (twin->m_line-1))
      twin->just_held = twin->is_holding = TRUE;
  display_line(twin, str, att, len);
  return;
}
/* ------------------------------------------------------------------------- */
#define SINGLE_CHAR(__ochar, __oform, __ichar, __att, __beeps, __adv) \
do { \
  __adv = FALSE; \
  if (__ichar <= 32) \
  { \
    switch (__ichar) { \
        case 2: \
          __att = (SMG$M_BOLD & ~__att) | (~SMG$M_BOLD & __att); \
          break; \
        case 15: \
          __att = 0; \
          break; \
        case 22: \
          __att = (SMG$M_REVERSE & ~__att) | (~SMG$M_REVERSE & __att); \
          break; \
        case 31: \
          __att = (SMG$M_UNDERLINE & ~__att) | (~SMG$M_UNDERLINE & __att); \
          break; \
        case 7: \
          if (!getivar(VAR_BEEP) || (__beeps++>=getivar(VAR_BEEP_MAX))) { \
            __oform = SMG$M_REVERSE; \
            __ochar = __ichar + 64; \
          } else { \
            __oform = __att; \
            __ochar = __ichar; \
          } \
          __adv = TRUE; \
          break; \
        case 9: \
          __oform = __att; \
          __ochar = __ichar; \
          __adv = TRUE; \
        break; \
        default: \
          if (__ichar < 0) __ichar += 128; \
          if (__ichar < 32) { \
            __oform = SMG$M_REVERSE; \
            __ochar = (char)((int)__ichar + 64); \
            __adv = TRUE; \
          } else if (__ichar < 128) { \
            __oform = __att; \
            __ochar = __ichar; \
            __adv = TRUE; \
          } else { /* __ichar >= 128 */ \
          } \
        break; \
    } /* end of switch */ \
  } else { \
    __oform = __att; \
    __ochar = __ichar; \
    __adv = TRUE; \
  } \
} while (0) \

/* ------------------------------------------------------------------------- */
void write_raw_to_screen(int row, int col, int clear, char *str)
{
  int status, flag = 0;
  int real, loop, att, beeps, adv;
  real = loop = att = beeps = 0;

  if (clear) flag |= SMG$M_ERASE_TO_EOL;

  while (str[loop] && (loop < sizeof(string)))
  {
    SINGLE_CHAR(string[real], format[real], str[loop], att, beeps, adv);
    if (adv) real++;
    loop++;
  }
  mvw_d.dsc$a_pointer = string;
  for_d.dsc$a_pointer = format;
  for_d.dsc$w_length = mvw_d.dsc$w_length = real;
  status = smg$put_chars_multi(&gwin->id, &mvw_d, &row, &col, &flag,
                               &for_d, 0, 0);
}

/* ------------------------------------------------------------------------- */
static void my_mvwaddstr(win_ptr win, char *str, int MAX, int indent_mode,
                         int multi_row)
{
  int current, att, beeps, space, indent, loop, space_att, real, last, lines;
  int adv;
  lines = last = loop = space_att = space = indent = real =
    att = beeps = current = 0;
  while (str[loop]) {
/*
    adv = single_char(&string[real], &format[real], str[loop], &att,
                      &beeps);
*/
    SINGLE_CHAR(string[real], format[real], str[loop], att, beeps, adv);
    if (string[real] == 32)
    {
      if ((indent == 0) || (indent == real)) indent = real + 1;
      space = real;
      space_att = format[real];
    }
    if (adv) real++;
    loop++;
    if (real >= MAX) { /* end of a line for the screen */
      int where;
      lines++;
      if (!indent || (indent >= (MAX/6))) indent = MAX/6;
      if (space && (space > indent)) where = space;
      else where = real;
      write_to_screen(win, string, format, where);
      if (!multi_row) return;
      if (space > indent) {
#if 0
        yell("indent %d space %d where %d real %d", indent, space, where,
             real);
#endif
        att = space_att;
        where = real-space-1;
        memcpy(&string[indent], &string[space+1], where);
        memcpy(&format[indent], &format[space+1], where);
        real = indent + where;
#if 0
        yell("indent %d space %d where %d real %d", indent, space, where,
             real);
#endif
      } else real = indent;
      memset(string, 32, indent);
      memset(format, 0, indent);
      space = 0;
      last = where;
    } /* If real >= MAX */
  } /* end of while */
#if 1
  if (!lines || ((last != loop) && (real != indent)))
    write_to_screen(win, string, format, real);
#else
  if (!lines || (last != loop)) write_to_screen(win, string, format, real);
#endif
#if 0
  if (lines && (last!=loop))
    yell("Wrap: indent %d real_len %d last %d loop %d", indent, real,
         last, loop);
#endif
}

/* ------------------------------------------------------------------------- */
/* This is the routine that is called when a message needs to be printed to  */
/* a particular window. It takes care of splitting the line up and scrolling */
/* ------------------------------------------------------------------------- */
static int in_redirect = FALSE;
static int inonw = FALSE;

static void print_window(win_ptr twin, char *msg, int type)
{
  int col2 = 1, x, status;

  if (!is_window(twin)) {
    (void)printf("%s\n", msg);
    return;
  }
  if (!in_redirect && twin->server && twin->server->redirect)
    new_send(twin->server, "PRIVMSG %s :%s\n", twin->server->redirect, msg+3);
  if (twin->file) (void)fprintf(twin->file, "%s\n", msg+3);

  if (type==(LASTLOG*2)) type = log_level;
  if ((twin->lastloglevel & type) == type)
    (void)add_list(twin->lastlog, msg+3, type);
  inonw++;
  if ((inonw<=2) && !handle_on(o_window, msg, 0))
  {
    getx(&x);
    my_mvwaddstr(twin, msg+3, twin->m_column, TRUE, TRUE);
    putcursor(x);
  }
  inonw--;
}

/* ------------------------------------------------------------------------ */
static void do_notify_window(win_ptr win)
{
  if (!win || !win->hidden) return;
  win->activity = 1;
  if ((win->notified < win->notify_count) &&
      (log_level & win->notify_level))
  {
    sprintf(string, "%2.2d *** Activity in window %d", gwin->num, win->num);
    win->notified++;
    print_window(gwin, string, LASTLOG*2);
  }

}

/* ------------------------------------------------------------------------ */
void yell(char *format, ...)
{
  char string[MAXLEN*100];
  int old;
  va_list arglist;
  va_start(arglist, format);
  old = sys$setast(0);
  sprintf(string, "%2.2d ", outwin?outwin->num:-1);
  (void)vsprintf(string+3, format, arglist);
  print_window(outwin, string, LASTLOG*2);
  do_notify_window(outwin);
  va_end(arglist);
  if (old == SS$_WASSET) (void)sys$setast(1);
}
/* ------------------------------------------------------------------------ */
void win_say(win_ptr win, char *format, ...)
{
  char string[MAXLEN*100];
  int old;
  va_list arglist;
  va_start(arglist, format);
  if (getivar(VAR_DISPLAY)<=0) return;
  old = sys$setast(0);
  sprintf(string, "%2.2d ", win?win->num:-1);
  (void)vsprintf(string+3, format, arglist);
  print_window(win, string, LASTLOG*2);
  do_notify_window(win);
  va_end(arglist);
  if (old == SS$_WASSET) (void)sys$setast(1);
}
/* ------------------------------------------------------------------------ */
void say(char *format, ...)
{
  char string[MAXLEN*100];
  int old;
  va_list arglist;
  va_start(arglist, format);
  if (getivar(VAR_DISPLAY)<=0) return;
  old = sys$setast(0);
  sprintf(string, "%2.2d ", outwin?outwin->num:-1);
  (void)vsprintf(string+3, format, arglist);
  print_window(outwin, string, LASTLOG*2);
  do_notify_window(outwin);
  va_end(arglist);
  if (old == SS$_WASSET) (void)sys$setast(1);
}
/* ------------------------------------------------------------------------ */
typedef struct {
  server_ptr ss;
  win_ptr ww;
  int ll;
} levels_save;

static stackptr levels_stack = NULL;

void unset_win_lvl_srv(void)
{
  levels_save *tmp;
  tmp = (levels_save *)pop_element(levels_stack, 0, NULL);
  if (!tmp) return;
  gsrv = tmp->ss;
  outwin = tmp->ww;
  log_level = tmp->ll;
}

void set_win_lvl_srv(server_ptr srv, int level)
{
  win_ptr walk, tmpwin = NULL, tmpwin2 = NULL;
  levels_save tmp;
  tmp.ss = gsrv;
  tmp.ww = outwin;
  tmp.ll = log_level;
  if (!levels_stack) levels_stack = make_new_stack(TRUE, 0);
  push_element(levels_stack, NULL, (char *)&tmp, sizeof(tmp), TRUE);
  for (walk=awins; walk && !tmpwin; walk = walk->next)
    if (walk->server == srv) 
      if (walk->wlevel & level) tmpwin = walk;
      else tmpwin2 = walk;
  if (!tmpwin) tmpwin = tmpwin2;
  gsrv = srv;
  outwin = tmpwin;
  log_level = level;
}

/* ------------------------------------------------------------------------ */
win_ptr set_outwin(win_ptr win)
{
  win_ptr temp;
  temp = outwin;
  outwin = win;
  return temp;
}

int set_loglevel(int new)
{
  int old = log_level;
  log_level = new;
  return old;
}
/* ------------------------------------------------------------------------ */
int get_menu_choice(int menu_count, char *menu_items[])
{
#if 1
	return;
#else
  unsigned winid, status;
  int lines, cols, col_width, orig_x, orig_y, loop;
  struct dsc$descriptor_a choice_list;
  char *fixed_choices;
  unsigned short menu_num;
  unsigned long kbid, max_width = 0, flag1, flag2;

  lines = pb.maxrows;
  if (lines > 10) lines = 10;
  if (menu_count <= lines) lines = menu_count, cols = 1;
  else cols = menu_count / lines + 1;
  for (loop=0; loop<menu_count; loop++)
  {
/*    yell("[%s]", menu_items[loop]); */
    if (strlen(menu_items[loop]) > max_width)
      max_width = strlen(menu_items[loop]);
  }
  col_width = (max_width + 2) * cols;
  orig_x = (col_width < pb.maxcols)?((pb.maxcols - col_width)/2):0;
  orig_y = (lines < pb.maxrows)?((pb.maxrows - lines)/2):0;
  winid = simple_window(lines, col_width, orig_x, orig_y, SMG$M_BORDER);
/*  yell("max_width is %d, choice count is %d", max_width, menu_count); */
  max_width++;
  fixed_choices = (char *)mymalloc(menu_count * max_width);
  if (!fixed_choices) { say("malloc failed for menu"); return -1; }
  memset(fixed_choices, ' ', menu_count * max_width);
  for (loop=0;loop<menu_count;loop++)
  {
#if 1
     sprintf(&fixed_choices[loop * max_width], "%s", menu_items[loop]);
#else
    memcpy(&fixed_choices[loop * max_width], menu_items[loop],
           strlen(menu_items[loop]));
    fixed_choices[loop * max_width + max_width - 1] = 0;
#endif
  }
  choice_list.dsc$w_length = max_width;
  choice_list.dsc$b_dtype = DSC$K_DTYPE_T;
  choice_list.dsc$b_class = DSC$K_CLASS_A;
  choice_list.dsc$a_pointer = fixed_choices;
  choice_list.dsc$b_dimct = 1;
  choice_list.dsc$l_arsize = menu_count * max_width;
  stop_input();
  flag1 = SMG$K_VERTICAL;
  flag2 = SMG$M_WRAP_MENU;
  status = smg$create_menu(&winid, &choice_list, &flag1,
                           &flag2, 0, 0, 0);
  if (!ODD(status)) my_sig(status, "create_menu");
  status = smg$create_virtual_keyboard(&kbid, 0, 0, 0, 0);
  if (!ODD(status)) my_sig(status, "create_keyboard");
  status = smg$select_from_menu(&kbid, &winid, &menu_num, 0, 0, 0, 0, 0, 0, 0, 0);
  if (!ODD(status)) my_sig(status, "select_menu");
  status = smg$delete_virtual_keyboard(&kbid);
  if (!ODD(status)) my_sig(status, "delete_keyboard");
  flag1 = SMG$M_ERASE_MENU;
  status = smg$delete_menu(&winid, &flag1);
  if (!ODD(status)) my_sig(status, "delete_menu");
  status = smg$unpaste_virtual_display(&winid, &pb.id);
  if (!ODD(status)) my_sig(status, "unpaste_display");
  status = smg$delete_virtual_display(&winid);
  if (!ODD(status)) my_sig(status, "delete_display");
  myfree(fixed_choices);
  start_keybdio();
  if (!ODD(status)) return -1;
  return menu_num - 1;
#endif
}
/* ------------------------------------------------------------------------ */
