/*
	Converts between ASCII and Flexowriter code.

	(C) Copyright 2001 by Mogens Kjaer


   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <math.h>

unsigned char flx2a(char c,int *uppercase)
{
  if(c==64) return '\n';
  if(c>64) return 255;

  switch(c+64*(*uppercase))
  {
    case   0: return ' ';
    case   1: return '1';
    case   2: return '2';
    case   3: return '3';
    case   4: return '4';
    case   5: return '5';
    case   6: return '6';
    case   7: return '7';
    case   8: return '8';
    case   9: return '9';
    case  13: return 0xe5; /* å */
    case  14: return '_';
    case  16: return '0';
    case  17: return '<';
    case  18: return 's';
    case  19: return 't';
    case  20: return 'u';
    case  21: return 'v';
    case  22: return 'w';
    case  23: return 'x';
    case  24: return 'y';
    case  25: return 'z';
    case  27: return ',';
    case  30: /* tabulator */ break;
    case  32: return '-';
    case  33: return 'j';
    case  34: return 'k';
    case  35: return 'l';
    case  36: return 'm';
    case  37: return 'n';
    case  38: return 'o';
    case  39: return 'p';
    case  40: return 'q';
    case  41: return 'r';
    case  43: return 0xf8; /* ø */
    case  48: return 0xe6; /* æ */
    case  49: return 'a';
    case  50: return 'b';
    case  51: return 'c';
    case  52: return 'd';
    case  53: return 'e';
    case  54: return 'f';
    case  55: return 'g';
    case  56: return 'h';
    case  57: return 'i';
    case  58: *uppercase=0; break;
    case  59: return '.';
    case  60: *uppercase=1; break;
    case  64: return ' ';
    case  65: return 0xa3; /* £ */
    case  66: return '*';
    case  67: return '/';
    case  68: return '=';
    case  69: return ';';
    case  70: return '[';
    case  71: return ']';
    case  72: return '(';
    case  73: return ')';
    case  77: return 0xc5; /* Å */
    case  78: return '|';
    case  80: return '&';
    case  81: return '>';
    case  82: return 'S';
    case  83: return 'T';
    case  84: return 'U';
    case  85: return 'V';
    case  86: return 'W';
    case  87: return 'X';
    case  88: return 'Y';
    case  89: return 'Z';
    case  91: return '\'';
    case  94: /* tabulator */ break;
    case  96: return '+';
    case  97: return 'J';
    case  98: return 'K';
    case  99: return 'L';
    case 100: return 'M';
    case 101: return 'N';
    case 102: return 'O';
    case 103: return 'P';
    case 104: return 'Q';
    case 105: return 'R';
    case 107: return 0xd8; /* Ø */
    case 112: return 0xc6; /* Æ */
    case 113: return 'A';
    case 114: return 'B';
    case 115: return 'C';
    case 116: return 'D';
    case 117: return 'E';
    case 118: return 'F';
    case 119: return 'G';
    case 120: return 'H';
    case 121: return 'I';
    case 122: *uppercase=0; break;
    case 123: return ':';
    case 124: *uppercase=1; break;
    default: break;
  }
  return 255;
}

unsigned char a2flx(unsigned char c, int *uppercase)
{
  switch(c)
  {
    case ' ': return 0;
    case '1': *uppercase=0; return 1;
    case '2': *uppercase=0; return 2;
    case '3': *uppercase=0; return 3;
    case '4': *uppercase=0; return 4;
    case '5': *uppercase=0; return 5;
    case '6': *uppercase=0; return 6;
    case '7': *uppercase=0; return 7;
    case '8': *uppercase=0; return 8;
    case '9': *uppercase=0; return 9;
    case 0xe5: *uppercase=0; return 13; /* å */
    case '_': *uppercase=0; return 14;
    case '0': *uppercase=0; return 16;
    case '<': *uppercase=0; return 17;
    case 's': *uppercase=0; return 18;
    case 't': *uppercase=0; return 19;
    case 'u': *uppercase=0; return 20;
    case 'v': *uppercase=0; return 21;
    case 'w': *uppercase=0; return 22;
    case 'x': *uppercase=0; return 23;
    case 'y': *uppercase=0; return 24;
    case 'z': *uppercase=0; return 25;
    case ',': *uppercase=0; return 27;
    case '\t': return 30;
    case '-': *uppercase=0; return 32;
    case 'j': *uppercase=0; return 33;
    case 'k': *uppercase=0; return 34;
    case 'l': *uppercase=0; return 35;
    case 'm': *uppercase=0; return 36;
    case 'n': *uppercase=0; return 37;
    case 'o': *uppercase=0; return 38;
    case 'p': *uppercase=0; return 39;
    case 'q': *uppercase=0; return 40;
    case 'r': *uppercase=0; return 41;
    case 0xf8: *uppercase=0; return 43; /* ø */
    case 0xe6: *uppercase=0; return 48; /* æ */
    case 'a': *uppercase=0; return 49;
    case 'b': *uppercase=0; return 50;
    case 'c': *uppercase=0; return 51;
    case 'd': *uppercase=0; return 52;
    case 'e': *uppercase=0; return 53;
    case 'f': *uppercase=0; return 54;
    case 'g': *uppercase=0; return 55;
    case 'h': *uppercase=0; return 56;
    case 'i': *uppercase=0; return 57;
    case '.': *uppercase=0; return 59;
    case '\n': return 64;
    case '\r': return 64;
    case 0xa3: *uppercase=1; return 1; /* £ */
    case '*': *uppercase=1; return 2;
    case '/': *uppercase=1; return 3;
    case '=': *uppercase=1; return 4;
    case ';': *uppercase=1; return 5;
    case '[': *uppercase=1; return 6;
    case ']': *uppercase=1; return 7;
    case '(': *uppercase=1; return 8;
    case ')': *uppercase=1; return 9;
    case 0xc5: *uppercase=1; return 13; /* Å */
    case '|': *uppercase=1; return 14;
    case '&': *uppercase=1; return 16;
    case '>': *uppercase=1; return 17;
    case 'S': *uppercase=1; return 18;
    case 'T': *uppercase=1; return 19;
    case 'U': *uppercase=1; return 20;
    case 'V': *uppercase=1; return 21;
    case 'W': *uppercase=1; return 22;
    case 'X': *uppercase=1; return 23;
    case 'Y': *uppercase=1; return 24;
    case 'Z': *uppercase=1; return 25;
    case '\'': *uppercase=1; return 27;
    case '+': *uppercase=1; return 32;
    case 'J': *uppercase=1; return 33;
    case 'K': *uppercase=1; return 34;
    case 'L': *uppercase=1; return 35;
    case 'M': *uppercase=1; return 36;
    case 'N': *uppercase=1; return 37;
    case 'O': *uppercase=1; return 38;
    case 'P': *uppercase=1; return 39;
    case 'Q': *uppercase=1; return 40;
    case 'R': *uppercase=1; return 41;
    case 0xd8: *uppercase=1; return 43; /* Ø */
    case 0xc6: *uppercase=1; return 48; /* Æ */
    case 'A': *uppercase=1; return 49;
    case 'B': *uppercase=1; return 50;
    case 'C': *uppercase=1; return 51;
    case 'D': *uppercase=1; return 52;
    case 'E': *uppercase=1; return 53;
    case 'F': *uppercase=1; return 54;
    case 'G': *uppercase=1; return 55;
    case 'H': *uppercase=1; return 56;
    case 'I': *uppercase=1; return 57;
    case ':': *uppercase=1; return 59;
    default:
      break;
  }
  return 0;
}

unsigned char set_parity(unsigned char c)
{
  int i;
  int ones;
  unsigned char mask;

  ones=0;

  mask = 1;

  for(i=0; i<7; i++)
  {
    if(c&mask) ones++;
    mask = mask<<1;
  }

  return ((c&0x70)<<1) | (((ones&1)==0)?0x10:0x00) | (c&0x0f);
}

unsigned char remove_parity(unsigned char c)
{
  return ((c&0xe0)>>1) | (c&0x0f);
}

int check_parity(unsigned char c)
{
  int ones;
  int i;
  unsigned char mask;

  ones=0;
  mask=1;

  for(i=0; i<8; i++)
  {
    if(c&mask) ones++;
    mask = mask<<1;
  }

  return ones&1;
}

/*
	Code for conversion from Flexowriter to
	PostScript:
*/

static int lpi=6, lines=72;

static double leftmargin, rightmargin, topmargin, bottommargin, rightshift, currentleftmargin;
int userightshift=0;
static double fontsize;
static double last_fontsize=-1;
static double last_fonttype=-1; /* 0: courier, 1: symbol */
static double x,y;
static int uadvance=0;
static int pagecount;
static int lineno;
static FILE *fh=NULL;
static int use_stdout;
static int ps_uppercase;
static int use_landscape;

/* overprint to bold: */
#define BOLDLEN 1024
static unsigned char boldbuf[BOLDLEN];
static int tabmode=0;


static void ps_endpage()
{
  fprintf(fh, "showpage\n");
}

static void clearbold()
{
  int i;
  for(i=0;i<BOLDLEN;i++) boldbuf[i]=' ';
}

static void ps_newpage()
{
  if(pagecount != 0)
  {
    ps_endpage();
  }
  pagecount++;
  fprintf(fh, "%%%%Page %d\n", pagecount);
  currentleftmargin=leftmargin;
  if(pagecount % 2==1)
  {
    currentleftmargin+=rightshift;
  }
  else
  {
    currentleftmargin-=rightshift;
  }
  x=currentleftmargin;
  y=topmargin;
  lineno=1;
  if(use_landscape)
    fprintf(fh, "0 842 translate -90 rotate\n");
  clearbold();
}

int ps_open(char *filename, int letter, double use_fontsize, int use_lpi, int use_lines, int use_uadvance, int landscape)
{
  use_stdout = filename == NULL;
  use_landscape = landscape;

  if(use_stdout)
  {
    fh=stdout;
  }
  else
  {
    fh=fopen(filename, "w");
  }
  if(fh == NULL)
  {
    fprintf(stderr, "Error in open of %s\n", filename);
    return 0;
  }
  ps_uppercase=0;
  fontsize=use_fontsize;
  lpi=use_lpi;
  lines=use_lines;
  uadvance=use_uadvance;
  last_fontsize=-1;
  last_fonttype=-1;

  fprintf(fh, "%%!PS-Adobe-3.0\n");
  fprintf(fh, "%%%%Creator: flx2ps\n");
  fprintf(fh, "%%%%DocumentNeededResources: \n");
  fprintf(fh, "%%%%+ font Courier\n");
  fprintf(fh, "%%%%+ font Courier-Bold\n");
  fprintf(fh, "%%%%+ font Symbol\n");
  fprintf(fh, "%%%%Pages: (atend)\n");
  if(letter)
  {
    fprintf(fh, "%%%%DocumentMedia: Letter 612 792 0 white ()\n");
  }
  else
  {
    fprintf(fh, "%%%%DocumentMedia: A4 595 842 0 white ()\n");
  }
  fprintf(fh, "%%%%BoundingBox: %g %g %g %g\n", leftmargin, bottommargin, rightmargin, topmargin);
  if(use_landscape)
  {
    fprintf(fh, "%%%%Orientation: Landscape\n");
  }
  else
  {
    fprintf(fh, "%%%%Orientation: Portrait\n");
  }
  fprintf(fh, "%%%%EndComments\n\n");

  if(letter)
  {
    fprintf(fh, "[{\n");
    fprintf(fh, "%%%%BeginFeature: *PageSize Letter\n");
    fprintf(fh, "<</PageSize[612 792]/ImagingBBox null>>setpagedevice\n");
    fprintf(fh, "%%%%EndFeature\n");
    fprintf(fh, "} stopped cleartomark\n");
  }
  else
  {
    fprintf(fh, "[{\n");
    fprintf(fh, "%%%%BeginFeature: *PageSize A4\n");
    fprintf(fh, "<</PageSize[595 842]/ImagingBBox null>>setpagedevice\n");
    fprintf(fh, "%%%%EndFeature\n");
    fprintf(fh, "} stopped cleartomark\n");
  }
  fprintf(fh, "mark\n");
  fprintf(fh, "/ISOLatin1Encoding \n");
  fprintf(fh, "8#000 1 8#054 {StandardEncoding exch get} for \n");
  fprintf(fh, "  /minus \n");
  fprintf(fh, "  8#056 1 8#217 {StandardEncoding exch get} for \n");
  fprintf(fh, "  /dotlessi \n");
  fprintf(fh, "  8#301 1 8#317 {StandardEncoding exch get} for \n");
  fprintf(fh, "  /space /exclamdown /cent /sterling /currency /yen /brokenbar /section \n");
  fprintf(fh, "  /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen \n");
  fprintf(fh, "  /registered /macron /degree /plusminus /twosuperior /threesuperior /acute \n");
  fprintf(fh, "  /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine \n");
  fprintf(fh, "  /guillemotright /onequarter /onehalf /threequarters /questiondown /Agrave \n");
  fprintf(fh, "  /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute\n");
  fprintf(fh, "  /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde\n");
  fprintf(fh, "  /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave \n");
  fprintf(fh, "  /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute \n");
  fprintf(fh, "  /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute \n");
  fprintf(fh, "  /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde\n");
  fprintf(fh, "  /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave \n");
  fprintf(fh, "  /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis \n");
  fprintf(fh, "  /ISOLatin1Encoding where not {256 array astore def} if \n");
  fprintf(fh, " cleartomark \n");
  fprintf(fh, "/makeISOEncoded \n");
  fprintf(fh, "{ findfont /curfont exch def \n");
  fprintf(fh, "  /newfont curfont maxlength dict def  \n");
  fprintf(fh, "  /ISOLatin1 (-ISOLatin1) def\n");
  fprintf(fh, "  /curfontname curfont /FontName get dup length string cvs def \n");
  fprintf(fh, "  /newfontname curfontname length ISOLatin1 length add string \n");
  fprintf(fh, " dup 0                  curfontname putinterval \n");
  fprintf(fh, " dup curfontname length ISOLatin1   putinterval \n");
  fprintf(fh, "  def \n");
  fprintf(fh, "  curfont   \n");
  fprintf(fh, "  { exch dup /FID ne  \n");
  fprintf(fh, "{ dup /Encoding eq  \n");
  fprintf(fh, "  { exch pop ISOLatin1Encoding exch }  \n");
  fprintf(fh, "  if  \n");
  fprintf(fh, "  dup /FontName eq  \n");
  fprintf(fh, "  { exch pop newfontname exch }  \n");
  fprintf(fh, "  if  \n");
  fprintf(fh, "  exch newfont 3 1 roll put  \n");
  fprintf(fh, "}  \n");
  fprintf(fh, "{ pop pop }  \n");
  fprintf(fh, "ifelse  \n");
  fprintf(fh, "  }  \n");
  fprintf(fh, "  forall \n");
  fprintf(fh, "  newfontname newfont definefont \n");
  fprintf(fh, "} def \n");
  fprintf(fh, "/Courier makeISOEncoded pop \n");
  fprintf(fh, "/Courier-Bold makeISOEncoded pop \n");

  if(use_landscape)
  {
    if(letter)
    {
      leftmargin=36;
      if(userightshift)
	rightshift=18;
      else
	rightshift=0;
      rightmargin=792-36;
      topmargin=612-36;
      bottommargin=36;
    }
    else
    {
      leftmargin=36;
      if(userightshift)
	rightshift=18;
      else
	rightshift=0;
      rightmargin=842-36;
      topmargin=595-36;
      bottommargin=36;
    }
  }
  else
  {
    if(letter)
    {
      leftmargin=36;
      if(userightshift)
	rightshift=18;
      else
	rightshift=0;
      rightmargin=612-36;
      topmargin=792-36;
      bottommargin=36;
    }
    else
    {
      leftmargin=36;
      if(userightshift)
	rightshift=18;
      else
	rightshift=0;
      rightmargin=595-36;
      topmargin=842-36;
      bottommargin=36;
    }
  }
  pagecount=0;
  currentleftmargin=leftmargin+rightshift;
  ps_newpage();

  return 1;
}

void ps_close()
{
  ps_endpage();
  fprintf(fh, "%%%%Pages: %d\n", pagecount);
  fprintf(fh, "%%%%EOF\n%c", (char) 4);
  if(!use_stdout) fclose(fh);
}

static void printchar(int fonttype, double fsize, unsigned char c)
{
  if(c!=' ')
  {
    if(fonttype!=last_fonttype || fsize!=last_fontsize)
    {
      fprintf(fh, "/%s findfont %g scalefont setfont\n", 
	  fonttype==0?"Courier-ISOLatin1":(fonttype==1?"Symbol":"Courier-Bold-ISOLatin1"), fsize);
      last_fonttype=fonttype;
      last_fontsize=fsize;
    }
    fprintf(fh, "%.2f %.2f moveto ", x, y);
    if(c==')' || c=='(')
    {
      fprintf(fh, "(\\%c) show\n", c);
    }
    else
    {
      fprintf(fh, "(%c) show\n", c);
    }
  }
}

void ps_SY(unsigned char c1)
{
  unsigned char c2;
  int ichar,dobold;

  if(tabmode)
  {
    if(c1>0) printf("lineprinter: non-zero tab: %d\n",c1);
    x = currentleftmargin+(c1*2)*72.0/120.0*fontsize;
    tabmode=0;
  }
  else if(c1==64)
  {
    x=currentleftmargin;
    y=y-72.0/lpi;
    lineno++;
    if(lineno>lines) ps_newpage();
    clearbold();
  }
  else if(c1==72 || c1==66)
  {
    /* Don't write out blank pages */
    if(lineno>1 || x>currentleftmargin) ps_newpage();
  }
  else if(c1==80)
  {
    x=currentleftmargin;
  }
  else if(c1==30)
  {
    tabmode=1;
  }
  else if(c1==29)
  {
    fprintf(fh, "1 0 0 setrgbcolor\n");
  }
  else if(c1==62)
  {
    fprintf(fh, "0 0 0 setrgbcolor\n");
  }
  else
  {
    c2=flx2a(c1, &ps_uppercase);
    if(c1 == 44)
    {
      c2='*';
    }
    if(c2 == 255)
    {
    }
    else
    {
      if(c2=='\'')
      {
	/* subscript 10 */
	y = y-72.0/lpi/6.0;
	printchar(0, fontsize*0.5, '1');
	x = x + 72.0/120.0*fontsize*0.5;
	printchar(0, fontsize*0.5, '0');
	x = x - 72.0/120.0*fontsize*0.5;
	y = y+72.0/lpi/6.0;
      }
      else if(c2==(unsigned char)0xa3) /* £ */
      {
	printchar(1, fontsize, 218);
      }
      else if(c1==2 && ps_uppercase)
      {
	printchar(1, fontsize, 180);
      }
      else if(c2=='&')
      {
	printchar(1, fontsize, 217);
      }
      else
      {
	ichar=round((x-currentleftmargin)/(72.0/120.0*fontsize));
	dobold=0;
	if(ichar>=0 && ichar<BOLDLEN) dobold=(boldbuf[ichar]==c2)&&(c2!=' ');
/*	if(dobold) printf("BOLD: '%c'\n", c2); */
	printchar(dobold?2:0, fontsize, c2);
	if(ichar>=0 && ichar<BOLDLEN) boldbuf[ichar]=c2;
      }
      if((c2!='_' && c2!='|')||uadvance)
      {
	x = x + 72.0/120.0*fontsize;
      }
    }
  }
}

