/*
 * The functions in this file negotiate with the operating system for
 * characters, and write characters in a barely buffered fashion on the display
 * All operating systems.
 */
#include <exec/types.h>
#include <exec/exec.h>
#include <exec/execbase.h>
#include <devices/console.h>
#include <graphics/text.h>
#include <graphics/gfxmacros.h>
#include <graphics/gfxbase.h>
#include <hardware/custom.h>
#include <hardware/dmabits.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <libraries/dosextens.h>
#include <utility/tagitem.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/exec_pragmas.h>
#include <clib/dos_pragmas.h>
#include <clib/graphics_pragmas.h>
#include <clib/intuition_pragmas.h>
#include <stdio.h>
#include "keymap.h"
#include "ed.h"

struct MsgPort    *CreatePort();
struct IOStdReq   *CreateStdIO();
long              CurrentDir();

/*** CLOSE FLAGS FOR VARIOUS THINGS ***/

#define CONSOLE     0x00000001
#define SCREEN      0x00000002
#define WIND        0x00000004
#define SIGNAL      0x00000008
#define MENU        0x00000010
#define ICONBASE    0x00000020
#define MSGPORT     0x00000040
#define ASLBASE     0x00000080


extern short    FONT_Y;
extern short    interlace;
extern struct   Custom __far custom;
extern void     redo_buffer_menu();
extern struct   Screen *stdscreen;
extern struct   Library *SysBase;

#define INTUITION_REV 33

char *VERSION = "$VER: m2emacs 4.4, 2000-10-21 \r\n";
char *title1  = "m2emacs";
char *title2  = "Amiga Modula-2 Editor";
#ifdef DEUTSCH
char *version = ", 4.4d, 2000-10-21";
char *usage1  = "Aufruf: ";
char *usage2  = " {+-bcdilr -gDir -pProject -tTabwidth} [File]";
#define NOMEM "Nicht genügend Speicher!"
#define NOSCREEN "Kann Screen nicht öffnen!"
#else
char *version = ", 4.4e, 2000-10-21";
char *usage1  = "Usage: ";
char *usage2  = " { [+-bcdilr gDir pProject tNumber] } file";
#define NOMEM "Insufficient Memory!"
#define NOSCREEN "Cannot open Screen"
#endif


static void Put_Data(int, char *, int);
static void vtmove(int, int);
static void vtputc(char);

#define NCOL 256
static int ncol;

char screenTitle[NCOL];

TERM term = {
  0,
  0
};

static struct TextAttr NewFont = {
   "topaz.font",
   TOPAZ_EIGHTY,
   0,0
};

/*
 * Diese Struktur wird nur noch für 1.3
 * verwendet.
 */
static struct NewScreen NewScreen = {
   0, 0, 640, -1, 2,
   0, 1,
   HIRES,
   CUSTOMSCREEN,
   &NewFont,
   screenTitle,
   NULL,
   NULL
};

static struct NewWindow NewWindow = {
   0, 12,
   0, 0,
   0, 1,
   0,
   SMART_REFRESH | ACTIVATE | BACKDROP | BORDERLESS,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   100, 35,
   800, 512,
   CUSTOMSCREEN
};

static long            mask = 0;
static int             taskPri;
struct GfxBase         *GfxBase = NULL;
struct Library         *IntuitionBase = NULL,
                       *IconBase = NULL,
                       *AslBase = NULL;
static struct Screen   *Screen;
       struct Window   *Window = NULL;
static struct Window   *oldWindow;
static struct IOStdReq *consoleIO;
static struct MsgPort  *consoleMsgPort;
static struct KeyMap   keyMap;
static int             curX,curY,cursorDrawn;

/*
 * This function fills in the default entries of the keymap
 */
static void ChangeHiMap()
{
  typedef UBYTE (*ByteArrayPtr)[];
  typedef char  *(*StringArrayPtr)[];

  register int                  mask, i, j;
  register ByteArrayPtr         origTypes, origRep, origCap;
  register StringArrayPtr       origMap;

  origTypes = (ByteArrayPtr) keyMap.km_HiKeyMapTypes;
  origMap = (StringArrayPtr) keyMap.km_HiKeyMap;
  origCap = (ByteArrayPtr) keyMap.km_HiCapsable;
  origRep = (ByteArrayPtr) keyMap.km_HiRepeatable;

  for (i = 0; i < 40; ++i) {
    if (HiKeyMapTypes[i] == KC_DEFAULT) {
      HiKeyMapTypes[i] = (*origTypes)[i];
      HiKeyMap[i] = (*origMap)[i];
      HiRepeatable[j = i >> 3] &= ~(mask = 1 << (i & 7));
      HiRepeatable[j] |= (*origRep)[j] & mask;
      HiCapsable[j] &= ~mask;
      HiCapsable[j] |= (*origCap)[j] & mask;
    }
  }

  keyMap.km_HiKeyMapTypes = (UBYTE*) &HiKeyMapTypes;
  keyMap.km_HiKeyMap = (ULONG*) &HiKeyMap;
  keyMap.km_HiCapsable = (UBYTE*) &HiCapsable;
  keyMap.km_HiRepeatable = (UBYTE*) &HiRepeatable;
}

/*
 * This function gets called just before we go back
 * home to the command interpreter.
 */
static ttclose2()
{
  struct Process *task;

  task = (struct Process *)FindTask(NULL);

  if (mask & MENU) {
#ifdef DEBUG
  printf("    mouse_clear_menu();\n"); Delay(25L);
#endif
    mouse_clear_menu();
  }
  if (mask & CONSOLE) {
#ifdef DEBUG
  printf("    CloseDevice(consoleIO);\n"); Delay(25L);
#endif
    CloseDevice(consoleIO);
  }
  if (mask & SIGNAL) {
#ifdef DEBUG
  printf("    DeleteStdIO(consoleIO);\n"); Delay(25L);
#endif
    DeleteStdIO(consoleIO);
  }
  if (mask & MSGPORT) {
#ifdef DEBUG
  printf("    DeletePort(consoleMsgPort);\n"); Delay(25L);
#endif
    DeletePort(consoleMsgPort);
  }
  if (mask & WIND) {
    task->pr_WindowPtr = (APTR)oldWindow;
#ifdef DEBUG
  printf("    CloseWindow(Window);\n"); Delay(25L);
#endif
    CloseWindow(Window);
    Window = NULL;
  }
  if (mask & SCREEN) {
#ifdef DEBUG
  printf("    CloseScreen(Screen);\n"); Delay(25L);
#endif
    if (Screen->FirstWindow != NULL) {
      SetWindowTitles(
        Screen->FirstWindow,
        (char *)-1L,
#ifdef DEUTSCH
        "M2Emacs:    Bitte alle Fenster auf diesem Screen schliessen ..."
#else
       "M2Emacs:    Please close all windows on this screen ..."
#endif
      );
      ActivateWindow(Screen->FirstWindow);
    }
    while (Screen->FirstWindow != NULL) {
      Delay(100);
    }
    CloseScreen(Screen);
  }
  return TRUE;
}


static ttclose()
{
  struct Process *task;

  task = (struct Process *)FindTask(NULL);

  if (doCD) {
#ifdef DEBUG
  printf("CurrentDir()\n");
  Delay(25);
#endif
    origDir = CurrentDir(origDir);
    UnLock(origDir);
  }

  if (mask & ASLBASE) {
#ifdef DEBUG
  printf("    CloseLibrary(AslBase);\n"); Delay(25L);
#endif
    CloseLibrary(AslBase);
  }
  if (mask & ICONBASE) {
#ifdef DEBUG
  printf("    CloseLibrary(IconBase);\n"); Delay(25L);
#endif
    CloseLibrary(IconBase);
  }
#ifdef DEBUG
  printf("  CloseLibrary(IntuitionBase);\n"); Delay(25L);
#endif
  CloseLibrary(IntuitionBase);
#ifdef DEBUG
  printf("  CloseLibrary((struct Library *)GfxBase);\n"); Delay(25L);
#endif
  CloseLibrary((struct Library *)GfxBase);
#ifdef DEBUG
  printf("  SetTaskPri((struct Task *)task, taskPri);\n"); Delay(25L);
#endif
  SetTaskPri((struct Task *)task, taskPri);

#ifdef DEBUG
  printf("  return (TRUE);\n"); Delay(25L);
#endif
  return (TRUE);
}

/*
 * This function is called once to set up the
 * terminal device streams.
 */
static ttopen()
{
  struct Process *task;

  strcpy(screenTitle, title2);
  strcat(screenTitle, version);

#ifdef DEBUG
  printf("  GfxBase = (struct GfxBase *)OpenLibrary(\"graphics.library\",INTUITION_REV);\n"); Delay(25L);
#endif
  GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",INTUITION_REV);

#ifdef DEBUG
  printf("  IconBase = OpenLibrary(\"icon.library\",INTUITION_REV);\n"); Delay(25L);
#endif
  IconBase = OpenLibrary("icon.library",INTUITION_REV);
  if (IconBase)
   mask |= ICONBASE;

#ifdef DEBUG
  printf("  AslBase = OpenLibrary(\"asl.library\",36);\n"); Delay(25L);
#endif
  AslBase = OpenLibrary("asl.library",36);
  if (AslBase)
    mask |= ASLBASE;

 /* raise priority a little as we are interactive */
  task = (struct Process *)FindTask(NULL);
  taskPri = SetTaskPri((struct Task *)task, 1);

  return TRUE;
}

static int tag_pens = -1;
static struct TagItem tags[] = {
  { SA_DisplayID, 0           },
  { SA_Pens,      (int)&tag_pens   },
  { SA_Depth,     2           },
  { SA_Font,      (int)&NewFont    },
  { SA_Title,     (int)&screenTitle},
  { TAG_END,      0        },
};

static ttopen2()
{
  struct Process *task;

#ifdef DEBUG
  printf("  Screen = OpenScreen[TagList](&NewScreen [, tags]);\n"); Delay(25L);
#endif
  if (IntuitionBase->lib_Version < 36) {
   /*
    * Soll es Interlace sein?
    */
    if (interlace) {
      NewScreen.ViewModes |= LACE;
      NewScreen.Depth = 2;
    }
    else
      NewScreen.ViewModes &= ~LACE;

    Screen = OpenScreen(&NewScreen);
  }
  else {
    ULONG wbKey, mId;

    Screen = LockPubScreen("Workbench");
    if (Screen) {
      wbKey = (ULONG)GetVPModeID(&(Screen->ViewPort));
      UnlockPubScreen(NULL, Screen);
    }
    else {
      wbKey = HIRES_KEY;
    }
    mId = wbKey & MONITOR_ID_MASK;
    if (mId >= A2024_MONITOR_ID) /* do_nothing() */;
    else if (mId >= VGA_MONITOR_ID) {
      if (interlace)
        wbKey |= 1;
      else
        wbKey &= ~1;
    }
    else {
      if (interlace)
        wbKey |= 4;
      else
        wbKey &= ~4;
    }
    tags[0].ti_Data = wbKey;
    Screen = OpenScreenTagList(NULL, tags);
  }
  if (Screen == NULL) {
    Error(NOSCREEN,FALSE);
    ttclose2();
    ttclose();
    exit(3);
  }

  mask |= SCREEN;
  stdscreen = Screen;

  FONT_Y = Screen->Font->ta_YSize;
/* !!!!!
 * 20-7-92/bp FontInfo aus window.rPort .txWidth .txHeight .txBaseline
 * holen!! Ebenso die Startpos des Windows im Screen.
 * Menu via NewMenu generieren!
 */
  NewWindow.Height = Screen->Height - 11;
  NewWindow.Width = Screen->Width;
  NewWindow.Screen = Screen;
  NewWindow.TopEdge = 11;

  term.t_ncol = ncol = (Screen->Width / 8) ;
  term.t_nrow = ( NewWindow.Height / FONT_Y ) - 1;
#ifdef DEBUG
  printf("Screen Size: %d cols %d rows ", term.t_ncol, term.t_nrow); Delay(25L);
#endif

#ifdef DEBUG
  printf("  Window = OpenWindow(&NewWindow);\n"); Delay(25L);
#endif
  Window = OpenWindow(&NewWindow);
  if (Window == NULL) {
    Error(NOMEM, FALSE);
    ttclose2();
    ttclose();
    exit(3);
  }
  task = (struct Process *)FindTask(NULL);
  oldWindow = (struct Window *)task->pr_WindowPtr;
  task->pr_WindowPtr = (APTR)Window;

  mask |= WIND;

  SetAPen(Window->RPort,1);

#ifdef DEBUG
  printf("  consoleMsgPort = CreatePort(NULL,0);\n"); Delay(25L);
#endif
  consoleMsgPort = CreatePort(NULL,0);
  if (consoleMsgPort == NULL) {
    Error(NOMEM, FALSE);
    ttclose2();
    ttclose();
    exit(3);
  }
  mask |= MSGPORT;

#ifdef DEBUG
  printf("  consoleIO = CreateStdIO(consoleMsgPort);\n"); Delay(25L);
#endif
  consoleIO = CreateStdIO(consoleMsgPort);
  if (consoleIO == NULL) {
    Error(NOMEM, FALSE);
    ttclose2();
    ttclose();
    exit(3);
  }

  mask |= SIGNAL;

  consoleIO->io_Data = (APTR)Window;

#ifdef DEBUG
  printf("  if (OpenDevice(\"console.device\", 0, consoleIO, 0) != 0) {\n"); Delay(25L);
#endif
  if (OpenDevice("console.device", 0, consoleIO, 0) != 0) {
    Error(NOMEM, FALSE);
    ttclose2();
    ttclose();
    exit(3);
  }
  mask |= CONSOLE;

  consoleIO->io_Command = CD_ASKKEYMAP;
  consoleIO->io_Data = (APTR)&keyMap;
  consoleIO->io_Length = sizeof(struct KeyMap);
#ifdef DEBUG
  printf("  DoIO(consoleIO);\n"); Delay(25L);
#endif
  DoIO(consoleIO);

#ifdef DEBUG
  printf("  ChangeHiMap();\n"); Delay(25L);
#endif
  ChangeHiMap();

  consoleIO->io_Command = CD_SETKEYMAP;
  consoleIO->io_Data = (APTR)&keyMap;
  consoleIO->io_Length = sizeof(struct KeyMap);
  DoIO(consoleIO);

  SetMacroTitle(FALSE);
  mouse_setup_menu();
  mask |= MENU;

 /* Enable mouse events from Console.device */
  consoleIO->io_Command = CMD_WRITE;
  consoleIO->io_Data = (APTR)"[2;10{";
  consoleIO->io_Length = 7;
  DoIO(consoleIO);

#ifdef DEBUG
  printf("  return(TRUE);\n"); Delay(25L);
#endif
  return(TRUE);
}

/*
 * Read a character from the terminal, performing no editing and doing no echo
 * at all. More complex in VMS that almost anyplace else, which figures. Very
 * simple on CPM, because the system can do exactly what you want.
 */
unsigned char GetChar()
{
   unsigned char ch='\0';

   consoleIO->io_Command = CMD_READ;
   consoleIO->io_Data = (APTR)&ch;
   consoleIO->io_Length = 1L;
   DoIO(consoleIO);

   return(ch);
}

void screenback()                               /* APS 04-nov-86        */
{
   struct Process *task;

   task = (struct Process *)FindTask(NULL);

   task->pr_WindowPtr = (APTR)oldWindow;
   ScreenToBack(Screen);
}

void screenfront()                              /* APS 04-nov-86        */
{
   struct Process *task;

   task = (struct Process *)FindTask(NULL);

   task->pr_WindowPtr = (APTR)Window;
   ScreenToFront(Screen);
}

SetMacroTitle(on)
{
  if (on)
#ifdef DEUTSCH
    SetWindowTitles(Window,(char *)-1L,"m2emacs makro definition");
#else
    SetWindowTitles(Window,(char *)-1L,"m2emacs macro definition");
#endif
  else
    SetWindowTitles(Window,(char *)-1L,screenTitle);
  return(TRUE);
}

static void Put_Data(row, buf, inverse)
int row;
char *buf;
int inverse;
{
  register struct RastPort *rp;

  rp = Window->RPort;

  Move(rp, 0, row*FONT_Y+6);
  if (inverse) {
    SetAPen(rp,3);
    SetBPen(rp,2);
  }
  Text(rp, buf, (long)ncol);
  SetAPen(rp,1);
  SetBPen(rp,0);
}

void ClearWindow()
{
  SetRast(Window->RPort, 0L);
}

static void ToggleCursor()
{
  register  struct RastPort *rastPort;

  rastPort = Window->RPort;

  SetAPen(rastPort,3);
  SetDrMd(rastPort,2);
  Move(rastPort, curX, curY);
  RectFill(rastPort, curX, curY-FONT_Y+1, curX+7, curY);
  SetAPen(rastPort,1);
  SetDrMd(rastPort,1);
}

void EraseCursor()
{
  if (cursorDrawn) {
    ToggleCursor();
  }
  cursorDrawn = FALSE;
}

void SetCursor(row,col)
int row,col;
{
  if (cursorDrawn) {
    ToggleCursor();
  }
  curX=col*8; curY=row*FONT_Y+7;

  cursorDrawn = TRUE;
  ToggleCursor();
}

void ScrollScreen(d, top, bot)
int d, bot, top;
{
  ScrollRaster(
    Window->RPort,
    0,
    (d == 1) ? FONT_Y : -FONT_Y,
    0,
    top*FONT_Y,
    Screen->Width,
    bot*FONT_Y-1);
}


/*
 * ------------------ altes display.c folgt -------------------------------
 *
 * The functions in this file handle redisplay. There are two halves, the
 * ones that update the virtual display screen, and the ones that make the
 * physical display screen the same as the virtual display screen. These
 * functions use hints that are left in the windows by the commands.
 *
 * REVISION HISTORY:
 *
 * ?    Steve Wilhite, 1-Dec-85
 *      - massive cleanup on code.
 *
 * 7.7.89/ms another massive cleanup on code.
 */

int sgarbf  = TRUE;    /* TRUE if screen is garbage */
int mpresf  = FALSE;   /* TRUE if message in last line */
int vtrow   = 0;       /* Row location of SW cursor */
int vtcol   = 0;       /* Column location of SW cursor */

char vscreen[NCOL];    /* Actual Screen line. */

/*
 * Initialize the data structures used by the display code. The edge vectors
 * used to access the screens are set up. The operating system's terminal I/O
 * channel is set up. All the other things get initialized at compile time.
 * The original window has "WFCHG" set, so that it will get completely
 * redrawn on the first call to "update".
 */
vtinit()
{
  cursorDrawn=FALSE;
  ttopen();
  ttopen2();
  return(TRUE);
}

/*
 * Clean up the virtual terminal system, in anticipation for a return to the
 * operating system. Move down to the last line and clear it out (the next
 * system prompt will be written in the line). Shut down the channel to the
 * terminal.
 */
vttidy()
{
  ttclose2();
  ttclose();
  return(TRUE);
}

vtrethink()
{
  WINDOW  *w;
  short    top;

  Forbid();
  OFF_DISPLAY;
  ttclose2();
  ttopen2();
  redo_buffer_menu();
  ON_DISPLAY;
  Permit();
 /*
  * Adjust windows to the new screen size
  */
  w = wheadp;
  top = 0;
  while (w->w_wndp != NULL) {
    w->w_toprow = top;

    if (interlace) {
      w->w_ntrows = (w->w_ntrows * 2);
    }
    else {
      w->w_ntrows = (w->w_ntrows / 2);
    }
    top = top + (w->w_ntrows + 1);

    w = w->w_wndp;
  }
  w->w_toprow = top;
  w->w_ntrows = term.t_nrow - top - 1;

  sgarbf = TRUE;
  return(TRUE);
}

/*
 * Set the virtual cursor to the specified row and column on the virtual
 * screen. There is no checking for nonsense values; this might be a good
 * idea during the early stages.
 */
static void vtmove(row, col)
int row;
int col;
{
  vtrow = row;
  vtcol = col;
}

/*
 * Write a character to the virtual screen. The virtual row and column are
 * updated. If the line is too long put a "$" in the last column. This routine
 * only puts printing characters into the virtual terminal buffers. Only
 * column overflow is checked.
 *
 * M2Emacs no longer prints a '$' in the last column if it is longer than
 * one screen line
 */
static void vtputc(c)
char c;
{
  if (vtcol < ncol) {
    if (c == '\t') {
      if (displaytab)
        vtputc('\273');
      else
        vtputc(' ');
      while ((vtcol < ncol) && ((vtcol & 0x07) != 0))
        vtputc(' ');
    }
    else if ((c & 0x7F) < 0x20 || c == 0x7F) {
      if (vtcol < ncol)
        vscreen[vtcol++] = '^';
      vtputc((char)(c ^ 0x40));
    }
    else
      vscreen[vtcol++] = c;
  }
}

/*
 * Erase from the end of the software cursor to the end of the line on which
 * the software cursor is located.
 */
static vteeol(inverse)
int inverse;
{
  register char *cp;
  register int i;

  i = vtcol;
  cp = &(vscreen[i]);

  while (i++ < ncol)
    *cp++ = ' ';

  Put_Data(vtrow,vscreen,inverse);
  return(TRUE);
}

static vtline(i, lp, leftOff)
int    i;
LINE  *lp;
short  leftOff;
{
  int j;
  int col;
  int c;

#ifdef DEBUG
  printf("\nvtline %2d\n", i);
#endif
  vtmove(i,0);

/* run through the line to find the starting position */

  col = 0; j = 0;
  while ( (j < llength(lp)) && (col < leftOff)) {
    c = lgetc(lp, j++);

    if (c == '\t')
      col |= 0x07;
    else if ((c & 0x7F) < 0x20 || c == 0x7F)
      ++col;

    ++col;
#ifdef DEBUG
  printf("c=%3d while ( (j=%3d < ll=%d) && (col=%3d < lO=%d))\n",
          c, j,llength(lp),col,leftOff);
#endif
  }

#ifdef DEBUG
  printf("display from pos %d up to %d ", j, llength(lp));
#endif

  while (j < llength(lp)) {
    vtputc((char)lgetc(lp,j++));
#ifdef DEBUG
  printf("%d ", j);
#endif
  }
#ifdef DEBUG
  printf(" -- through\n");
#endif

  vteeol(FALSE);
#ifdef DEBUG
  printf("vtline --- done\n\n");
#endif
  return 0;
}

/*
 * Redisplay the mode line for the window pointed to by the "wp". This is the
 * only routine that has any idea of how the modeline is formatted. You can
 * change the modeline format by hacking at this routine. Called by "update"
 * any time there is a dirty window.
 */
static modeline(wp)
WINDOW *wp;
{
  register char *cp;
  register char c;
  register BUFFER *bp;

  vtmove(wp->w_toprow+wp->w_ntrows, 0);   /* Seek to right line. */

/*
 * Is the leftmost character visible? If not a '<' marks that.
 */
  if (wp->w_leftOff == 0)
    vtputc('-');
  else
    vtputc('<');

  bp = wp->w_bufp;

  if ((bp->b_flag&BFCHG) != 0)            /* "*" if changed. */
    vtputc('*');
  else
    vtputc('-');

  cp = " m2emacs -- ";                     /* Buffer name. */

  while ((c = *cp++) != 0) {
    vtputc(c);
  }

  cp = &bp->b_bname[0];

  while ((c = *cp++) != 0) {
    vtputc(c);
  }

  vtputc(' ');

  if (bp->b_fname[0] != 0) {          /* File name. */
#ifdef DEUTSCH
    cp = "-- Datei: ";
#else
    cp = "-- File: ";
#endif

    while ((c = *cp++) != 0) {
      vtputc(c);
    }

    cp = &bp->b_fname[0];

    while ((c = *cp++) != 0) {
      vtputc(c);
    }

    vtputc(' ');
  }

  while (vtcol < term.t_ncol) {  /* Pad to full width. */
    vtputc('-');
  }
  vteeol(TRUE);                  /* Erzwinge Ausgabe der Zeile */
  return(TRUE);
}

/*
 * Make sure that the display is right. This is a three part process. First,
 * scan through all of the windows looking for dirty ones. Check the framing,
 * and refresh the screen. Second, make sure that "currow" and "curcol" are
 * correct for the current window. Third, make the virtual and physical
 * screens the same.
 */
update()
{
  register LINE *lp;
  register WINDOW *wp;
  register int i;
  register int c;
  int scroll;
  UBYTE mask;

#define NONE    0
#define FORWARD 1
#define BACK    2

  scroll = NONE;

#ifdef UPDATE
  printf("update 1\n"); Delay(5);
#endif

  EraseCursor();
#ifdef UPDATE
  printf("update 2\n"); Delay(5);
#endif

 /* Special hacking if the screen is garbage. Clear the hardware screen,
  * and update your copy to agree with it. Set all the virtual screen
  * change bits, to force a full update.
  */
  if (sgarbf != FALSE) {
    ClearWindow();
    mpresf = FALSE;                 /* the message area. */
  }
#ifdef UPDATE
  printf("update 3a\n"); Delay(5);
#endif

/***********************************************************************/


  curcol = 0;
  i = 0;

  lp = curwp->w_dotp;
  while (i < curwp->w_doto) {
    c = lgetc(lp, i++);

    if (c == '\t')
      curcol |= 0x07;
    else if ((c & 0x7F) < 0x20 || c == 0x7F)
      ++curcol;

    ++curcol;
  }
#ifdef UPDATE
  printf("update 3b\n"); Delay(5);
#endif

  while (curcol >= (curwp->w_leftOff+term.t_ncol-3)) {      /* Long line. */
    curwp->w_leftOff += 8;
    curwp->w_flag |= WFHARD|WFMODE;
  }
#ifdef UPDATE
  printf("update 3c\n"); Delay(5);
#endif
  while ((curwp->w_leftOff > 0) && (curcol < (curwp->w_leftOff+60))) {
    curwp->w_leftOff -= 8;
    curwp->w_flag |= WFHARD|WFMODE;
  }

/***********************************************************************/

  wp = wheadp;

  while (wp != NULL) { /* Look at any window with update flags set on. */
    if (sgarbf)
      wp->w_flag |= WFHARD|WFMODE;

#ifdef UPDATE
  printf("update 4\n"); Delay(5);
#endif
    if (wp->w_flag != 0) {

      mask = Window->RPort->Mask;
      Window->RPort->Mask = 1;

     /*
      * Falls eine Neupositionierung nicht verlangt wird, überprüfen
      * wir die Position der Cursorzeile.
      * Innerhalb eines Macros darf dies auch nicht geschehen.
      */
      if ((wp->w_flag & WFFORCE) == 0) {
        lp = wp->w_linep;

        if (    wp->w_dotp == lback(wp->w_linep)
             && wp->w_dotp != wp->w_bufp->b_linep  )
          scroll = BACK;
        else {
          for (i = 0; i < wp->w_ntrows; ++i) {
            if (lp == wp->w_dotp)              /* Cursorzeile gefunden */
              goto out;

            if (lp == wp->w_bufp->b_linep)     /* Ende des Textes erreicht */
              break;
            lp = lforw(lp);
          }

          if (wp->w_dotp == lp)
            scroll = FORWARD;
        }
      }

      if (scroll != NONE) {
        if (scroll == BACK)
          wp->w_linep = lback(wp->w_linep);
        else
          wp->w_linep = lforw(wp->w_linep);

        if ((wp->w_flag & WFHARD) == 0) {
#ifdef UPDATE
  printf("update 5\n"); Delay(5);
#endif
          ScrollScreen(
            (scroll == BACK) ? -1 : 1,
            wp->w_toprow,
            wp->w_toprow+wp->w_ntrows
          );

          wp->w_flag = (wp->w_flag & WFMODE) | WFEDIT;
        }
        goto out;
      }
     /* Not acceptable, better compute a new value for the line at the
      * top of the window. Then set the "WFHARD" flag to force full
      * redraw.
      */
      i = wp->w_force;

      if (i > 0) {
        --i;
        if (i >= wp->w_ntrows)
          i = wp->w_ntrows-1;
      }
      else if (i < 0) {
        i += wp->w_ntrows;
        if (i < 0)
          i = 0;
      }
      else
        i = wp->w_ntrows/2;

      lp = wp->w_dotp;

      while (i != 0 && lback(lp) != wp->w_bufp->b_linep) {
        --i;
        lp = lback(lp);
      }

      wp->w_linep = lp;
      wp->w_flag |= WFHARD;       /* Force full. */

out:
     /* Try to use reduced update. Mode line update has its own special
      * flag. The fast update is used if the only thing to do is within
      * the line editing.
      */
      lp = wp->w_linep;
      i = wp->w_toprow;

#ifdef UPDATE
  printf("update 6\n"); Delay(5);
#endif
      if ((wp->w_flag & ~WFMODE) == WFEDIT) {
        while (lp != wp->w_dotp) {
          ++i;
          lp = lforw(lp);
        }
        vtline(i, lp, wp->w_leftOff);
      }
      else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0) {
        while (i < wp->w_toprow+wp->w_ntrows) {

          if (lp != wp->w_bufp->b_linep) {
            vtline(i,lp, wp->w_leftOff);
            lp = lforw(lp);
          }
          else {
            vtmove(i,0);
            vteeol(FALSE);
          }

          ++i;
        }
      }

      Window->RPort->Mask = mask;

#ifdef UPDATE
  printf("update 7\n"); Delay(5);
#endif
      if ((wp->w_flag & WFMODE) != 0)
        modeline(wp);

      wp->w_flag  = 0;
      wp->w_force = 0;
    }
    wp = wp->w_wndp;
  }

 /* Always recompute the row and column number of the hardware cursor. This
  * is the only update for simple moves. Has been done for curcol
  */
  lp = curwp->w_linep;
  currow = curwp->w_toprow;

  while (lp != curwp->w_dotp) {
    ++currow;
    lp = lforw(lp);
  }

  sgarbf = FALSE;

#ifdef UPDATE
  printf("update 8\n"); Delay(5);
#endif
  SetCursor(currow, curcol-curwp->w_leftOff);

#ifdef UPDATE
  printf("update 9\n"); Delay(5);
#endif
  return(TRUE);
}

static unsigned char msgLine[NCOL];
static int  msgIdx;

static void PutMsgLine(c)
unsigned char c;
{
  if (msgIdx<ncol)
    msgLine[msgIdx++]=c;
}

static void PrintMsgLine()
{
  int i;

  for (i=msgIdx; i<ncol; i++)
    msgLine[i]=' ';

  Put_Data(term.t_nrow,msgLine,FALSE);
}

/*
 * Erase the message line. This is a special routine because the message line
 * is not considered to be part of the virtual screen. It always works
 * immediately; the terminal buffer is flushed via a call to the flusher.
 */
mlerase()
{
    msgIdx=0;
    PrintMsgLine();

    mpresf = FALSE;
    return(TRUE);
}

/*
 * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
 * ABORT. The ABORT status is returned if the user bumps out of the question
 * with a ^G. Used any time a confirmation is required.
 */
mlyesno(prompt)
char *prompt;
{
  register int s;
  char promptBuf[64],buf[8];

  buf[0]='\0';

  for (;;) {
    strncpy(promptBuf, prompt, sizeof(promptBuf)-10);
#ifdef DEUTSCH
    strcat(promptBuf, " [j/n]? ");
#else
    strcat(promptBuf, " [y/n]? ");
#endif
    s = mlreply(promptBuf, buf, sizeof(buf));

    if (s == ABORT)
      return (ABORT);

    if (s != FALSE) {
#ifdef DEUTSCH
      if (buf[0]=='j' || buf[0]=='J')
#else
      if (buf[0]=='y' || buf[0]=='Y')
#endif
        return (TRUE);

      if (buf[0]=='n' || buf[0]=='N')
        return (FALSE);
    }
  }
  return(FALSE);
}

/*
 * Write a prompt into the message line, then read back a response. Keep
 * track of the physical position of the cursor. If we are in a keyboard
 * macro throw the prompt away, and return the remembered response. This
 * lets macros run at full speed. The reply is always terminated by a carriage
 * return. Handle erase, kill, and abort keys.
 */
mlreply(prompt, buf, nbuf)
unsigned char *prompt;
unsigned char *buf;
{
  register int cpos;
  int cursor,curpos,firstkey;
  register unsigned int c;

  for (cpos=strlen(buf); cpos<nbuf; cpos++)
    buf[cpos]=0;

  cpos = 0; firstkey=TRUE;

  if (kbdmop != NULL) {
    while ((c = *kbdmop++) != '\0')
      buf[cpos++] = c;

    buf[cpos] = 0;

    if (buf[0] == 0) {
      mlerase();
      return (FALSE);
    }

    return (TRUE);
  }

  mlwrite(prompt);
  cursor=msgIdx;

  cpos=strlen(buf);

  for (;;) {
    {
      register int i;

      msgIdx = cursor;
      curpos=0;

      for (i=0; buf[i]; i++) {
        c=buf[i];
        if (c<' ') {
          if (i<cpos) curpos++;
          PutMsgLine('^');
          c |= 0x40;
        }
        if (i<cpos) curpos++;

        PutMsgLine(c);
      }
    }
    PrintMsgLine();

    SetCursor(term.t_nrow,cursor+curpos);
    c = (unsigned int)GetChar() & 0xFF;
    EraseCursor();

    switch (c) {
    case 0x0D:                  /* Return, end of line */
      if (kbdmip != NULL) {
        register int i;

        if (kbdmip+strlen(buf) > &kbdm[NKBDM-3]) {
          ctrlg(FALSE, 0);
          return (ABORT);
        }

        for (i=0; i<strlen(buf); ++i)
          *kbdmip++ = buf[i];

        *kbdmip++ = '\0';
      }

      if (buf[0]==0) {
        mlerase();
        return(FALSE);
      }

      return (TRUE);

    case 0x07:                  /* Bell, abort */
      ctrlg(FALSE, 0);
      mlwrite("");
      return (ABORT);

    case 0x7F:                  /* Rubout, erase */
    case 0x04:
      cpos++;
    case 0x08:                  /* Backspace, erase */
      if (cpos != 0) {
        register char *cp0,*cp1;
        register int i;

        cp0=&(buf[cpos-1]);
        cp1=&(buf[cpos]);
        for (i=cpos; i<nbuf; i++) {
          *cp0++=*cp1++;
        }
        cpos--;
      }
      break;

    case 0x15:                  /* C-U, kill */
    case 0x18:                  /* also C-X */
      for(cpos=0; cpos<nbuf; cpos++)
        buf[cpos]=0;
      cpos=0;

      break;
    case 0x01:
      cpos=0;                           /* SHIFT Cursor left */
      break;
    case 0x05:
      cpos=strlen(buf);                 /* SHIFT Cursor right */
      break;
    case 0x02:
      if (cpos>0) cpos--;               /* Cursor left */
      break;
    case 0x06:
      if (cpos<strlen(buf)) cpos++;     /* Cursor right */
      break;
    case 0x9B:          /* skip mouse events */
      for (; (c != '|'); c = GetChar() );
      break;

    default:
      if (firstkey) {
        buf[0]=0; cpos=0;
      }
      if (cpos < nbuf-1) {
        register char *cp0,*cp1;
        register int i;

        cp0=&(buf[nbuf-2]);
        cp1=&(buf[nbuf-1]);
        for (i=nbuf-2; i>=cpos; i--)
          *cp1--=*cp0--;

        buf[cpos++] = c;
      }
    }
    firstkey = FALSE;
  }
}

/*
 * Write a message into the message line. Keep track of the physical cursor
 * position. A small class of printf like format items is handled. Assumes the
 * stack grows down; this assumption is made by the "++" in the argument scan
 * loop. Set the "message line" flag TRUE.
 */
mlwrite(fmt, arg)
char *fmt;
{
  register int c;
  register char *ap, *s;

  msgIdx=0;
  ap = (char *) &arg;
  while ((c = *fmt++) != 0) {
    if (c != '%') {
      PutMsgLine(c);
    }
    else {
      c = *fmt++;
      switch (c) {
        case 'd':
          mlputi(*(int *)ap, 10);
          ap += sizeof(int);
          break;

        case 'o':
          mlputi(*(int *)ap,  8);
          ap += sizeof(int);
          break;

        case 'x':
          mlputi(*(int *)ap, 16);
          ap += sizeof(int);
          break;

        case 'D':
          mlputli(*(long *)ap, 10);
          ap += sizeof(long);
          break;

        case 's':
          s = *(char **)ap;
          while ((c = *s++) != 0) {
            PutMsgLine(c);
          }
          ap += sizeof(char *);
          break;

        default:
           PutMsgLine(c);
      }
    }
  }
  PrintMsgLine();
  mpresf = TRUE;
  return(TRUE);
}

/*
 * Write out an integer, in the specified radix. Update the physical cursor
 * position. This will not handle any negative numbers; maybe it should.
 */
mlputi(i, r)
{
  register int q;
  static char hexdigits[] = "0123456789ABCDEF";

  if (i < 0) {
    i = -i;
    PutMsgLine('-');
  }

  q = i/r;

  if (q != 0)
    mlputi(q, r);

  PutMsgLine(hexdigits[i%r]);
  return(TRUE);
}

/*
 * do the same except as a long integer.
 */
mlputli(l, r)
long l;
{
  register long q;

  if (l < 0) {
    l = -l;
    PutMsgLine('-');
  }

  q = l/r;

  if (q != 0)
    mlputli(q, r);

  PutMsgLine((int)(l%r)+'0');
  return(TRUE);
}
