#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include "fontutils.h"
#include "miscutils.h"
#include "loadim.h"
#include "left.xbm"
#include "right.xbm"
#include "prev.xbm"
#include "next.xbm"

#define TAB 9
#define RETURN 10
#define ESC 27
#define DEL 127

#ifndef LIGHTGRAY
#define LIGHTGRAY 7
#define DARKGRAY 8
#define BLUE 1
#endif

void
vga_drawbox(int x, int y, int wid, int ht, int color, char flags) {
  char *scanline, top_color, bottom_color;
  int i;

  top_color = (flags & RAISED) ? LIGHTGRAY : DARKGRAY ;
  bottom_color = (flags & RAISED) ? DARKGRAY : LIGHTGRAY ;

  scanline = malloc (wid);
  memset (scanline,color, wid);
  scanline[0] = scanline[1] = top_color;
  scanline[wid - 1] = scanline[wid - 2] = bottom_color;

  if (flags & FILLED)
	for (i = 0; i <= ht; i++)
		vga_drawscansegment (scanline, x, i + y, wid);
  else {
	vga_setcolor (top_color);
	vga_drawline (x, y, x, y+ht);
	vga_drawline (x+1, y+1, x+1, y+ht-2);

	vga_setcolor (bottom_color);
	vga_drawline (x+wid-1, y, x+wid-1, y+ht);
	vga_drawline (x+wid-2, y+1, x+wid-2, y+ht-2);
  }
	
  free (scanline);

  vga_setcolor (top_color);
  vga_drawline (x, y, x + wid - 1, y);
  vga_drawline (x + 1, y + 1, x + wid - 2, y + 1);
  vga_drawpixel (x, y + ht);
  vga_drawpixel (x + wid - 1, y);

  vga_setcolor (bottom_color);
  vga_drawline (x, y + ht, x + wid - 1, y + ht);
  vga_drawline (x + 1, y + ht - 1, x + wid - 2, y + ht - 1);

}

char *
vga_prompt (short x, short y, short wid, short ht,
	    char *prompt, font_t * pfont, font_t * tfont,
	    short pfg, short tfg, short bg, unsigned char type)
{
  char *str,
   *save;
  int nchars,
    nlines;
  int i,
    j = 0;
  short px,
    py,
    pxx;

  /* Because scanline start and lengths must be multiples of 8 */
  if (x % 8)
    x = (x >> 3) << 3;
  if (wid % 8)
    wid = (wid >> 3) << 3;

  nchars = (wid - 16 - (strlen (prompt) << 3)) >> 3;
  nlines = ht / pfont->font_height;

#ifdef DEBUG
  fprintf (stderr, "nchars = %d, nlines = %d\n", nchars, nlines);
#endif

  /* Position of textual input */
  px = pxx = x + 16 + (strlen (prompt) << 3);
  py = y + (ht >> 1) - (pfont->font_height >> 1);

  /* Save the background */
  save = malloc (wid * ht);
  for (i = 0; i <= ht; i++)
    vga_getscansegment (save + i * wid, x, y + i, wid);

  vga_drawbox(x,y,wid,ht,bg,FILLED|RAISED);

  if (~type & PROMPT_NOINPUT) {
    vga_drawline (x + wid - 8, py - 1,
		  x + wid - 8, py + 2 + tfont->font_height * tfont->yscale);
    vga_drawline (px - 8, py + 2 + tfont->font_height * tfont->yscale,
		  x + wid - 8, py + 2 + tfont->font_height * tfont->yscale);
  }

  if (~type & PROMPT_NOINPUT) {
    vga_drawline (px - 8, py - 2,
		  x + wid - 8, py - 2);
    vga_drawline (px - 8, py - 2,
		  px - 8, py + 2 + tfont->font_height * tfont->yscale);
  }
  if (type & PROMPT_NOINPUT) {
    vga_write (prompt, x + (wid >> 1),
	       y + (ht >> 1) - (pfont->font_height >> 1),
	       pfont, pfg, bg, ALIGN_CENTER);
    if (type >> 2 == 0)
      goto leave;
    sleep (type >> 2);
    goto end;
  }
  vga_write (prompt, x + 8, y + (ht >> 1) - (pfont->font_height >> 1),
	     pfont, pfg, bg, ALIGN_LEFT);

  str = malloc (MAXPROMPT);
  bzero (str, MAXPROMPT);

#ifdef DEBUG
  fprintf (stderr, "px=%d, py=%d\n", px, py);
#endif
#if 0
  vga_putc ('_', px, py + 1, tfont, tfg, bg);
#else
  vga_setcolor (tfg);
  vga_drawline (px, py + tfont->font_height - 2, px + 6, py + tfont->font_height - 2);
#endif
  while ((str[j] = getchar ()) != ESC && isascii (str[j])) {
    switch (str[j]) {
    case RETURN:
#if 0
      if ((py += tfont->font_height) > y + ht)
	goto end;
      px = pxx;
      continue;
      break;
#endif
      str[j] = (char) NULL;
      goto end;

    case DEL:
      str[j] = ' ';
#if 0
      vga_putc ('_', px, py + 1, tfont, bg, bg);
#else
      vga_setcolor (bg);
      vga_drawline (px, py + tfont->font_height - 2, px + 6, py + tfont->font_height - 2);
#endif
      if ((px -= 8) >= pxx)
	vga_putc (str[j--], px, py, tfont, tfg, bg);
      else {
	px = pxx;
	j = j - 1 < 0 ? 0 : j - 1;
      }
#if 0
      vga_putc ('_', px, py + 1, tfont, tfg, bg);
#else
      vga_setcolor (tfg);
      vga_drawline (px, py + tfont->font_height - 2, px + 6, py + tfont->font_height - 2);
#endif
      continue;
    }

    if (py > y + ht)
      break;
    if (px > x + wid - 24) {
      void *tmpsave;

      if (type & PROMPT_FIXED) {
	printf ("\007");
	continue;
      }
      tmpsave = malloc (px - pxx - 8);
      for (i = py; i < tfont->font_height + py; i++) {
	vga_getscansegment (tmpsave, pxx + 8, i, px - pxx - 8);
	vga_drawscansegment (tmpsave, pxx, i, px - pxx - 8);
      }
      free (tmpsave);
      px -= 8;
    }
    if (j >= MAXPROMPT)
      continue;
    vga_putc (str[j++], px, py, tfont, tfg, bg);
#if 0
    vga_putc ('_', px += 8, py + 1, tfont, tfg, bg);
#else
    vga_setcolor (tfg);
    px += 8;
    vga_drawline (px, py + tfont->font_height - 2, px + 6, py + tfont->font_height - 2);
#endif
  }
end:

  /* Restore the background */
  for (i = 0; i <= ht; i++)
    vga_drawscansegment (save + i * wid, x, y + i, wid);

leave:
  free (save);
  return str;
}

void
vga_form (short x, short y, short wid, short ht,
	  char **prompts, char **replys, int num,
	  char *title, font_t * titlefont,
	  font_t * pfont, font_t * tfont,
	  char pafg, char pifg, char rfg, char bg,
	  int callback (), char *opts)
{

  typedef struct {
    char *prompt;
    char *reply;
    short pxx;
    short px;
    short py;
    int pos;
  } form_t;

  int curentry = 0;
  char *save;
  int i;
  int cbret;
  char key;
  form_t *entry = malloc (num * sizeof (form_t));

  /* Because scanline start and lengths must be multiples of 8 */
  if (x % 8)
    x = (x >> 3) << 3;
  if (wid % 8)
    wid = (wid >> 3) << 3;

  /* Save the background */
  save = malloc (wid * ht);
  for (i = 0; i <= ht; i++)
    vga_getscansegment (save + i * wid, x, y + i, wid);

  vga_drawbox(x,y,wid,ht,bg,FILLED|RAISED);

  if (title && titlefont)
  vga_message (x, y, wid, 8 + titlefont->font_height * titlefont->yscale,
	       title, titlefont, rfg, bg, 0);

  for (i = 0; i < num; i++) {
    entry[i].prompt = prompts[i];
    entry[i].reply = replys[i] = malloc (MAXPROMPT);
    bzero (replys[i], MAXPROMPT);
    entry[i].px = entry[i].pxx = x + 16 + (strlen (prompts[i]) << 3);
    if (title && titlefont)
    entry[i].py = y + 8 + titlefont->font_height * titlefont->yscale + 8 + i * (8 + pfont->font_height * pfont->yscale);
    else
    entry[i].py = y + 8 + i * (8 + pfont->font_height * pfont->yscale);
    entry[i].pos = 0;

    vga_write (entry[i].prompt,
	       x + 8, entry[i].py,
	       pfont, i ? pifg : pafg, bg, ALIGN_LEFT);

    vga_setcolor (LIGHTGRAY);
    vga_drawline (x + wid - 8,
		  entry[i].py - 1,
		  x + wid - 8,
		  entry[i].py + 2 + tfont->font_height * tfont->yscale);
    vga_drawline (entry[i].px - 8,
		  entry[i].py + 2 + tfont->font_height * tfont->yscale,
		  x + wid - 8,
		  entry[i].py + 2 + tfont->font_height * tfont->yscale);

    vga_setcolor (DARKGRAY);
    vga_drawline (entry[i].px - 8,
		  entry[i].py - 2,
		  x + wid - 8,
		  entry[i].py - 2);
    vga_drawline (entry[i].px - 8,
		  entry[i].py - 2,
		  entry[i].px - 8,
		  entry[i].py + 2 + tfont->font_height * tfont->yscale);
  }

begin:
  vga_setcolor (rfg);
  vga_drawline (entry[curentry].px,
		entry[curentry].py + tfont->font_height - 2,
		entry[curentry].px + 6,
		entry[curentry].py + tfont->font_height - 2);
  while ((entry[curentry].reply[entry[curentry].pos] = getchar ()) != ESC &&
	 isascii (entry[curentry].reply[entry[curentry].pos])) {
    switch (key = entry[curentry].reply[entry[curentry].pos]) {

    case TAB:
    case RETURN:
      entry[curentry].reply[entry[curentry].pos] = (char) NULL;
      if (callback)
	switch (callback (curentry, entry[curentry].reply)) {
	case FIELD_STAY:
	  continue;
	  break;
	case FIELD_CLEAR:
	  vga_setcolor (bg);
	  for (i = 0; i < tfont->font_height; i++)
	    vga_drawline (entry[curentry].pxx,
			  entry[curentry].py + i,
			  entry[curentry].px + 6,
			  entry[curentry].py + i);
	  bzero (entry[curentry].reply, MAXPROMPT);
	  entry[curentry].px = entry[curentry].pxx;
	  entry[curentry].pos = 0;
	  vga_setcolor (rfg);
	  vga_drawline (entry[curentry].px,
			entry[curentry].py + tfont->font_height - 2,
			entry[curentry].px + 6,
			entry[curentry].py + tfont->font_height - 2);
	  continue;
	  break;
	case FIELD_OK:
	default:
	  break;
	}
      if (key == TAB) {
	vga_write (entry[curentry].prompt,
		   x + 8, entry[curentry].py,
		   pfont, pifg, bg, ALIGN_LEFT);
	curentry = ++curentry == num ? 0 : curentry;
	vga_write (entry[curentry].prompt,
		   x + 8, entry[curentry].py,
		   pfont, pafg, bg, ALIGN_LEFT);
	goto begin;
      } else {
	if (curentry + 1 < num) {
	  vga_write (entry[curentry].prompt,
		     x + 8, entry[curentry].py,
		     pfont, pifg, bg, ALIGN_LEFT);
	  curentry++;
	  vga_write (entry[curentry].prompt,
		     x + 8, entry[curentry].py,
		     pfont, pafg, bg, ALIGN_LEFT);
	  goto begin;
	} else
	  goto end;
      }
    case DEL:
      entry[curentry].reply[entry[curentry].pos] = ' ';
      vga_setcolor (bg);
      vga_drawline (entry[curentry].px,
		    entry[curentry].py + tfont->font_height - 2,
		    entry[curentry].px + 6,
		    entry[curentry].py + tfont->font_height - 2);
      if ((entry[curentry].px -= 8) >= entry[curentry].pxx)
	vga_putc (entry[curentry].reply[entry[curentry].pos--],
		  entry[curentry].px,
		  entry[curentry].py,
		  tfont, rfg, bg);
      else {
	entry[curentry].px = entry[curentry].pxx;
	entry[curentry].pos = entry[curentry].pos - 1 < 0 ? 0 :
	  entry[curentry].pos - 1;
      }
      vga_setcolor (rfg);
      vga_drawline (entry[curentry].px,
		    entry[curentry].py + tfont->font_height - 2,
		    entry[curentry].px + 6,
		    entry[curentry].py + tfont->font_height - 2);

      continue;
    }

    if (entry[curentry].py > y + ht)
      break;
    if (entry[curentry].px > x + wid - 24) {
      void *tmpsave;

      if (opts && opts[curentry] & PROMPT_FIXED) {
	printf ("\007");
	continue;
      }
      tmpsave = malloc (entry[curentry].px - entry[curentry].pxx - 8);
      for (i = entry[curentry].py;
	   i < tfont->font_height + entry[curentry].py;
	   i++) {
	vga_getscansegment (tmpsave,
			    entry[curentry].pxx + 8,
			    i,
			    entry[curentry].px - entry[curentry].pxx - 8);
	vga_drawscansegment (tmpsave,
			     entry[curentry].pxx,
			     i,
			     entry[curentry].px - entry[curentry].pxx - 8);
      }
      free (tmpsave);
      entry[curentry].px -= 8;
    }
    if (entry[curentry].pos >= MAXPROMPT)
      continue;
    if (opts && opts[curentry] & PROMPT_NOECHO) {
      vga_putc ('*',
		entry[curentry].px,
		entry[curentry].py,
		tfont, rfg, bg);
      entry[curentry].pos++;
    } else
      vga_putc (entry[curentry].reply[entry[curentry].pos++],
		entry[curentry].px,
		entry[curentry].py,
		tfont, rfg, bg);
    entry[curentry].px += 8;
    vga_setcolor (rfg);
    vga_drawline (entry[curentry].px,
		  entry[curentry].py + tfont->font_height - 2,
		  entry[curentry].px + 6,
		  entry[curentry].py + tfont->font_height - 2);

  }
end:

  /* Restore the background */
  for (i = 0; i <= ht; i++)
    vga_drawscansegment (save + i * wid, x, y + i, wid);
  free (save);
  free (entry);
}

#define OFFSET 10

vga_bitmap left_bm={0,0,0}, right_bm={0,0,0},
		prev_bm={0,0,0}, next_bm={0,0,0};

void  vga_loaddefaultbitmaps(void) {

  if (!left_bm.data)
	loadxbmfromdata(left_bits,left_width,left_height,&left_bm);
  if (!right_bm.data)
	loadxbmfromdata(right_bits,right_width,right_height,&right_bm);
  if (!prev_bm.data)
	loadxbmfromdata(prev_bits,prev_width,prev_height,&prev_bm);
  if (!next_bm.data)
	loadxbmfromdata(next_bits,next_width,next_height,&next_bm);

}

void
vga_showmultiline (int x, int y, int wid, int ht, int fg, int bg, const char *text[], const char *title)
{
  font_t font;
  char *save, *p;
  int nlines, nchars, i;

  if (x<0 || y<0 || wid<0 || ht<0 || !text)
	return;
  vga_loaddefaultbitmaps();

  vga_initfont ("", &font, 1, 1);
  nchars = (wid - OFFSET) / (8 * font.xscale) - 1;
  nlines = (ht - OFFSET) / (font.yscale * font.font_height) - 1;

  /* Because scanline start and lengths must be multiples of 8 */
  if (x % 8)
    x = (x >> 3) << 3;
  if (wid % 8)
    wid = (wid >> 3) << 3;

  /* Save the background */
  save = malloc (wid * ht);
  for (i = 0; i <= ht; i++)
    vga_getscansegment (save + i * wid, x, y + i, wid);

  vga_drawbox(x,y,wid,ht,bg,FILLED|RAISED);

  if (title && *title) {
  char xtitle[1024];

  vga_setegacolor (BLUE);
  for (i = 0; i <= 18; i++) {
    vga_drawline (x+left_bm.wid, i+y, x+wid-1-right_bm.wid, i+y);
  }
  vga_tile(&left_bm, x+1, y+1, left_bm.wid, left_bm.ht);
  vga_tile(&right_bm, x+wid-right_bm.wid, y+1, right_bm.wid, right_bm.ht);

  sprintf (xtitle, "%s", title);
  vga_write ((char *) title, x+wid/2, y + 2,
	     &font, fg, BLUE, ALIGN_CENTER);
  }

  for (i = 0; i <= nlines && text[i]; i++) {
    int n = strlen (text[i]);

    p = strdup (text[i]);
    if (!p || !*p)
      continue;
    if (n > nchars)
      p[nchars] = (char) NULL;
    vga_write (p, x + OFFSET, y + OFFSET + i * font.font_height, &font, fg, bg, ALIGN_LEFT);
    free (p);
  }
  getchar ();
  /* Restore the background */
  for (i = 0; i <= ht; i++)
    vga_drawscansegment (save + i * wid, x, y + i, wid);

  free (save);

}

void vga_box(x,y,wid,ht,fg,bg,fill) {
	int i;
	int bx = fill & BOXED ? 1 : 0;

	vga_setcolor(bg);
	if (fill & FILLED)
		for (i=0; i<ht;i++)
			vga_drawline(x,y+i,x+wid-1,y+i);

	vga_setcolor(fg);
	vga_drawline(x+bx,y+bx,x+wid-bx-1,y+bx);
	vga_drawline(x+wid-bx-1,y+bx,x+wid-bx-1,y+ht-bx-1);
	vga_drawline(x+bx,y+bx,x+bx,y+ht-bx-1);
	vga_drawline(x+bx,y+ht-bx-1,x+wid-bx-1,y+ht-bx-1);

}

void vga_reverse_win (vga_win *win, int fg, int bg) {
	int i, j;
	char *line;

	if (!(line=malloc(win->wid)))
		return;
	for (i=0; i<win->ht; i++)
		for (j=0; j<win->wid; j++) {
			line[j] = win->data[i*win->wid+j]  == bg ? fg :
					win->data[i*win->wid+j] == fg ? bg :
						win->data[i*win->wid+j];
		vga_drawscansegment (line,
				     win->x,
				     win->y + i,
				     win->wid);

		}
	free(line);
}

void vga_save_win (vga_win *win) {
	int i;

	if (!win) return;

	/* Save the background */
	win->data = malloc (win->wid * (win->ht+1));
	for (i = 0; i <= win->ht; i++)
		vga_getscansegment (win->data + i * win->wid,
				    win->x,
				    win->y + i,
				    win->wid);
	
}

void vga_restore_win (vga_win *win) {
	int i;

	if (!win || !win->data) return;

	/* Restore the background */
	for (i = 0; i <= win->ht; i++)
		vga_drawscansegment (win->data + i * win->wid,
				     win->x,
				     win->y + i,
				     win->wid);
	free (win->data);
}
