// C++ code Copyright (C) David R. Evans G4AMJ/NQ0I

//  surface class

#include <gf_vga.h>
#include <surf_vga.h>

#include <bios.h>
#include <ctype.h>
#include <dos.h>
#include <fstream.h>
#include <graphics.h>
#include <math.h>

// constructor
vga::vga(void)
{ // register the BGI VGA driver
  int status = registerbgidriver(EGAVGA_driver);
  if (status < 0)
    fatal_error((DREstring)"Unable to register BGI driver; error number " +
                DREstring10(status));

// initialise the BGI information
  int gdriver = VGA, gmode = 2;
  initgraph(&gdriver, &gmode, "");
  status = graphresult();
  if (status < 0) then
    fatal_error("Unable to initialise VGA device driver");
  cleardevice();

// define the grey font
  heap_check(gfp = new grey_font_vga(3, 3));

// now store the important information
  x_ofs = 0;
  y_ofs = 0;
  use_left = 0;
  use_right = getmaxx();
  use_top = 0;
  use_bottom = getmaxy() - 40;  // leave room for command line
  clr = EGA_WHITE;
  _dotsize = 1;
  _x = 0;
  _y = 0;

// set the default output device
  printer_is_pcl();

#ifdef LASERJET
  printer_is_pcl();
#endif
#ifdef LASERWRITER
  printer_is_postscript();
#endif

}

// destructor
vga::~vga(void)
{ closegraph(); 
  destroy(gfp);
}

// change output device to PCL
void vga::printer_is_pcl(void)
{ _dump_to_printer = (PMFV)&vga::_dump_to_pcl; }

// flush the grey_font buffer to a vga
void vga::flush(void)
{ if (!(gfp->null())) then
  { for (int n = 0; n < gfp->length(); n++)
    { if (!(gfp->null(n))) then
      { _drawsquare(_x, _y, gfp, n);
      }
      _y += gfp->height();
    }
  }
  gfp->length(0);
}

// draw a square
void vga::_drawsquare(const int x, const int y, grey_font* gfp,
                     const int n)
{ const int square_to_plot = gfp->element(n);
  putimage(x, y, gfp->memory_pattern(square_to_plot), COPY_PUT);
}

// move the cursor (absolute)
void vga::moveto(const int x, const int y)
{ if ((x >= 0) && (y >= 0)) then
  { ::moveto(x, y); 
    _x = x;
    _y = y;
  }
}

// move the cursor (relative)
void vga::moverel(const int x, const int y)
{ ::moverel(x, y);
  _x += x;
  _y += y;
}

// vga << text
surface& vga::operator<<(text& txt)
{ const int x = (txt.x() < 0) ? (width() - txt.length() * 8) / 2 : txt.x();
  moveto(x, txt.y());
  if (txt.length()) then
    ::outtext((const char*)txt.words());
  return *this;
}

// clear
void vga::clear(void)
{ setfillstyle(EMPTY_FILL, 0);
  bar(use_left, use_top, use_right, use_bottom);
}

// draw a line
surface& vga::operator<<(LINE& l)
{ setlinestyle(0, 0, 1);
  line(l.x1(), l.y1(), l.x2(), l.y2());
  return *this;
}

// draw a dot
surface& vga::operator<<(dot& d)
{ int ds = ( (d.size() == -1) ? _dotsize : d.size());
  for (int n = 0; n < ds; n++)
    for (int m = 0; m < ds; m++)
      putpixel(d.x() - n, d.y() - m, 
        (d.clr() == -1 ? clr : d.clr()));
  return *this;
}

// dump to a PCL printer
void vga::_dump_to_pcl(void)
{ // this routine should have taken no more than half an hour to write.
  // In fact it took the better part of a day. For reasons that I don't
  // understand at all, none of the obvious (and most of the less obvious)
  // ways of doing this produced erroneous output on my printer.

  // It got to the point where if I send output directly to the printer,
  // then it is wrong, but if I send it to a file and THEN to the printer,
  // it is correct. Using stdprn as the output C stream never did work;
  // neither did using the C++ stream built into the lj2 device. The
  // routine below DOES work on my system; I don't know why none of the 
  // variants I tried earlier didn't work just as well.

  FILE* tdprn = tmpfile();

  fprintf(tdprn, "%c*t%dR", (char)27, 75);
  fprintf(tdprn, "%c*r0A", (char)27);

  for (int n = 0; n < height(); n++)
  { // warn the printer we're about to send graphics data
    fprintf(tdprn, "%c*b%dW", (char)27, 80);
    for (int k = 0; k < 80; k++)
    { octet datum = 0;
      for (int m = 0; m < 8; m++)
      { if (getpixel(k * 8 + m, n)) then
        { octet mask = '\001';
          mask = mask << (7 - m);
          datum = datum | mask;
        }
      }
      fputc(datum, tdprn);
    }
  }
  fprintf(tdprn, "%c*rB", (char)27);
  fputc(12, tdprn);
  fseek(tdprn, 0, 2);
  long filesize = ftell(tdprn);
  fseek(tdprn, 0, 0);
  char c;
  for (long s = 0; s < filesize; s++)
  { char c = fgetc(tdprn); 
// if we test the status of the following write, I always get a 16,
// which indicates an I/O error!
    _bios_printer(_PRINTER_WRITE, 0, c);
  }
}

