/* 
 * Copyright 1994 Chris Smith
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appears in all copies and that
 * both that copyright notice and this permission notice appears in
 * supporting documentation.  I make no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 */

/* Here we build a bit map of one page.   The bits are stored in column
   major order, in words, bit big endian.  I.e. the first word contains
   the first 32 bits of the leftmost column, top bit in the most significant
   bit of the word.  This order is what the Bubblejet wants, and what
   pxl format provides, but care is needed on this godforsaken little-endian
   machine.

   The coordinate system depends on the -landscape flag.  For portrait
   mode, the origin is at the top left corner of the paper with Y
   increasing down the page and X increasing left to right.  For
   landscape the origin is at the top right, with X increasing down
   the page and Y increasing right to left.  Coordinates for setting
   rules and chars are transformed appropriately here.  Character bit
   maps are transformed by the caller, once, when the char is first pulled
   from the font file.

   Character glyphs are supplied in basically pxl format, but in
   column major order -- scan lines padded to a word boundary, the
   first scan line being the left column.  (For landscape, this
   means the first scan line is the bottom row.)  */

#include "pxl.h"
#include "bj200dev.h"
#include "bj200b.h"
#include <signal.h>
#include <unistd.h>

static int LFlag;
static void sigint (int signo);

/* flag true when a page is actually being printed */
static volatile sig_atomic_t in_page;

/* the bit map */

unsigned long page[NW * NWCOL];

/* max printable Y, in dots */

int YN;

/* initialize */

void lp_init (int is_landscape, int dpi, float paper_length)
{
  LFlag = is_landscape;
  YN = 0.5 + 360 * (paper_length - BJ200_BOTTOM_MARGIN);

  bj_init ();
  signal (SIGINT, sigint);
}

/* all done */

void lp_eof ()
{
  bj_eof ();
}

/* initialize for new page */

void lp_beginpage ()
{
  bzero (page, sizeof page);
}

/* page output complete, send to printer */

void lp_endpage ()
{
  in_page = 1;
  bj_printpage (page);
  in_page = 0;
}

/* draw a rule, bottom left at X,Y, width and height W,H */

static void lp_drawrule_1 (int x, int y, int w, int h, int draft);

void lp_drawrule (int x, int y, int w, int h, int draft)
{
  if (LFlag)
    lp_drawrule_1 (NW - y, x + w - 1, h, w, draft);
  else
    lp_drawrule_1 (x, y, w, h, draft);
}

static void lp_drawrule_1 (int x, int y, int w, int h, int draft)
{
  unsigned long *pg;
  unsigned long lbits, rbits, screen, compl;
  int t, nsh, nmid;
 
  /* clip to page */
  if (y - h + 1 < Y0)
    h = y - Y0 - 1;
  if (y >= YN)
    h -= y - YN + 1, y = YN - 1;
  if (h <= 0) return;

  if (x < X0)
    w -= X0 - x, x = X0;
  if (x + w > XN)
    w = XN - x;
  if (w <= 0) return;

  /* get top left corner of rule */
  y -= h - 1;
  
  /* scan across W columns, setting a string of bits H long starting at Y. 
  ---|- y ->           |                |                |
     |      <----------|---- h ---------|---------->     |
     |      <--lbits-->|<--nmid words-->|<--rbits-->     |
   */

  /* point to first word to change */
  pg = &page[x * NWCOL + (y >> 5)];

  /* get # of leading zeros in left word */
  nsh = y & 31;

  /* get bits to or in, left piece */
  lbits = (unsigned long) -1 >> nsh;
  /* number of excess bits in left piece */
  t = 32 - nsh - h;
  if (t > 0) {
    lbits >>= t;
    lbits <<= t;
    nmid = rbits = 0;
  } else {
    /* number of bits in right piece */
    t = (y + h) & 31;
    /* bits to or in, right piece */
    rbits = ~((unsigned long) -1 >> t);
    /* there are t bits in the right piece and 32-nsh in the left piece */
    nmid = (h - t - (32 - nsh)) >> 5;
  }
  
  if (draft)
    screen = 0x55555555, compl = -1;
  else
    screen = -1, compl = 0;

  if (nmid == 0)
    for ( ; w != 0; w--)
      {
	pg[0] |= lbits & screen;
	pg[1] |= rbits & screen;
	pg += NWCOL;
	screen ^= compl;
      }
  else
    for ( ; w != 0; w--)
      {
	pg[0] |= lbits & screen;
	for (t = nmid; t != 0; t--)
	  pg[1] = screen, pg++;
	pg[1] |= rbits & screen;
	pg += NWCOL - nmid;
	screen ^= compl;
      }
}

/* draw a character glyph, reference point at x,y */

static void lp_drawchar_1 (int x, int y, struct chinfo *ch);

void lp_drawchar (int x, int y, struct chinfo *ch)
{
  if (LFlag)
    lp_drawchar_1 (NW - y, x, ch);
  else
    lp_drawchar_1 (x, y, ch);
}

static void lp_drawchar_1 (int x, int y, struct chinfo *ch)
{
  unsigned wd = ch->ch_width;
  unsigned ht = ch->ch_height;
  unsigned long *p = ch->ch_raster;
  unsigned long *pg;
  unsigned i, j, lsh, rsh, noffs;

  /* Move reference point to top left */

  x -= ch->ch_xoffset;
  y -= ch->ch_yoffset;

  /* Punt if off page */
  if (x < 0 || x + wd > NW)
    return;
  if (y < 0 || y + ht > NH)
    return;

  /* move up to a word boundary, convert ht from bits to words */

  rsh = y & 31;
  ht = (ht + 31) >> 5;
  
  /* point to first word to be modified */
  pg = &page[x * NWCOL + (y >> 5)];

  /* get offset from the last word in one column to the
     first word in the next */
  noffs = NWCOL - ht;

  /* or the char's bits into the page bitmap */

  if (rsh == 0)
    {
      for (i = wd; i > 0; i--) {
	for (j = ht; j > 0; j--)
	  *pg++ |= *p++;
	pg += noffs;
      }
    }
  else
    {
      lsh = 32 - rsh;
      for (i = wd; i > 0; i--) {
	for (j = ht; j > 0; j--) {
	  unsigned long word = *p++;
	  pg[0] |= word >> rsh;
	  pg[1] |= word << lsh;
	  pg++;
	}
	pg += noffs;
      }
    }
}

/* Here if lpd aborts the job (because of, e.g., lprm).  Eject any partially
   printed page and reset the printer before exiting. */

void sigint (int signo)
{
  int n;

  if (in_page)
    {
      char nulls[256];
      bzero (nulls, sizeof nulls);
      
      /* send a bunch of nulls to complete any partial escape sequence.
	 The longest sensible graphics string has 6*2880 bytes. */
      for (n = 0; n < 6 * 2880; n += 256)
	write (STDOUT_FILENO, nulls, sizeof nulls);

      /* send ^X to flush the buffer in the printer, \f to eject the page */
      write (STDOUT_FILENO, "\030\f", 2);
    }

  /* magic reset string -- all settings to DIP switch defaults */
  write (STDOUT_FILENO, "\033[K\004\000\001\044\200\200", 9);

  /* exit, do not flush stdout */
  _exit (0);
}
