# include "Ximage.h"
# include "alphabet.h"

# define NROT 256
# define XPROC(x,y) (scale*(cs*((x) - x0) - sn*((y) - y0)) + X0)
# define YPROC(x,y) (scale*(cs*((y) - y0) + sn*((x) - x0)) + Y0)
# define NEARINT(x) ((x < 0) ? ((int)(x - 0.5)) : ((int)(x + 0.5)))
  
int DrawRotText (int x, int y, char *string, int pos, double angle) {

  char *c, *bitmap, basefont[64];
  int i, dy, dx, N, X, Y, X1, Y1, code;
  int dX, Xoff, dY, Yoff, YoffBase, basesize;
  double cs, sn;

  /* strip leading whitespace */
  for (c = string; (*c == ' ') || (*c == '\t'); c++);
  if (*c == 0) return;
  
  /* compute string length */
  cs = cos(angle*RAD_DEG);
  sn = sin(angle*RAD_DEG);
  dX = RotStrlen (c);
  dY = activefont[65].ascent;

  /* apply appropriate offset */
  switch (pos) {
  case 0: Xoff =     -dX; Yoff = dY; break;
  case 1: Xoff = -0.5*dX; Yoff = dY; break;
  case 2: Xoff =       0; Yoff = dY; break;
  case 3: Xoff =     -dX; Yoff = 0.5*dY; break;
  case 4: Xoff = -0.5*dX; Yoff = 0.5*dY; break;
  case 5: Xoff =       0; Yoff = 0.5*dY; break;
  case 6: Xoff =     -dX; Yoff = 0; break;
  case 7: Xoff = -0.5*dX; Yoff = 0; break;
  case 8: Xoff =       0; Yoff = 0; break;
  }

  code = FALSE;

  strcpy (basefont, currentname);
  basesize = currentsize;

  YoffBase = Yoff;
  /* draw characters one-by-one */
  for (i = 0; i < strlen(c); i++) {
    N = (int)(c[i]);
    if ((N < 0) || (N >= NROT)) continue;

    /* check for special characters */
    if (!code) {
      if (N == 94) {
	SetRotFont (currentname, (int)(0.8*currentsize));
	Yoff -= 0.5*activescale*dY;
	continue;
      }
      if (N == 95) { 
	SetRotFont (currentname, (int)(0.8*currentsize));
	Yoff += 0.5*activescale*dY;
	continue;
      }
      if (N == 124) {
	SetRotFont (currentname, basesize);
	Yoff = YoffBase;
	continue;
      }
      if (N == 92) {
	code = TRUE;
	continue;
      } 
      if (N == 38) {
	if (c[i+1] == 'h') {
	  SetRotFont ("helvetica", currentsize);
	}
	if (c[i+1] == 't') {
	  SetRotFont ("times", currentsize);
	}
	if (c[i+1] == 'c') {
	  SetRotFont ("courier", currentsize);
	}
	if (c[i+1] == 's') {
	  SetRotFont ("symbol", currentsize);
	}
	i++;
	continue;
      }
    }
    code = FALSE;

    bitmap = activefont[N].bits;
    dx = activefont[N].dx;
    dy = activefont[N].dy;
    X = x + (int)(Xoff*cs - Yoff*sn) + (int)(activescale*activefont[N].ascent*sn);
    Y = y + (int)(Xoff*sn + Yoff*cs) - (int)(activescale*activefont[N].ascent*cs);
    DrawRotBitmap (X, Y, dx, dy, bitmap, TRUE, angle, activescale);
    Xoff += 1 + (int)(activescale*dx + 0.5);
  }
  SetRotFont (basefont, basesize);
}

int DrawRotBitmap (x, y, dx, dy, bitmap, mode, angle, scale)
int         x, y, dx, dy;
char        bitmap[];
int         mode;
double      angle, scale;
{

  int ii, jj, byte_line, byte, bit, flag;
  unsigned long int fore, back;
  double i, j, cs, sn, rscale, tmp;
  int Xt, Yt, X, Y, X0, X1, X2, Y0, Y1, Y2, x0, y0;

  if (mode) {
    fore = graphic.fore;
    back = graphic.back;
  } else {
    fore = graphic.back;
    back = graphic.fore;
  } 
    
  byte_line = (int) ((dx + 7) / 8);

  cs = cos(angle*RAD_DEG);  sn = sin(angle*RAD_DEG);
  rscale = 1.0 / scale;

  X0 = 0;
  Y0 = 0;
  x0 = 0;
  y0 = 0;

  X2 = X1 = XPROC (0,0);
  Y2 = Y1 = YPROC (0,0);

  X = XPROC (dx,0);
  Y = YPROC (dx,0);
# ifdef DRAWBOXES
  XDrawLine (graphic.display, graphic.window, graphic.gc, x+X, y+Y, x+X1, y+Y1);
  Xt = X;
  Yt = Y;
# endif
  X1 = MIN (X, X1);
  X2 = MAX (X, X2);
  Y1 = MIN (Y, Y1);
  Y2 = MAX (Y, Y2);

  X = XPROC (dx,dy);
  Y = YPROC (dx,dy);
# ifdef DRAWBOXES
  XDrawLine (graphic.display, graphic.window, graphic.gc, x+X, y+Y, x+Xt, y+Yt);
  Xt = X;
  Yt = Y;
# endif
  Y1 = MIN (Y, Y1);
  Y2 = MAX (Y, Y2);
  X1 = MIN (X, X1);
  X2 = MAX (X, X2);

  X = XPROC (0,dy);
  Y = YPROC (0,dy);
# ifdef DRAWBOXES
  XDrawLine (graphic.display, graphic.window, graphic.gc, x+X, y+Y, x+Xt, y+Yt);
  Xt = X;
  Yt = Y;
# endif
  Y1 = MIN (Y, Y1);
  Y2 = MAX (Y, Y2);
  X1 = MIN (X, X1);
  X2 = MAX (X, X2);

  XSetForeground (graphic.display, graphic.gc, fore);
  if (scale > 1) {
    for (i = X1; i <= X2; i+=1) {
      for (j = Y1; j <= Y2; j+=1) {
	tmp = rscale*(cs*(i - X0) + sn*(j - Y0)) + x0;  ii = NEARINT (tmp);
	tmp = rscale*(cs*(j - Y0) - sn*(i - X0)) + y0;  jj = NEARINT (tmp);
	/* fprintf (stderr, "%d %d  %d %d\n", i, j, ii, jj);  */
	if ((ii < 0) || (ii >= dx) || (jj < 0) || (jj >= dy)) continue;
	byte = byte_line * jj + (ii / 8);
	bit = ii % 8;
	flag = 0x01 & (bitmap[byte] >> bit);
	if (flag) XDrawPoint (graphic.display, graphic.window, graphic.gc, x + i, y + j);
      }
    }
  } else {
    for (i = X1; i <= X2; i+=scale) {
      for (j = Y1; j <= Y2; j+=scale) {
	tmp = rscale*(cs*(i - X0) + sn*(j - Y0)) + x0;  ii = NEARINT (tmp);
	tmp = rscale*(cs*(j - Y0) - sn*(i - X0)) + y0;  jj = NEARINT (tmp);
	/* fprintf (stderr, "%d %d  %d %d\n", i, j, ii, jj);  */
	if ((ii < 0) || (ii >= dx) || (jj < 0) || (jj >= dy)) continue;
	byte = byte_line * jj + (ii / 8);
	bit = ii % 8;
	flag = 0x01 & (bitmap[byte] >> bit);
	/* fprintf (stderr, "%2d %2d  %3d %3d  %1d  %f  %f\n", i, j, ii, jj, flag,
	   rscale*(cs*(i - X0) + sn*(j - Y0)) + x0, rscale*(cs*(j - Y0) - sn*(i - X0)) + y0);  */
	if (flag) XDrawPoint (graphic.display, graphic.window, graphic.gc, x + i, y + j);
      }
    }
# if (0)
    for (i = 0; i < dx; i++) {
      for (j = 0; j < dy; j++) {
	tmp = scale*(cs*(i - x0) - sn*(j - y0)) + X0; ii = NEARINT (tmp);
	tmp = scale*(cs*(j - y0) + sn*(i - x0)) + Y0; jj = NEARINT (tmp);
	/* if ((ii < 0) || (ii >= dx) || (jj < 0) || (jj >= dy)) continue; */
	byte = byte_line * j + (i / 8);
	bit = i % 8;
	flag = 0x01 & (bitmap[byte] >> bit);
	fprintf (stderr, "%2d %2d  %3d %3d  %1d  %f  %f\n", i, j, ii, jj, flag, 
		 scale*(cs*(i - x0) - sn*(j - y0)) + X0, scale*(cs*(j - y0) + sn*(i - x0)) + Y0); 
	if (flag) XDrawPoint (graphic.display, graphic.window, graphic.gc, x + ii, y + jj);
      }
    }
# endif
  }
  XSetForeground (graphic.display, graphic.gc, graphic.fore);
}

InitRotFonts () {

  int i, Nhardwired;

  Nhardwired = sizeof (HardwiredFonts) / sizeof (FontSet);
  
  Nallfonts = Nhardwired;
  ALLOCATE (AllFonts, FontSet, Nallfonts);
  
  for (i = 0; i < Nhardwired; i++) {
    AllFonts[i] = HardwiredFonts[i];
  }

  activefont = AllFonts[DEFFONT].font;
  activescale = 1.0;
  strcpy (currentname, AllFonts[DEFFONT].name);
  currentsize = AllFonts[DEFFONT].size;
}

int SetRotFont (char *name, int size) {
  
  int i, nsize, msize, bsize, bigger, dsize, match, good;

  bigger = good = match = -1;
  dsize = 10000;
  bsize = 10000;
  for (i = 0; i < Nallfonts; i++) {
    if (!strcasecmp (AllFonts[i].name, name)) {
      good = i;
      nsize = abs (AllFonts[i].size - size);
      if (nsize < dsize) {
	match = i;
	dsize = nsize;
      }
      msize = AllFonts[i].size - size;
      if ((msize < bsize) && (msize >= 0)) {
	bigger = i;
	bsize = msize;
      }
    }
  }
  
  if ((match == -1) && (good != -1)) match = good;
  if (bigger != -1) match = bigger;
  if (match != -1) {
    activefont = AllFonts[match].font;
    activescale = (double) size / AllFonts[match].size;
    currentsize = size;
    strcpy (currentname, name);
    return (TRUE);
  } else {
    fprintf (stderr, "no matching font\n");
    return (FALSE);
  }

}
  
char *GetRotFont (int *size) {

  *size = currentsize;
  return (currentname);

}

int RotStrlen (char *c) {

  int i, N, dX, code;
  double scale; 
  
  scale = activescale;

  /* find string length */
  dX = 0;

  code = FALSE;
  for (i = 0; i < strlen (c); i++) {
    N = (int)(c[i]);
    /* skip non-printing characters */
    if ((N < 0) || (N >= NROT)) continue;

    /* check for special characters */
    if (!code) {
      if (N == 94) { /* super-script */
	scale *= 0.8;
	continue;
      }
      if (N == 95) { /* sub-script */
	scale *= 0.8;
	continue;
      }
      if (N == 124) { /* normal-script */
	scale = activescale;
	continue;
      }
      if (N == 92) { /* backslash */
	code = TRUE;
	continue;
      } 
      if (N == 38) { /* font-code */
	i++;
	continue;
      }
    }
    code = FALSE;
    dX += scale*activefont[N].dx + 1;
  }
  return (dX);
}

/* writes commands to print string at location and angle using
   currently set font and size */
void PSRotText (FILE *f, int x, int y, char *string, int pos, double angle) {

  char *c, *segment, basefont[64];
  int i, N, code;
  int dX, dY, Xoff, Yoff, X, Y, Nseg, YoffBase;
  double cs, sn, fscale;
  int basesize;
    
  /* strip off leading whitespace */
  for (c = string; (*c == ' ') || (*c == '\t'); c++);
  if (*c == 0) return;
  
  /* compute string length */
  /* PS fonts are somewhat different scales from bitmap font equivalents */
  if (!strcmp (currentname, "times")) fscale = 1.07;
  if (!strcmp (currentname, "courier")) fscale = 1.5;
  if (!strcmp (currentname, "helvetica")) fscale = 0.9;
  if (!strcmp (currentname, "symbol")) fscale = 1.2;
  dX = fscale*RotStrlen (c);
  dY = activefont[65].ascent;
  
  /* apply appropriate offset */
  switch (pos) {
  case 0: Xoff =     -dX; Yoff = -dY; break;
  case 1: Xoff = -0.5*dX; Yoff = -dY; break;
  case 2: Xoff =       0; Yoff = -dY; break;
  case 3: Xoff =     -dX; Yoff = -0.5*dY; break;
  case 4: Xoff = -0.5*dX; Yoff = -0.5*dY; break;
  case 5: Xoff =       0; Yoff = -0.5*dY; break;
  case 6: Xoff =     -dX; Yoff = 0; break;
  case 7: Xoff = -0.5*dX; Yoff = 0; break;
  case 8: Xoff =       0; Yoff = 0; break;
  }
  cs = cos(angle*RAD_DEG);
  sn = sin(angle*RAD_DEG);
  X = x + Xoff*cs + Yoff*sn;
  Y = y - Xoff*sn + Yoff*cs;

  basesize = currentsize;
  strcpy (basefont, currentname);

  PSSetFont (f, currentsize);
  fprintf (f, "gsave\n");
  fprintf (f, " %d %d moveto %f rotate\n", X, Y, -angle);

  ALLOCATE (segment, char, strlen (c)+2);
  bzero (segment, strlen (c)+2);
  Nseg = 0;

  code = FALSE;
  YoffBase = 0;
  /* accumulate string segments with common state */
  for (i = 0; i < strlen (c); i++) {
    N = (int)(c[i]);
    if ((N < 0) || (N >= NROT)) continue;

    /* check for special characters */
    if (!code) {
      if (N == 94) {
	PSDumpRotSegment (f, segment, &Nseg);
	SetRotFont (currentname, (int)(0.8*currentsize));
	Yoff = 0.75*activescale*dY;
	fprintf (f, "0 %d rmoveto\n", Yoff);
	YoffBase += Yoff;
	PSSetFont (f, currentsize);
	continue;
      }
      if (N == 95) { 
	PSDumpRotSegment (f, segment, &Nseg);
	SetRotFont (currentname, (int)(0.8*currentsize));
	Yoff = -0.5*activescale*dY;
	fprintf (f, "0 %d rmoveto\n", Yoff);
	YoffBase += Yoff;
	PSSetFont (f, currentsize);
	continue;
      }
      if (N == 124) {
	PSDumpRotSegment (f, segment, &Nseg);
	SetRotFont (currentname, basesize);
	fprintf (f, "0 %d rmoveto\n", -YoffBase);
	YoffBase = 0;
	PSSetFont (f, currentsize);
	continue;
      }
      if (N == 92) {
	code = TRUE;
	continue;
      } 
      if (N == 38) {
	PSDumpRotSegment (f, segment, &Nseg);
	if (c[i+1] == 'h') {
	  SetRotFont ("helvetica", currentsize);
	  PSSetFont (f, currentsize);
	}
	if (c[i+1] == 't') {
	  SetRotFont ("times", currentsize);
	  PSSetFont (f, currentsize);
	}
	if (c[i+1] == 'c') {
	  SetRotFont ("courier", currentsize);
	  PSSetFont (f, currentsize);
	}
	if (c[i+1] == 's') {
	  SetRotFont ("symbol", currentsize);
	  PSSetFont (f, currentsize);
	}
	i++;
	continue;
      }
    }
    code = FALSE;
    segment[Nseg] = N;
    Nseg ++;
  }
  PSDumpRotSegment (f, segment, &Nseg);
  fprintf (f, "stroke grestore\n");
  free (segment);
  SetRotFont (basefont, basesize);
}


PSDumpRotSegment (FILE *f, char *segment, int *Nseg) {
  fprintf (f, "(%s) show\n", segment);
  bzero (segment, *Nseg);
  *Nseg = 0;
}

PSSetFont (FILE *f, int size) {
  if (!strcmp (currentname, "times")) 
    fprintf (f, "/Times-Roman findfont %d scalefont setfont\n", size);
  if (!strcmp (currentname, "helvetica")) 
    fprintf (f, "/Helvetica findfont %d scalefont setfont\n", size);
  if (!strcmp (currentname, "courier")) 
    fprintf (f, "/Courier findfont %d scalefont setfont\n", size);
  if (!strcmp (currentname, "symbol")) 
    fprintf (f, "/Symbol findfont %d scalefont setfont\n", size);
}



  /* test cross hair
  fprintf (f, "%d %d %d %d L\n", x-10, y, x+10, y);
  fprintf (f, "%d %d %d %d L\n", x, y-10, x, y+10);
  */
