/* -------------- window.c ----------------------- */
/* From DDJ, September 1988 */

#include <stdio.h>
#include <alloc.h>
#include <string.h>
#include <conio.h>
#include <mem.h>
#include <dos.h>
#include <stdlib.h>
#include "window.h"


/* ------------ window border characters --------- */
#define NW   '\332'
#define NE   '\277'
#define SE   '\331'
#define SW   '\300'
#define SIDE '\263'
#define LINE '\304'

int editing;
static union REGS rg;

/* -------------- window definition structure --------- */
struct wn wdo[MAX_WINDOWS];
int curr_wnd;                     /* current window */
struct wn wkw;                    /* a working window structure */

static void upline(void);
static void downline(void);
static void firstline(void);
static void lastline(void);
static void dline(int,int,int);

/* ---------------- establish a new window --------------- */
void establish_window(left,top,right,bottom,foreg,backg,save)
{
  if (curr_wnd < MAX_WINDOWS)
    {
     if (curr_wnd)
        wdo[curr_wnd-1] = wkw;
     setmem(&wkw, sizeof(wkw), 0);
     wkw.lf = left;
     wkw.tp = top;
     wkw.rt = right;
     wkw.bt = bottom;
     wkw.fg = foreg;
     wkw.bg = backg;
     wkw.wd = right+1-left;
     wkw.ht = bottom-top-1;
     if (save)
       {
        if ((wkw.wsave=malloc((wkw.ht+2)*wkw.wd*2)) == NULL)
           return;
        gettext(left,top,right,bottom,wkw.wsave);
       }
     wdo[curr_wnd++] = wkw;
     current_window();
     clear_window();
    }
}

/* -------- initialize the working window as current ------- */
void current_window()
{
  window(wkw.lf,wkw.tp,wkw.rt,wkw.bt);
  hidecursor();
  if (wkw.fg || wkw.bg)
    {
     textcolor(wkw.fg);
     textbackground(wkw.bg);
    }
}

/* -------------- set a window's title -------------------- */
void window_title(char *ttl)
{
  writeline((wkw.wd-strlen(ttl))/2, 1, ttl);
}

/* --------------- remove a window -------------------------- */
void delete_window()
{
  if (curr_wnd)
    {
     if (wkw.wsave)
       {
        puttext(wkw.lf,wkw.tp,wkw.rt,wkw.bt,wkw.wsave);
        free(wkw.wsave);
       }
     setmem(wdo+curr_wnd-1,sizeof(struct wn),0);
     --curr_wnd;
     if (curr_wnd)
       {
        wkw = wdo[curr_wnd-1];
        current_window();
       }
     }
}

/* --------- clear window area, display border -------------------- */
void clear_window()
{
  int height,width,y=1;
  char line1[81],line2[81];

  height = wkw.ht;
  width = wkw.wd;
  setmem(line1 + 1,width - 1,LINE);
  setmem(line2 + 1,width - 1,' ');
  *line1 = NW;
  line1[width-1] = NE;
  line1[width] = '\0';
  *line2 = SIDE;
  line2[width-1] = SIDE;
  line2[width] = '\0';
  line1[width] = line2[width] = '\0';
  writeline(1,y++,line1);
  while (height--)
     writeline(1,y++,line2);
  *line1 = SW;
  line1[width-1] = SE;
  writeline(1,y,line1);
}

/* ---------- scroll the window; d:  1 = up, 0 = dn ------------ */
void scroll_window(d)
{
  if (_video.snow == 0)
     movetext(wkw.lf+1,wkw.tp+1,wkw.rt-1,wkw.bt-2+d,wkw.lf+1,wkw.tp+2-d);
  else
    {
     rg.h.ah = d ? 6 : 7;
     rg.h.bh = _video.attribute;
     rg.h.cl = wkw.lf;
     rg.h.ch = wkw.tp;
     rg.h.dl = wkw.rt-2;
     rg.h.dh = wkw.bt-2;
     int86(16,&rg,&rg);
    }
}

/* ----------- display text in window -------------------- */
void text_window(char *txt[],int ln)
{
  int height = wkw.ht;

  wkw.wtext = txt;
  wkw.wtop = ln;
  wkw.wy = 1;
  while (height-- && txt[ln-1])
     dline(ln++,wkw.fg,wkw.bg);
  wkw.wlines = 0;
  while (*txt++)
     wkw.wlines++;
}


static int lineno;
/* ----------- page and scroll through window of text -------- */
int select_window(int ln,int foreg,int backg,int (*func)(int,int))
{
  int c=0;
  int frtn;
  int height,dln,ptop;

  if (ln > wkw.wtop + wkw.ht - 1 || ln < wkw.wtop)
     text_window(wkw.wtext,ln);
  else
     wkw.wy = ln - wkw.wtop + 1;
  while (TRUE)
    {
     lineno = wkw.wtop + wkw.wy -1;
     ptop = wkw.wtop;
     dline(lineno,foreg,backg);
     if (wkw.wx == 0)
        hidecursor();
     else gotoxy(wkw.wx,wkw.wy+1);
     c = get_key();
     if (c == '\r' || c == ESC)
        break;
     switch(c)
       {
        case CTRL_HOME:
          firstline();
          break;
        case CTRL_END:
          lastline();
          break;
        case PGUP:
          wkw.wtop -= wkw.ht;
          if (wkw.wtop < 1)
             wkw.wtop = 1;
          break;
        case PGDN:
          wkw.wtop +=wkw.ht;
          if (wkw.wtop > wkw.wlines - (wkw.ht-1))
            {
             wkw.wtop = wkw.wlines - (wkw.ht-1);
             if (wkw.wtop < 1)
                wkw.wtop = 1;
            }
          break;
        case Up:
          upline();
          break;
        case Dn:
          downline();
          break;
        default:
          if (!editing && wkw.wlines <= wkw.ht)
            {
             if (c == HOME)
               {
                firstline();
                break;
               }
             if (c == End)
               {
                lastline();
                break;
               }
            }
          if (func)
            {
             frtn
              = (*func)(c,lineno);
             if (frtn == ERROR)
                putch(BELL);
             else
             if (frtn)
               {
                wkw.wy = frtn;
                return frtn;
               }
             c = 0;
            }
         break;
       }
     switch(c)
       {
        case HOME:
        case CTRL_HOME:
        case End:
        case CTRL_END:
        case PGUP:
        case PGDN:
          if (wkw.wtop != ptop)
            {
             height = wkw.ht;
             dln = wkw.wtop;
             while (height-- && wkw.wtext[dln-1])
                dline(dln++,wkw.fg,wkw.bg);
             break;
            }
        default:
          dline(lineno,wkw.fg,wkw.bg);
          break;
       }
    }
  return c == ESC ? 0 : lineno;
}

/* ------------------- move up one line ------------------- */
static void upline()
{
  if (lineno > 1)
    {
     if (wkw.wy == 1)
       {
        if (wkw.wtop >1)
          {
           --wkw.wtop;
           scroll_window(0);
          }
       }
     else
        --wkw.wy;
    }
  else
  if (wkw.wlines <= wkw.ht)
        lastline();
}


/* ------------------- move down one line ----------------------- */
static void downline()
{
  if (lineno < wkw.wlines)
    {
     if (wkw.wy == wkw.ht)
       {
        scroll_window(1);
        wkw.wtop++;
       }
     else
        wkw.wy++;
    }
  else
  if (wkw.wlines <= wkw.ht)
     firstline();
}

/* --------------------- move to first line ------------------------- */
static void firstline()
{
  wkw.wtop = wkw.wy = 1;
}

/* ---------------------- move to last line -------------------------- */
static void lastline()
{
  wkw.wtop = wkw.wlines - (wkw.ht-1);
  if (wkw.wtop < 1)
     wkw.wtop = 1;
  wkw.wy = wkw.ht;
  if (wkw.wy > wkw.wlines)
     wkw.wy = wkw.wlines;
}

char spaces[80] =
"                                                                             ";

/* ------------- display a line of text, highlight or normal ----------- */
static void dline(ln,foreg,backg)
{
  if (foreg || backg)
    {
     textcolor(foreg);
     textbackground(backg);
     --ln;
     writeline(2,ln-wkw.wtop+3, *(wkw.wtext + ln));
     if (strlen(*(wkw.wtext + ln)) < wkw.wd-2)
        writeline(2+strlen(*(wkw.wtext + ln)), ln-wkw.wtop+3,spaces+79-wkw.wd+
                 + strlen(*(wkw.wtext + ln)) + 2);
    }
}

/* --------------- write a line of text to video window ------------------ */
void writeline(int x,int y,char *str)
{
  int c1[80], *cp = c1;
  while (*str)
     *cp++ = (*str++ & 255) | (_video.attribute << 8);
  __vram(__vptr(x+wkw.lf-1,y+wkw.tp-1),(int far *)c1,
         (unsigned)((int far *)cp-(int far *)c1));
}

/* ---------------- hide the cursor ----------------- */
void hidecursor()
{
  rg.h.ah = 2;
  rg.x.dx = 0x1900;
  rg.h.bh = 0;
  int86(0x10,&rg,&rg);
}

/* ----------------- set cursor type ------------------ */
void set_cursor_type(unsigned t)
{
  rg.x.ax = 0x0100;
  rg.x.bx = 0;
  rg.x.cx = t;
  int86(0x10,&rg,&rg);
}

/* ------------------ clear screen ----------------------- */
void clear_screen()
{
  window(1,1,80,25);
  gotoxy(1,1);
  rg.h.al = ' ';
  rg.h.ah = 9;
  rg.x.bx = 7;
  rg.x.cx = 2000;
  int86(0x10,&rg,&rg);
}

void (*helpfunc)(void);
int helpkey;

/* ------------------- read keyboard ----------------------- */
int get_key()
{
  int c;

  if ((c=getch()) == 0)
     c = getch() | 128;
  if (c == helpkey && helpfunc)
    {
     (*helpfunc)();
     c = get_key();
    }
    return c;
}

/* -------------------- write error message -------------------- */
void error_message(char *errmsg)
{
  int lf = (80-strlen(errmsg)+2)/2;
  int rt = lf + max(strlen(errmsg)+2,15);
  establish_window(lf,11,rt,14,ERRORFG,ERRORBG,TRUE);
  gotoxy(2,2);
  cputs(errmsg);
  gotoxy(2,3);
  cputs("(Press [Esc])");
  hidecursor();
  putch(BELL);
  while (get_key() != ESC)
     ;
  delete_window();
}

