#include <stdio.h>
#include <descrip.h>
#include <smgdef.h>
#include <ssdef.h>
#include <string.h>
#include <stdlib.h>
#include "global.h"
#include "list.h"
#include "constants.h"
#include "window.h"

void putcursor(), sl_away(), sl_window(), sl_channel(), sl_time(), sl_nick();
void sl_query(), redraw(), sl_chanmode(), sl_hold_lines();

extern int beep, lastlog_level, lastlog, log_level, display, is_window();
extern server_ptr gsrv;
extern win_ptr outwin, back_win;
extern char logfile[];

extern int smg$put_chars_multi(), smg$change_virtual_display(),
           smg$move_virtual_display(), smg$scroll_display_area(),
           smg$set_display_scroll_region(), smg$unpaste_virtual_display(),
           lib$signal(), smg$delete_virtual_display(),
           smg$delete_pasteboard(), smg$cursor_column(), smg$put_line(),
           smg$set_cursor_abs(), smg$create_virtual_display(),
           smg$erase_display(), smg$paste_virtual_display(),
           sys$setast(), smg$insert_chars(), smg$repaint_screen(),
           smg$put_chars(), smg$delete_chars(),
           smg$set_broadcast_trapping(), smg$create_pasteboard(),
           lib$spawn(), smg$disable_broadcast_trapping(),
           smg$get_broadcast_message(), smg$begin_pasteboard_update(),
           smg$end_pasteboard_update();

extern int send_msg();
extern void remove_server(), remove_window();

extern char status_window[], status_format[], status_channel[],
       status_mode[], status_query[], status_insert[], status_server[],
       status_overwrite[], status_away[], status_clock[], status_user[],
       status_oper[], status_mail[], curr_time[], status_hold_lines[];

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

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

win_ptr awins = NW;
extern win_ptr gwin;

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

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

/* -------------------------------------------------------------------------*/
extern void reset_outwin();

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 */
/*  "%I", NULL,         /* Insert mode */
/*  "%M", NULL,         /* May contain %M (number of mail) */
/*  "%O", NULL,         /* Overwrite mode */
/*  "%S", NULL,         /* May contain %S (servername) */
/*  "%@", NULL,         /* chanop */
/*  "%*", NULL,         /* Are you oper? */
/*  "%#", NULL,         /* May contain %# (user mode) */
/*  { {"%+"}, sl_chanmode} ,  /* May contation %+ */
/* -------------------------------------------------------------------------*/
struct replace status_list[] =
{
  { {"%A"}, sl_away} ,
  { {"%B"}, sl_hold_lines} ,  /* May contain %B */
  { {"%C"}, sl_channel} ,   /* May contain %C (channel) */
  { {"%N"}, sl_nick} ,
  { {"%Q"}, sl_query} ,     /* May contain %Q (query user) */
  { {"%T"}, sl_time} ,      /* May contain %T (time) */
  { {"%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;
  for (walk=awins; walk; walk = walk->next)
    if ((walk->server == server) && (walk->wlevel & type))
      return walk;
  return gwin;
}

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

/* -------------------------------------------------------------------------
 * Delete all of the windows and the pasteboard that we have created 
 * ------------------------------------------------------------------------- */
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;

  for (walk=awins; walk; walk = walk->next) delete_a_window(walk);
  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;
{
  int status, old, col = 1;
  if (!inwin) return;
  old = sys$setast(0);
  status = smg$set_cursor_abs(&inwin, &col, &x);
  if (old == SS$_WASSET) (void)sys$setast(1);
}

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

char format[1024], string[1024];
$DESCRIPTOR(for_d, format);
$DESCRIPTOR(mvw_d, string);

extern FILE *log;
int att = 0;

void my_mvwaddstr(win, line, col, str, len)
  int win, line, col;
  char *str;
  int len;
{
  int status, loop, real, flag = SMG$M_ERASE_TO_EOL;

  if (len!=0) str[len] = 0;
  for (loop=0, real = 0;loop<strlen(str);loop++) {
    switch (str[loop]) {
      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 (!beep) {
          format[real] = SMG$M_REVERSE;
          string[real++] = str[loop] + 64;
        } else {
          format[real] = att;
          string[real++] = str[loop];
        }
        break;
      case 9:
        format[real] = att;
        string[real++] = str[loop];
      break;
      default:
        if (str[loop]<0) str[loop] += 128;
        if (str[loop]<32) {
          format[real] = SMG$M_REVERSE;
          string[real++] = (char)((int)str[loop] + 64);
        } else if (str[loop] < 128) {
          format[real] = att;
          string[real++] = str[loop];
        }
        break;
    };
  }
  for_d.dsc$w_length = real;
  mvw_d.dsc$w_length = real;
  status = smg$put_chars_multi(&win, &mvw_d, &line, &col, &flag,
                               &for_d, 0, 0);
  if (!ODD(status)) {
    say("line [%d] col [%d] real [%d]\n", line, col, real);
    (void)fprintf(log, "[%s]\n", str);
    my_sig(status, "MVADDSTR");
  }
}

/* ------------------------------------------------------------------------- */
/* Following are the functions that are called when a user uses /window      */
/* ------------------------------------------------------------------------- */
int w_s;
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);
}
/* ------------------------------------------------------------------------- */
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);
}
/* ------------------------------------------------------------------------- */
void window_do_scroll(win_ptr win, int lines)
{
  win->line -= lines;
  w_s = smg$scroll_display_area(&win->id, 0, 0, 0, 0, 0, &lines);
  if (!(w_s & 1)) lib$signal(w_s);
}
/* ------------------------------------------------------------------------- */
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);
   }
}
/* ------------------------------------------------------------------------- */
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);
}
/* ------------------------------------------------------------------------- */
void update_off()
 { smg$begin_pasteboard_update(&pb.id); }
void update_on()
 { 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;
  w_s = smg$paste_virtual_display(&win->status, &pb.id, &count, &x, 0);
  if (!(w_s & 1)) lib$signal(w_s);
  win->hidden = FALSE;
}

/* ------------------------------------------------------------------------- */
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;
  window_change_size(win, y, x);
  window_move_status(win);
  window_check_scroll(win);
}
/* ------------------------------------------------------------------------- */
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;
  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);
  return TRUE;
}

/* ------------------------------------------------------------------------- */
int *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 (int *)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;

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

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

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;
  }
  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();
  say("*** Window (%d) hidden.", win->num);
  return TRUE;
}

/* ------------------------------------------------------------------------- */
win_ptr last_invisible()
{
  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 status, count = 0, loop, is_hidden;
  win_ptr walk, p, n, w, temp = NW;
  server_ptr serv;

  if (!win) return;
  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 window.");
    return FALSE;
  }
  free_chan(gsrv->chan_list, win->channel);
  delete_a_window(win);
  if (!is_hidden)
  {
    temp = previous_window(win, FALSE);
    if (temp)
      window_resize(win->next, win->m_line, FALSE, TRUE);
    else {
      temp = previous_window(win, TRUE);
      window_resize(temp, win->m_line, FALSE, FALSE);
    }
  }
  remove_window(win);
  if (gwin == win) gwin = temp;
  if (!is_window(gwin)) gwin = awins;
  for (walk=awins;walk;walk=walk->next)
    if (walk->server == serv) return TRUE;
  remove_server(serv);
  return TRUE;
}

/* ------------------------------------------------------------------------- */
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* Status hold_lines ------------------------------------------------------- */
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, status_hold_lines);
    replace(str, "%B", 2, dummy);
  }
}
/* ------------------------------------------------------------------------- */
void sl_chanmode(win, str)
  win_ptr win;
  char *str;
{
  char mode[MAXLEN];
  (void)strcpy(str, status_mode);
  if (win->channel && win->server) {
    recreate_chan_mode(win->server->chan_list, win->channel, mode);
    replace(str, "%+", 2, mode);
  } else str[0] = 0;
}
/* ------------------------------------------------------------------------- */
void sl_away(win, str)
  win_ptr win;
  char *str;
{
  if (win->server && win->server->away) (void)strcpy(str, status_away);
  else str[0] = 0;
}
/* ------------------------------------------------------------------------- */
void sl_nick(win_ptr win, char *str)
{
  if (win->server) (void)strcpy(str, win->server->nick);
  else (void)strcpy(str, "NONE");
}
/* ------------------------------------------------------------------------- */
void sl_time(win_ptr win, char *str)
{
  (void)strcpy(str, curr_time);
}
/* ------------------------------------------------------------------------- */
void sl_channel(win_ptr win, char *str)
{
  char chan[MAXLEN];

  (void)strcpy(str, status_channel);
  if (win->server)
    if (!get_chan_name(win->server->chan_list, win->channel, chan)) str[0] = 0;
    else replace(str, "%C", 2, chan);
  else str[0] = 0;
}
/* ------------------------------------------------------------------------- */
void sl_query(win_ptr win, char *str)
{
  (void)strcpy(str, status_query);
  if (*win->query)  replace(str, "%Q", 2, win->query);
  else str[0] = 0;
}
/* ------------------------------------------------------------------------- */
void sl_window(win_ptr win, char *str)
{
  if ((win==gwin) && (win->next || (win!=awins)))
    (void)strcpy(str, status_window);
  else str[0] = 0;
}
/* ------------------------------------------------------------------------- */
/* DOUGH */

void updatestatwin(void)
{
  unsigned status;
  char str[MAXLEN], *temp, newfor[MAXLEN], *indx;
  int loop, loc, x, flag1 = SMG$M_REVERSE, flag2 = SMG$M_WRAP_CHAR;
  int flag3 = SMG$M_UP, col = 1, dummy = 0;
  win_ptr walk;
  struct dsc$descriptor_s str_d;

  str_d.dsc$a_pointer = str;
  str_d.dsc$b_dtype = DSC$K_DTYPE_T;
  str_d.dsc$b_class = DSC$K_CLASS_S;
  str_d.dsc$w_length = sizeof(str);

  getx(&x);
  for (walk=awins; is_window(walk) ; walk=walk->next)
  {
    (void)strcpy(str, status_format);
    temp = str;
    for (loop=0; status_list[loop].trigger[0] ;loop++)
    {
      indx = (char *)strstr(temp, status_list[loop].trigger);
      if ((indx) && status_list[loop].replace)
      {
        (void)status_list[loop].replace(walk, newfor);
        replace(str, status_list[loop].trigger, 2, newfor);
      }
    }
    for (loop=strlen(temp);loop<walk->m_column; loop++)
      temp[loop] = ' ';
    temp[loop-1] = '\0';
    if (!(walk->status_line && (strcmp(temp, walk->status_line) == 0))) {
      free(walk->status_line);
      walk->status_line = ms_strcpy(temp);
      str_d.dsc$w_length = strlen(temp);
      str_d.dsc$a_pointer= temp;
      status = smg$put_line(&walk->status, &str_d, &dummy, &flag1,
                            &dummy, &flag2, &dummy, &flag3);
    }
  }
  status = smg$set_cursor_abs(&inwin, &col, &x);
}

/* ------------------------------------------------------------------------
 * Create a simple window (no status bar associated)
 * ------------------------------------------------------------------------ */

unsigned simple_window(lines, columns, x, y)
  int lines, columns, x, y;
{
  unsigned status, win, dummy = 0;
  status = smg$create_virtual_display(&lines, &columns, &win,
                                      &dummy, &dummy, &dummy);
  if (!ODD(status))
  {
    my_sig(status, "Simple window");
    return 0;
  } else {
    (void)smg$erase_display(&win);
    (void)smg$paste_virtual_display(&win, &pb.id, &y, &x, 0);
    return win;
  }
  return 0;
}

/* ------------------------------------------------------------------------
 * Create a window and initialize the window statistics.
 * ------------------------------------------------------------------------ */

win_ptr create_window(lines, columns, x, y)
  int lines, columns, x, y;
{
  unsigned status, id;
  int loop, lev = NONE, dummy = 0, col = 1;
  win_ptr temp, f, p;

  if (!DUMB) {
    status = smg$create_virtual_display(&lines, &columns, &id,
                                        &dummy, &dummy, &dummy);
    if (!ODD(status)) {
      say("Error creating virtual display (%d)", status);
      return NW;
    }
  } else id = 0;
  temp = (win_ptr)malloc(sizeof(struct awin));
  if (!temp) return NW;
  temp->id = id;
  temp->line = 1;
  temp->num = max_win_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[0] = 0;
  temp->channel = 0;
  temp->hidden = FALSE;
  (void)strcpy(temp->logfile, logfile);
  temp->file = NULL;
  for (f=awins; f; f = f->next)
    if (f->server == gsrv) lev |= f->wlevel;
  temp->wlevel = ALL - lev;
  temp->lastloglevel = lastlog_level;
  temp->status_line = NULL;
  (void)init_head(&temp->lastlog);
  (void)set_list_limit(&temp->lastlog, lastlog);
  temp->server = NULL;
  temp->hold_mode = get_boolvariable("HOLD_MODE");
  temp->hold_mode_max = get_intvariable("HOLD_MODE_MAX");
  temp->hold_curr = 0;
  temp->is_holding = FALSE;
  temp->hold_count = 0;
  temp->hold_lines = NULL;

  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
 * ------------------------------------------------------------------------ */

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);
  if (!(DUMB && tempwin))
    tempwin->status = simple_window(1, columns, x, y+lines-1);
  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;
{
  free_chan(gsrv->chan_list, gwin->channel);
  if (set_chan_used(gsrv->chan_list, indx, gwin) == TRUE)
    gwin->channel = indx;
}

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

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

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

extern win_ptr delete_channel();
extern int find_chan_free();

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

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

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

/* ------------------------------------------------------------------------- */
/* Query functions.                                                          */
/* ------------------------------------------------------------------------- */

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

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

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

void setquery(query)
  char *query;
{
  if (gwin) (void)strcpy(gwin->query, query);
}

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

int getquery(query)
  char *query;
{
  if (!gwin) *query = 0;
  else (void)strcpy(query, gwin->query);
  return (query[0]!=(char)NULL);
}

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

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

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

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

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

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

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

extern void set_pasthru(int reset);

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

void user_spawn(void)
{
  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);
    status = smg$set_broadcast_trapping(&pb.id, ast_rsend, 0);
    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);
  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            */
/* ------------------------------------------------------------------------- */
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)
{
  int status, loop, line = 1, flag =SMG$M_ERASE_TO_EOL, col = 1;
  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);
/*    fprintf(log, "[%s]\n", str); /**/
    my_sig(status, "MVADDSTR");
  }
  putcursor(len);
  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.                                    */
/* ------------------------------------------------------------------------- */

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

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

int first_line(start, before, ret)
  char **start, *ret;
  int before;
{
  int count = 0, going = 0, curr = 0, max_space = 0, lspace = 0;
  char *msg, *ll;
  msg = *start;
  ll = ret;
  if (!*msg) {
    *ret = 0;
    *start = ret;
    return 0;
  }
  while ((*msg) && (curr<before))
  {
    if (*msg==' ') {
      if (!lspace) going = 1;
      max_space = curr;
    } else {
      if (going==1) {
        lspace = curr;
        going = -1;
      }
    }
    curr++;
    *ret++ = *msg++;
  }
  if (!*msg) max_space = curr;
  if (!max_space) max_space = before - 1;
  if ((!lspace) || (lspace>(int)(before/3))) lspace = (int)(before/3);
  else lspace++;

  ll[max_space] = 0;
  if (start[0][max_space]==' ') max_space++;
  *start = &(start[0][max_space]);
  return lspace;
}

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

void findlastspace(msg, before, ret)
  char **msg;
  int before;
  char *ret;
{
  int last = 0;
  int count = 0, space = 0;
  char *start, *ll;

  start = *msg;
  ll = ret;
  while ((*start) && (count<before))
  {
    if (*start==' ') space=count;
    count++;
    *ret++ = *start++;
  }
  if (count>=before)
  {
    if (space == 0) space = before - 1;
    else space++;
    ll[space] = 0;
    *msg = &(msg[0][space]);
  } else {
   *ret = 0;
   *msg = start;
  }
}

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

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

/* ------------------------------------------------------------------------- */
void flush_hold(int lines)
{
  int loop, old;
  hold_ptr temp, save;
  if (!gwin) return;

  old = sys$setast(0);
  update_off();
  temp = gwin->hold_lines;
  for (loop=0; (loop!=lines) && temp; loop++) 
  {
    put_line(gwin, temp->scol, temp->str);
    save = temp;
    temp = temp->next;
    free(save);
    gwin->hold_curr--;
  }
  gwin->hold_lines = temp;
  update_on();
  if (old == SS$_WASSET) sys$setast(1);
}

/* ------------------------------------------------------------------------- */
void add_to_hold_list(hold_ptr *list, int startcol, char *str)
{
  hold_ptr temp, walk, back;
  temp = (hold_ptr)malloc(sizeof(struct hold_list));
  if (!temp) return;
  temp->str = ms_strcpy(str);
  strcpy(temp->str, str);
  temp->scol = startcol;
  temp->next = NULL;
  walk = *list;
  if (!walk) *list = temp;
  else
  {
    while (walk->next) walk = walk->next;
    walk->next = temp;
  }
}

/* ------------------------------------------------------------------------- */
/* This routine prints a single line of text to the screen and scrolls if it */
/* is needed. Do not pass carriage returns to this routine.                  */
/* ------------------------------------------------------------------------- */

$DESCRIPTOR(print_d, NULL);

void put_line(twin, startcol, string)
  win_ptr twin;
  int startcol;
  char *string;
{
  int num = 1, status;
  my_mvwaddstr(twin->id, twin->line, startcol, string, strlen(string));
  if (++twin->line > twin->m_line)
  {
    if (twin->scroll == TRUE)
      window_do_scroll(twin, 1);
    else
      twin->line = 1;
  }
}

/* ------------------------------------------------------------------------- */
void free_hold()
{
  win_ptr walk;
  if (gwin) flush_hold(gwin->m_line);
  if (!gwin->hold_curr) gwin->is_holding = FALSE;
  gwin->hold_count = 0;
}

/* ------------------------------------------------------------------------- */
void print_window_mess(twin, startcol, string)
  win_ptr twin;
  int startcol;
  char *string;
{
  if ((twin->is_holding) && !twin->hidden)
  {
    add_to_hold_list(&twin->hold_lines, startcol, string);
    twin->hold_curr++;
    if ((twin->hold_curr > twin->hold_mode_max) &&
        twin->hold_mode_max) flush_hold(-1);
    return;
  }
  twin->hold_count++;
  if ((twin->hold_count >= (twin->m_line-1)) && twin->hold_mode)
    twin->is_holding = TRUE;
  put_line(twin, startcol, string);
}

/* ------------------------------------------------------------------------- */
/* 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 */
/* ------------------------------------------------------------------------- */
int in_redirect = FALSE;

int print_window(win_ptr twin, char *msg, int type)
{
  unsigned status;
  int x, loop, first;
  int col, col2 = 1, old_in_redirect;
  char *tstart, tline[MAXLEN];

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

  if (type==(LASTLOG*2)) type = log_level;
  if ((twin->lastloglevel & type) == type)
    (void)add_list(&(twin->lastlog), msg, type);
  getx(&x);
  att = 0;
  tstart = msg;
  col = first_line(&tstart, twin->m_column, tline);
  print_window_mess(twin, 1, tline);
  while (*tstart) {
    findlastspace(&tstart, twin->m_column-col, tline);
    print_window_mess(twin, col, tline);
  }
  status = smg$set_cursor_abs(&inwin, &col2, &x);
  return TRUE;
}

/* ------------------------------------------------------------------------ */
void say(char *format, char *p1, char *p2, char *p3, char *p4, char *p5,
         char *p6, char *p7, char *p8, char *p9, char *p10)
{
  char string[MAXLEN*100];
  int old;
  if (display<=0) return;
  old = sys$setast(0);
  (void)sprintf(string, format, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
  (void)print_window(outwin, string, LASTLOG*2);
  if (old == SS$_WASSET) (void)sys$setast(1);
}
/* ------------------------------------------------------------------------ */
void yell(char *format, char *p1, char *p2, char *p3, char *p4, char *p5,
          char *p6, char *p7, char *p8, char *p9, char *p10)
{
  char string[MAXLEN*100];
  int old;
  old = sys$setast(0);
  (void)sprintf(string, format, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
  (void)print_window(outwin, string, LASTLOG*2);
  if (old == SS$_WASSET) (void)sys$setast(1);
}

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