/*
 *  Copyright (c) 1992, 1994 John E. Davis  (davis@amy.tch.harvard.edu)
 *  All Rights Reserved.
 */
#include <stdio.h>
#include <string.h>

#ifdef msdos
#include <conio.h>
#include <bios.h>
#include <mem.h>
#endif

#ifdef __GO32__
#include <pc.h>
#define GO32_DPMI
#endif

#ifdef __os2__
#define INCL_BASE
#define INCL_NOPM
#define INCL_VIO
#define INCL_KBD
#include <os2.h>
#endif

#include <dos.h>

#include "config.h"
#include "sysdep.h"
#include "display.h"
#include "screen.h"
#include "hooks.h"

int Scroll_r1 = 0, Scroll_r2 = 25, Cursor_Row = 1, Cursor_Col = 1;
int One60;
int Term_Cannot_Insert;

unsigned char *Video_Base;
int Attribute_Byte = 0;
int Msdos_Insert_Mode = 0;
static int Rev_Vid_Flag;
static int IsColor = 0;
static int Blink_Killed;	       /* if high intensity background 
					* enabled.
					*/

#ifdef msdos
int Cheap_Video = 0;
static int Video_Status_Port;

#define SNOW_CHECK\
   if (Cheap_Video)\
     { while ((inp(0x3DA) & 0x08)); while (!(inp(0x3DA) & 0x08)); }

struct SREGS SegRegs;
#endif

typedef struct 
{
   int fg, bg;
} Ansi_Color_Type;

Ansi_Color_Type Ansi_Color_Map[JMAX_COLORS] = 
{
   {7, 0}, {0, 7}, {0, 7}, {0, 7},
   {7,0},  			       /* operator */
   {7,0},			       /* numbers */
   {7,0},			       /* strings */
   {7,0},			       /* comments */
   {7,0},			       /* keyword */
   {7,0}			       /* delimeters */
};



void send_string_to_term(char *s)
{
   fputs(s, stdout);
}


void set_scroll_region(int r1, int r2)
{
    Scroll_r1 = r1 - 1;
    Scroll_r2 = r2 - 1;
}

void reset_scroll_region(void)
{
    Scroll_r1 = 0;
    Scroll_r2 = Screen_Height;
}

void goto_rc(int r, int c)
{
#ifdef __GO32__
   if (r > Screen_Height) r = Screen_Height; 
   if (c > Screen_Width) c = Screen_Width; 
   ScreenSetCursor(Scroll_r1 + r - 1, c - 1);
   Cursor_Row = r;
   Cursor_Col = c;
#else
# ifdef __os2__
   if (r > Screen_Height) r = Screen_Height;
   if (c > Screen_Width) c = Screen_Width;
   VioSetCurPos(Scroll_r1 + r - 1, c - 1, 0);
   Cursor_Row = r;
   Cursor_Col = c;
# else
   /* if (r > Screen_Height) r = Screen_Height; */
   asm mov ax, r
   asm mov bx, Screen_Height
   asm cmp ax, bx
   asm jle L1
   asm mov ax, bx
   L1:
   /* if (c > Screen_Width) c = Screen_Width; */
   asm mov cx, Screen_Width
   asm mov bx, c
   asm cmp bx, cx
   asm jle L2
   asm mov bx, cx
   L2:
   asm mov Cursor_Row, ax
   asm mov Cursor_Col, bx
   
   asm dec ax
   asm dec bx
   asm add ax, Scroll_r1
   asm xor dx, dx
   asm mov dh, al
   asm mov dl, bl
   asm xor bx, bx
   asm mov ax, 0x200
   asm int 0x10
# endif /* __os2__ */
#endif
}

void wherexy(void)
{
#ifdef __GO32__
   ScreenGetCursor(&Cursor_Row, &Cursor_Col);
   Cursor_Row++; Cursor_Col++;
#else
# ifdef __os2__
   VioGetCurPos((USHORT*) &Cursor_Row, (USHORT*) &Cursor_Col, 0);
   Cursor_Row++; Cursor_Col++;
# else
   
   asm mov ah, 3
   asm mov bh, 0
   asm int 10h
   asm xor ax, ax
   asm mov al, dh
   asm inc ax
   asm mov Cursor_Row, ax
   asm xor ax, ax
   asm mov al, dl
   asm inc ax
   asm mov Cursor_Col, ax
#endif /* __os2__ */
#endif
}

#ifdef __os2__
# define MAXCOLS 256
static char tempBuffer [MAXCOLS*2];
#endif

#ifdef GO32_DPMI
static void go32_dpmi_viowrite_n(unsigned short sh, int n, int r, int c)
{
   int ch, at, cmax = c + n;
   ch = sh & 0xFF; at = sh >> 8;
   
   while (c < cmax) ScreenPutChar(ch, at, c++, r);
}

static void go32_dpmi_viowrite_str(unsigned char *s, int n, int a, 
				 int r, int c)
{
   unsigned char *smax = s + n;
   while (s < smax) ScreenPutChar((int) *s++, a, c++, r);
}
#endif

void begin_insert(void)
{
#ifndef GO32_DPMI
#ifdef __GO32__
   unsigned short *pmin;
#endif
   unsigned short *p; 
   int n;
   
   Msdos_Insert_Mode = 1;
   wherexy();
   
   /* move the stuff over 1 column */
   n = Screen_Width - Cursor_Col;
#ifdef __os2__
    n *= 2;
    VioReadCellStr (tempBuffer, (USHORT*) &n, Cursor_Row-1, Cursor_Col-1, 0);
    VioWrtCellStr (tempBuffer, n, Cursor_Row-1, Cursor_Col, 0);
#else   
   
   p = (unsigned short *) (Video_Base + ((One60 * (Cursor_Row - 1) 
					  + 2 * (Screen_Width - 1))));
#ifdef __GO32__
    pmin = (unsigned short *) (Video_Base +
				(One60 * (Cursor_Row - 1) + 
				 2 * (Cursor_Col - 1)));
   
   while(p-- > pmin) *(p + 1)= *p; 
#else
   SNOW_CHECK;
   asm mov ax, ds
   asm mov bx, di
   asm mov dx, si
   
   asm mov cx, n
   asm les di, p
   asm lds si, p
   asm sub si, 2
   asm std
   asm rep movsw

   asm mov ds, ax
   asm mov di, bx
   asm mov si, dx
#endif
#endif  /* __os2__ */
#endif /* GO32_DPMI */
}

void end_insert(void)
{
   Msdos_Insert_Mode = 0;
}

void tt_delete_char()
{
#ifndef GO32_DPMI
#ifdef __GO32__
   register unsigned short *p1;
#endif
   unsigned short *p;
   int n;
   
   wherexy();
   /* move the stuff over 1 column */
#ifdef __os2__
    n = 2 * (Screen_Width - Cursor_Col);
    VioReadCellStr (tempBuffer, (USHORT*)&n, Cursor_Row-1, Cursor_Col, 0);
    VioWrtCellStr (tempBuffer, n, Cursor_Row-1, Cursor_Col-1, 0);
    return;
#else
   
   p = (unsigned short *) (Video_Base +  
			   (One60 * (Cursor_Row - 1) + 2 * (Cursor_Col - 1)));
   n = Screen_Width - Cursor_Col;
   
#ifdef __GO32__
    while(n--)
      {
	 p1 = p + 1;
	 *p = *p1;
	  p++;
      }
#else
   
   SNOW_CHECK;
   asm mov ax, ds
   asm mov bx, si
   asm mov dx, di
   
   asm mov cx, n
   asm les di, p
   asm lds si, p
   asm add si, 2
   asm cld
   asm rep movsw
   
   asm mov ds, ax
   asm mov si, bx
   asm mov di, dx
#endif
#endif /* __os2__ */
#endif /* not GO32_DPMI */   
}

/* This function is ONLY called when exiting editor.  It sets attribute byte
   to Black & White */
void tt_erase_line(void)
{
#ifdef __os2__
   USHORT w;
   Attribute_Byte = 7;
   w = (Attribute_Byte << 8) | 32;
   VioWrtNCell ((BYTE*)&w, Screen_Width, Cursor_Row-1, 0, 0);
#else
#ifdef GO32_DPMI
   unsigned short w = (7 << 8) | 32;
   Attribute_Byte = 7;
   go32_dpmi_viowrite_n (w, Screen_Width, Cursor_Row - 1, 0);
#else
   
   unsigned short *p = (unsigned short *) (Video_Base + One60 * (Cursor_Row - 1));
   unsigned short w;
#ifdef __GO32__
   register unsigned short *pmax = p + Screen_Width;
   
   Attribute_Byte = 7;
   w = (Attribute_Byte << 8) | 32;
   while (p < pmax) *p++ = w;
#else
   Attribute_Byte = 7;
   w = (Attribute_Byte << 8) | 32;
   SNOW_CHECK;
   asm mov dx, di
   
   asm mov ax, w
   asm mov cx, Screen_Width
   asm les di, p
   asm cld
   asm rep stosw
   
   asm mov di, dx
#endif  /* __GO32__ */
#endif  /* GO32_DPMI */
#endif  /* __os2__ */
   Rev_Vid_Flag = -1;		       /* since we messed with attribute byte */
}

void tt_delete_nlines(int n)
{
#ifdef __os2__
   char Cell [2];
   tt_normal_video ();
   Cell [0] = ' ';
   Cell [1] = Attribute_Byte;
   VioScrollUp (Scroll_r1, 0, Scroll_r2, Screen_Width-1, n, Cell, 0);
#else   
#ifdef __GO32__
   union REGS in;
   tt_normal_video();
   in.x.ax = n;
   in.x.cx = 0;
   in.h.ah = 6;
   in.h.ch = Scroll_r1;
   in.h.dl = Screen_Width - 1;
   in.h.dh = Scroll_r2;
   in.h.bh = Attribute_Byte;
   int86(0x10, &in, &in);
#else
   tt_normal_video();
   /* This has the effect of pulling all lines below it up */
   asm mov ax, n
   asm mov ah, 6		       /* int 6h */
   asm xor cx, cx
   asm mov ch, byte ptr Scroll_r1
   asm mov dx, Screen_Width
   asm dec dx
   asm mov dh, byte ptr Scroll_r2
   asm mov bh, byte ptr Attribute_Byte
   asm int 10h
#endif
#endif /* __os2__ */
}


void reverse_index(int n)
{
#ifdef __os2__
   char Cell [2];
   tt_normal_video ();
   Cell [0] = ' ';
   Cell [1] = Attribute_Byte;
   VioScrollDn (Scroll_r1, 0, Scroll_r2, Screen_Width-1, n, Cell, 0);
#else   
#ifdef __GO32__
   union REGS in;
   tt_normal_video();
   in.h.al = n;
   in.x.cx = 0;
   in.h.ah = 7;
   in.h.ch = Scroll_r1;
   in.h.dl = Screen_Width - 1;
   in.h.dh = Scroll_r2;
   in.h.bh = Attribute_Byte;
   int86(0x10, &in, &in);
#else
   tt_normal_video();
   asm xor cx, cx
   asm mov ch, byte ptr Scroll_r1
   asm mov dx, Screen_Width
   asm dec dx
   asm mov dh, byte ptr Scroll_r2
   asm mov bh, byte ptr Attribute_Byte
   asm mov ah, 7
   asm mov al, byte ptr n
   asm int 10h
#endif
#endif /* __os2__ */
}

int beep(void)
{
    /* clear the keyboard buffer since this is an error */
   if (!Ignore_Beep)
     {
#ifdef __os2__
	DosBeep (1500, 100);
#else
	sound(1500);
	delay(100);
	nosound();
#endif
     }
   
   flush_input();
   return(0);
}

void tt_del_eol(void)
{
#ifdef __os2__
   int n = Screen_Width - Cursor_Col + 1;
   USHORT w;
 
   tt_normal_video ();
   w = (Attribute_Byte << 8) | 32;
   VioWrtNCell ((BYTE*)&w, n, Cursor_Row-1, Cursor_Col-1, 0);
#else
#ifdef GO32_DPMI
   int n = Screen_Width - Cursor_Col + 1;
   unsigned short w;
   tt_normal_video ();
   w = (Attribute_Byte << 8) | 32;
   go32_dpmi_viowrite_n (w, n, Cursor_Row - 1, Cursor_Col - 1);
#else
   unsigned short *p = (unsigned short *) (Video_Base +  (One60 * (Cursor_Row - 1) + 2 * (Cursor_Col - 1)));
   int n = Screen_Width - Cursor_Col;
   unsigned short w;
#ifdef __GO32__
   unsigned short *pmax = p + (n + 1);
   
   tt_normal_video();
   w = (Attribute_Byte << 8) + (unsigned short) ' ';
   while (p < pmax) *p++ = w;
#else
   
   tt_normal_video();
   w = (Attribute_Byte << 8) + (unsigned short) ' ';
   SNOW_CHECK;
   asm mov dx, di
   
   asm les di, p
   asm mov ax, w
   asm mov cx, n
   asm inc cx
   asm cld
   asm rep stosw
   
   asm mov di, dx
#endif
#endif /* GO32_DMPI */
#endif /* __os2__ */
}

static void tt_reverse_video(int color)
{
   Attribute_Byte = Ansi_Color_Map[color].fg | (Ansi_Color_Map[color].bg << 4);
   Rev_Vid_Flag = color;
}

void tt_normal_video(void)
{
   tt_reverse_video(JNORMAL_COLOR);
}

#define CGA_STATUS 0x3DA
#define CGA_SETMODE 0x3D8

static unsigned short *video_write(register unsigned char *pp, 
				   register unsigned char *p,
				   register unsigned short *pos)
{
#ifdef __os2__
   int n = p - pp;
   int attr = Attribute_Byte;
   VioWrtCharStrAtt ((CHAR*)pp, n, Cursor_Row-1, *pos-1, (BYTE*)&attr, 0);
   *pos += n;
   return pos;
#else
#ifdef GO32_DPMI
   int n = p - pp;
   go32_dpmi_viowrite_str (pp, n, Attribute_Byte, 
			   Cursor_Row - 1,  (int) *pos - 1);
   *pos += n;
   return pos;
#else
#ifdef __GO32__
   while (pp < p)
     {
	*pos++ = (Attribute_Byte << 8) + (unsigned short) (*pp++);
     }
   return pos;
#else
   int n = (int) (p - pp);
   
   asm push si
   asm push ds
   asm push di
   
   /* set up register for BOTH fast and slow */
   asm mov bx, Cheap_Video
   
   /* These are the registers needed for both fast AND slow */
   asm mov ah, byte ptr Attribute_Byte
   asm mov cx, n
   asm lds si, dword ptr pp
   asm les di, dword ptr pos
   asm cld
   
   asm cmp bx, 0		       /* cheap video test */
   asm je L_fast
   asm mov bx, ax
   asm mov dx, CGA_STATUS
   asm jg L_slow_blank
   
   /* slow video */
   asm cli
   /* wait for retrace */
   L_slow:
   asm in al, dx
   asm test al, 1
   asm jnz L_slow
   
   L_slow1:
   asm in al, dx
   asm test al, 1
   asm jz L_slow1
   
   /* move a character out */
   asm mov ah, bh
   asm lodsb
   asm stosw
   asm loop L_slow
   
   asm sti
   asm jmp done
   
/* -------------- slow video, vertical retace and pump --------------*/
   L_slow_blank:
   L_slow_blank_loop:
   asm in al, dx
   asm test al, 8
   asm jnz L_slow_blank_loop
   
   L_slow_blank1:
   asm in al, dx
   asm test al, 8
   asm jz L_slow_blank1
   
   /* write line */
   asm mov ah, bh
   L_slow_blank2:
   asm lodsb
   asm stosw
   asm loop L_slow_blank2
   
   asm jmp done
/*-------------- Fast video --------------*/   
   
   L_fast:
   asm lodsb
   asm stosw
   asm loop L_fast

   done:
   
   asm pop di
   asm pop ds
   asm pop si
   return (pos + n);
#endif
#endif  /* GO32_DPMI */
#endif  /* __os2__ */
}

static void write_attributes(unsigned short *s, int row)
{
   unsigned char out[250], ch, attr;
   register unsigned char *p;
   register unsigned short sh, *pos;
   unsigned short *smax;
#if defined(__os2__) || defined(GO32_DPMI)
    short Col;
#endif
   
   Cursor_Row = row;
   Cursor_Col = 1;
#if !defined(__os2__) && !defined(GO32_DPMI)
     pos = (unsigned short *) (Video_Base +  (One60 * (Cursor_Row - 1)));
#else
    Col = 1;
    pos = &Col;
#endif
   
   smax = s + Screen_Width;
   p = out;
   while (s < smax)
     {
	sh = *s++;
	ch = sh & 0xFF;
	attr = sh >> 8;
	if (attr != Rev_Vid_Flag)
	  {
	     if (p != out)
	       {
		  pos = video_write(out, p, pos);
		  p = out;
	       }
	     tt_reverse_video(attr);
	  }
	*p++ = ch;
     }
   pos = video_write(out, p, pos);
}


   

void smart_puts(unsigned short *new_string, unsigned short *dummy, int row, int dum1)
{
#ifndef __GO32__
   (void) dummy;  (void) dum1;
#endif
   Cursor_Row = row;
   Cursor_Col = 1;
   write_attributes(new_string, row);
}

void reset_video(void)
{
}

void init_video(void)
{
#ifdef __GO32__
   union REGS in, out;
#else
   unsigned char *p;
#endif
   Cursor_Row = Cursor_Col = 1;
   /* One60 = Screen_Width < 80 ?  80: Screen_Width; */
   One60 = Screen_Width;
   One60 *= 2;
   
#ifdef __os2__
   if (Attribute_Byte == 0) 
     {
	/* find the attribute currently under the cursor */
	USHORT Length = 2, Row, Col;
	char CellStr [2];
	VioGetCurPos (&Row, &Col, 0);
	VioReadCellStr(CellStr, &Length, Row, Col, 0);
	Attribute_Byte = CellStr[1];
	IsColor = 1;			       /* is it though?? */
     }
     {
	/* Enable high-intensity background colors */
	VIOINTENSITY RequestBlock;
	RequestBlock.cb = sizeof(RequestBlock);
	RequestBlock.type = 2; RequestBlock.fs = 1;
	VioSetState(&RequestBlock, 0);  /* nop if !fullscreen */
	Blink_Killed = 1;
     }
   IsColor = 1;
   
#else 
#ifndef __GO32__
   p = (unsigned char far *) 0x00400049L;

   Video_Status_Port = 0x3DA;
   
   if (*p == 7)
     {
	Video_Status_Port = 0x3BA;
	Video_Base = (unsigned char *) MK_FP(0xb000,0000);
     }
   else 
     {
	Video_Base = (unsigned char *) MK_FP(0xb800,0000);
	IsColor = 1;
     }
   

   /* test for video adapter type.  Of primary interest is whether there is
      snow or not.  Assume snow if the card is color and not EGA or greater.
   */

   /* Use Ralf Brown test for EGA or greater */
   asm mov ah, 0x12
   asm mov bl, 0x10
   asm mov bh, 0xFF
   asm int 10h
   asm cmp bh, 0xFF
   asm je L1
   
   /* (V)EGA */
   asm xor bx, bx
   asm mov Cheap_Video, bx
   asm mov ax, Attribute_Byte
   asm cmp ax, bx
   asm jne L2
   asm mov ax, 0x17
   asm mov Attribute_Byte, ax
   asm jmp L2
   
   L1:
   /* Not (V)EGA */
   asm mov ah, 0x0F
   asm int 10h
   asm cmp al, 7
   asm je L3
   asm mov ax, 1
   asm mov Cheap_Video, ax
   L3:
   asm mov ax, Attribute_Byte
   asm cmp ax, 0
   asm jne L2
   asm mov ax, 0x07
   asm mov Attribute_Byte, ax
   
   L2:
#else
#ifndef GO32_DPMI
   Video_Base = (unsigned char *) ScreenPrimary;
#endif
   if (Attribute_Byte == 0) Attribute_Byte = 0x17;
   IsColor = 1;			       /* is it though?? */
#endif
#endif  /* __os2__ */
   
#ifdef msdos
   /* toggle the blink bit so we can use hi intensity background */
   if (IsColor && !Cheap_Video)
     {
	asm mov ax, 0x1003
	asm mov bx, 0
	asm int 0x10
	Blink_Killed = 1;
     }
#endif
#ifdef __GO32__
#ifdef GO32_DPMI
   Term_Cannot_Insert = 1;
#endif
   if (IsColor)
     {
	in.x.ax = 0x1003; in.x.bx = 0;
	int86(0x10, &in, &out);
	Blink_Killed = 1;
     }
#endif
}

#if 0
void wide_width(void)
{
}

void narrow_width(void)
{
}
#endif

void cls(void)
{
#ifdef __os2__
   char Cell [2];
#endif
   
   tt_normal_video();
#ifdef __os2__
   Cell [0] = ' ';
   Cell [1] = Attribute_Byte;
   VioScrollUp (0, 0, -1, -1, -1, Cell, 0);
   return;
#else
#ifdef __GO32__
   set_scroll_region(1, Screen_Height);
   tt_delete_nlines(Screen_Height);
   
#else
   asm mov dx, Screen_Width
   asm dec dx
   asm mov ax, Screen_Height
   asm dec ax
   asm mov dh, al
   asm xor cx, cx
   asm xor ax, ax
   asm mov ah, 7
   asm mov bh, byte ptr Attribute_Byte
   asm int 10h
#endif
#endif /* __os2__ */
}

/* This function is called assuming that cursor is in correct 
   position */
void tt_putchar(char ch)
{
#ifndef GO32_DPMI
   unsigned short p, *pp;
#endif
   if (Rev_Vid_Flag) tt_normal_video();
   wherexy();
   switch(ch)
     {
      case 7: beep(); break;
      case 8: goto_rc(Cursor_Row, Cursor_Col - 1); break;
      case 13: goto_rc(Cursor_Row, 1); break;
	
      default: 
#ifdef __os2__
 	VioWrtCharStrAtt (&ch, 1, Cursor_Row - 1, Cursor_Col - 1,
 			  (BYTE*)&Attribute_Byte, 0);
#else
#ifdef GO32_DPMI
	go32_dpmi_viowrite_str(&ch, 1, Attribute_Byte, Cursor_Row - 1, Cursor_Col - 1);
#else
	pp = (unsigned short *) (Video_Base + (One60 * (Cursor_Row - 1)));
	pp += Cursor_Col - 1;
	p = (Attribute_Byte << 8) | (unsigned char) ch;
#ifndef __GO32__
	SNOW_CHECK;
#endif
	*pp = p;
#endif  /* GO32_DPMI */
#endif  /* __os2__ */
	goto_rc(Cursor_Row, Cursor_Col + 1);
     }
}
static char *pc_colors[16] =
{
   "black", "blue", "green", "cyan", "red", "magenta", "brown", "lightgray",
   "gray", "brightblue", "brightgreen", "brightcyan", "brightred", 
   "brightmagenta", "yellow", "white" 
};

static void pc_set_color (int obj, char *what, char *fg, char *bg)
{
   int i, f = -1, g = -1;
   
   if (!IsColor) return;
   if (obj == -1) 
     {
	(void) what;
	return;
     }
   
   
   i = 0; while (i < 16)
     {
	if (!strcmp(fg, pc_colors[i]))
	  {
	     f = i;
	     break;
	  }
	i++;
     }
   i = 0; while (i < 16)
     {
	if (!strcmp(bg, pc_colors[i]))
	  {
	     if (Blink_Killed == 0) g = i & 0x7; else g = i;
	     break;
	  }
	i++;
     }
   if ((f == -1) || (g == -1) || (f == g)) return;
   Ansi_Color_Map[obj].fg = f;
   Ansi_Color_Map[obj].bg = g;
}

void get_terminfo ()
{
   X_Set_Color_Hook = pc_set_color;
}
