/*
 *	Universal Terminal Interface -- Linux /dev/vcsa support.
 *
 *	(c) 1997 Martin Mares, <mj@ericsson.cz>
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <alloca.h>

#include "termint.h"

int use_vcsa;

static int vcsa_fd, vcsa_buf_size, vcsa_pos;

struct vcsa_size {
  byte rows, cols;
};

struct vcsa_pos {
  byte col, row;
};

#ifdef HAVE_PREAD

void
vcsa_read(int pos, void *w, int c)
{
  if (pread(vcsa_fd, w, c, pos) != c)
    fatal_error("VCSA read error");
}

void
vcsa_write(int pos, void *w, int c)
{
  if (pwrite(vcsa_fd, w, c, pos) != c)
    fatal_error("VCSA write error");
}

#else

void
vcsa_read(int pos, void *w, int c)
{
  if ((vcsa_pos != pos && lseek(vcsa_fd, pos, SEEK_SET) < 0) ||
      read(vcsa_fd, w, c) != c)
    fatal_error("VCSA read error");
  vcsa_pos += c;
}

void
vcsa_write(int pos, void *w, int c)
{
  if ((vcsa_pos != pos && lseek(vcsa_fd, pos, SEEK_SET) < 0) ||
      write(vcsa_fd, w, c) != c)
    fatal_error("VCSA write error");
  vcsa_pos += c;
}

#endif

#define VCSA_MAX_DEAD 5

static byte color_xlate[16] = { 0, 4, 2, 6, 1, 5, 3, 7, 0, 4, 2, 6, 1, 5, 3, 7 };

static void
vcsa_ref_reg(int from, int to)
{
  byte *buf = alloca(vcsa_buf_size);
  byte *bw = buf;
  int i, pos, pp, lpp;

  pp = columns*from + lfirst[from];
  lpp = columns*to + llast[from];
  pos = 4 + 2*pp;
  while (pp <= lpp)
    {
      byte a;
      *bw++ = cnew[pp];
      a = knew[pp];
      if (anew[pp] & ATTR_INVERSE)
	a = (color_xlate[a & 0x0f] << 4) | color_xlate[(a >> 4) & 0x0f];
      else
	a = (color_xlate[(a >> 4) & 0x0f] << 4) | color_xlate[a & 0x0f];
      if (anew[pp] & ATTR_BRIGHT)
	a |= 0x08;
      *bw++ = a;
      cold[pp] = cnew[pp];
      kold[pp] = knew[pp];
      aold[pp] = anew[pp];
      pp++;
    }
  vcsa_write(pos, buf, bw - buf);

  for(i=from; i<=to; i++)
    {
      lfirst[i] = 0xff;
      llast[i] = 0;
    }
}

void
vcsa_refresh(void)
{
  int li;

  FLUSH;

  while (ltop <= lbot)
    {
      if (lfirst[ltop] > llast[ltop])
	{
	  ltop++;
	  continue;
	}
      li = ltop++;
      while (ltop <= lbot)
	{
	  if (lfirst[ltop] <= llast[ltop])
	    ltop++;
	  else
	    {
	      int p = 0;
	      int l = ltop;
	      while (p < VCSA_MAX_DEAD && l <= lbot && lfirst[l] > llast[l])
		l++, p++;
	      if (p >= VCSA_MAX_DEAD || l > lbot)
		break;
	      else
		ltop += p;
	    }
	}
      vcsa_ref_reg(li, ltop-1);
    }
}

void
vcsa_goto(int r, int c)
{
  struct vcsa_pos vp;

  FLUSH;

  vp.row = r;
  vp.col = c;
  vcsa_write(2, &vp, sizeof(vp));
}

void
try_vcsa(void)
{
  char *x, *y;
  char buf[64];
  struct vcsa_size size;

#if 1
  return;
#endif

  if (CTRL(NO_VCSA))
    return;
  if (strcmp(term_type, "linux"))
    return;
  if (!(x = getenv("TTY")))
    return;
  if (!(y = strstr(x, "/tty")) || y[4] < '0' || y[4] > '9')
    return;
  *y = 0;
  sprintf(buf, "%s/vcsa%s", x, y+4);
  vcsa_fd = open(buf, O_RDWR);
  DBG(("Trying to open %s\n", buf));
  if (vcsa_fd < 0)
    return;
  DBG(("Okay, using fd %d for vcsa\n", vcsa_fd));

  use_vcsa = 1;
  vcsa_read(0, &size, sizeof(size));
  if (rows != size.rows || columns != size.cols)
    {
      rows = size.rows;
      columns = size.cols;
      DBG(("Overriding size by vcsa to %dx%d\n", columns, rows));
    }

  auto_margins = AM_NONE;
  use_tabs = TABS_NONE;

  vcsa_buf_size = rows * columns * 2;
}

void
vcsa_cleanup(void)
{
  close(vcsa_fd);
  use_vcsa = 0;
}
