// cc -o edwin -g edwin4.c -I ../.. ../../perms.c -lm
#include <perms.h>

int Uinfi(int Parm) {
  return 0;
}
void Console(int Fn, int *A, int *L) {
  //fprintf(stderr, "Console: %d %d %d\n", Fn, *A, *L);
}
void Messages(_imp_string State) { // "ON" or "OFF"
}

static const int True = 0;
static const int False = 1;
#include "config.h"
#include "utility.h"
#include "charspec.h"

#define Off  (-1)
static int Storing = Off;
static int Vis = 0;
static const int Ra[4 /*0:3*/] = {0, 5, 3, 6};
static int Attributes[16 /*0:15*/] = {1,  0, 12, 0, 0, 0, 90, 5,
                                      36, 0, 0,  0, 0, 0, 0,  1};
static int Xo = 0;
static int Yo = 0;
static int Xs = 1023;
static int Ys = 1023;
static int Cx = 0;
static int Cy = 0;
static int Xv = 1023;
static int Yv = 1023;
static int Xl = 0;
static int Xr = 1023;
static int Yb = 0;
static int Yt = 1023;
static int Owxl = 0;
static int Owxr = 1023;
static int Owyb = 0;
static int Owyt = 1023;
static void Swop(int *A, int *B) {
  int C;
  C = *A;
  *A = *B;
  *B = C;
}
void Maptodevicecoords(int *X, int *Y) {
  *X = EdwinMuldiv(*X - Xl, Xs, Xv) + Xo;
  *Y = EdwinMuldiv(*Y - Yb, Ys, Yv) + Yo;
}
void Maptovirtualcoords(int *X, int *Y) {
  *X = EdwinMuldiv(*X - Xo, Xv, Xs) + Xl;
  *Y = EdwinMuldiv(*Y - Yo, Yv, Ys) + Yb;
}
static void Vector(int Fx, int Fy, int Tx, int Ty, int V) {
  static int Otx = 0;
  static int Oty = 0;
  Maptodevicecoords(&Fx, &Fy);
  Maptodevicecoords(&Tx, &Ty);
  if (V) {
    if (Otx != Fx || Oty != Fy) Drivedev(4, Fx, Fy);
    Drivedev(5, Tx, Ty);
  } else
    Drivedev(4, Tx, Ty);
  Otx = Tx;
  Oty = Ty;
}
static void Clip_inner(int Tx, int Ty, int V, int line) {
#define Clip(x, y, v) Clip_inner(x, y, v, __LINE__)
  int F;
  int T;
  int Fx;
  int Fy;
  static const int Left = 1;
  static const int Right = 2;
  static const int Above = 4;
  static const int Below = 8;
  int Code(int X, int Y) {
    int C;
    C = 0;
    if (X < Xl) C = Left;
    if (X > Xr) C = Right;
    if (Y > Yt) C += Above;
    if (Y < Yb) C += Below;
    return (C);
  }
  //fprintf(stderr, "Line %03d: Clip(%08x,%08x,%d);\n", line, Tx, Ty, V);
  if (Viewing < 0) return;
  Fx = Cx;
  Fy = Cy;
  Cx = Tx;
  Cy = Ty;
  if (Clipping < 0) {
    Vector(Fx, Fy, Tx, Ty, V);
    return;
  }
  T = Code(Tx, Ty);
  Vis = T;
  for (;;) {
    F = Code(Fx, Fy);
    //fprintf(stderr, "Cliploop F=%08x T=%d\n", F, T);
    if (F & T) return;
    if (!(F + T)) {
      Vector(Fx, Fy, Tx, Ty, V);
      return;
    }
    if (!F) {
      Swop(&F, &T);
      Swop(&Fx, &Tx);
      Swop(&Fy, &Ty);
    }
    if (F & Left) {
      Fy += EdwinMuldiv(Ty - Fy, Xl - Fx, Tx - Fx);
      Fx = Xl;
    } else if (F & Right) {
      Fy += EdwinMuldiv(Ty - Fy, Xr - Fx, Tx - Fx);
      Fx = Xr;
    } else if (F & Above) {
      Fx += EdwinMuldiv(Tx - Fx, Yt - Fy, Ty - Fy);
      Fy = Yt;
    } else if (F & Below) {
      Fx += EdwinMuldiv(Tx - Fx, Yb - Fy, Ty - Fy);
      Fy = Yb;
    }
  }
}
static void Insert(int A, int X, int Y) {
  int Code;
  int Oos;
  if (Storing < 0) return;
  Oos = Outstream();
  Selectoutput(Storing);
  Code = A & 15;
  if (Code <= 5 && -128 <= X && X <= 127 && -128 <= Y && Y <= 127) {
    Write(A | 16, 1);
    Write((X & 255) << 8 | Y & 255, 1);
  } else {
    Write(A, 1);
    if (Code == 15)
      Write(X, 1);
    else if (9 > Code || Code > 12) {
      Write(X, 1);
      Write(Y, 1);
    }
  }
  Newline();
  Selectoutput(Oos);
}
static void Setattribute(int What, int To) {
  Attributes[What] = To;
  if (What == 2) To = EdwinMuldiv(To, Xs, Xv);
  if (Viewing >= 0) Drivedev(7, What, To);
}
static int Charoffset(int Dir) {
  static const int Rx[4 /*0:3*/] = {1, 0, -1, 0};
  static const int Ry[4 /*0:3*/] = {0, 1, 0, -1};
  int Size;
  int Rot;
  Rot = (Attributes[3] / 90) & 3;
  Size = Attributes[2];
  if (Dir == 'X') return (Size * Rx[Rot]);
  return (Size * Ry[Rot]);
}
static void Doaspect(void) {
  int Md;
  int Mv;
  int N;
  Md = Viewportgradient(Ys, Xs);
  Mv = EdwinMuldiv(5000, Imod(Yt - Yb), Imod(Xr - Xl));
  if (Md != Mv)
    if (Md > Mv) {
      N = (EdwinMuldiv(Md, Imod(Xr - Xl), 5000) + Yb - Yt) / 2;
      Yb -= N;
      Yt += N;
    } else {
      N = (EdwinMuldiv(5000, Imod(Yt - Yb), Md) + Xl - Xr) / 2;
      Xl -= N;
      Xr += N;
    }
}
static const int One = 32;
static void Interpret_inner(int Pc, int Size, int Rot, int line);
#define Interpret(P, S, R) Interpret_inner(P, S, R, __LINE__)
static void Marker(int N) {
  static const int Mk[11 /*0:10*/] = {'.', 'O', '#', 'A', 'X', '*',
                                      '+', '>', '<', 'V', '^'};
  int Scale;
  if (0 > N || N > 10) return;
  if (Xlim[Device] < 250) {
    Drivedev(6, Mk[N], 0);
    return;
  }
  Scale = One;
  if (Xlim[Device] > 4095) Scale = One * 10;
  Interpret(Charpdf[2000-N*2], EdwinMuldiv(Scale, Xv, Xs), 0);
}
static void Interpret_inner(int Pc, int Size, int Orient, int line) {
  int Word;
  int Code;
  int X=0;
  int Y=0;
  int Z;
  int P;
  int Lsave;
  int Csize;
  int Active;
  int Ox;
  int Oy;
  static int C_sw;
  static void *C[16 /*0:15*/] = {
      &&C_0,  &&C_1,  &&C_2,  &&C_default, &&C_default, &&C_default,
      &&C_6,  &&C_7,  &&C_8,  &&C_9,       &&C_10,      &&C_11,
      &&C_12, &&C_13, &&C_14, &&C_15,
  };
  void Get(int *N) {
    if (!Pc) {
      //fprintf(stderr, "Read from pdf\n");
      Read(N);
    } else {
      *N = (int)Charpdf[Pc];
      //fprintf(stderr, "Read from charpdf[%d] -> %d\n", Pc, *N);
      Pc++;
    }
  }
  Active = False;
  //fprintf(stderr, "Line %d: Interpret(Pc=%d Size=%d Orient=%d)\n", line, Pc, Size, Orient);
  for (;;) {
    //fprintf(stderr, "Interpret loop\n");
    Get(&Word);
    Code = Word & 15;
    //fprintf(stderr, "1: Code=%d  X=%d  Y=%d\n", Code, X, Y);
    if (Code <= 5) {
      Active = True;
      Get(&X);
      if (!(Word & 16))
        Get(&Y);
      else {
        Y = X & 255;
        X = ((unsigned)X >> 8) & 255;
        if (X & 128) X = X ^ (~255);
        if (Y & 128) Y = Y ^ (~255);
      }
      if (Code >= 3) {
        if (Orient & 4) {
          Z = X;
          X = Y;
          Y = Z;
        }
        if (Orient & 1) X = -X;
        if (Orient & 2) Y = -Y;
        //fprintf(stderr, "3: Code=%d  X=%d  Y=%d  Size=%d One=%d  Cx=%d Cy=%d\n", Code, X, Y, Size, One, Cx, Cy);
        X = EdwinMuldiv(X, Size, One) + Cx;
        Y = EdwinMuldiv(Y, Size, One) + Cy;
        Code -= 3;
      }
    }
    goto *C[C_sw = Code];
  C_2:
    Clip(X, Y, 0);
    Marker(((unsigned)Word >> 12) & 15);
    continue;
  C_0:
    Clip(X, Y, 1);
    continue;
  C_1:
    //fprintf(stderr, ">Clip\n");
    Clip(X, Y, 0);
    //fprintf(stderr, "<Clip\n");
    continue;
  C_6:
    _imp_signal(14, 5, 0, _imp_str_literal(""));
  C_8:;
    Get(&Xl);  Get(&Xr);  Get(&Code);  Get(&Yb);  Get(&Yt);
    if (Attributes[15]) Doaspect();
    Xv = Xr - Xl;
    Yv = Yt - Yb;
    continue;
  C_9:
    if (!(Attributes[4] & 1)) {
      Drivedev(6, ((unsigned)Word>>4), Vis);
      Cx += Attributes[2];
    } else {
      P = 2000 - (((((unsigned)Word >> 4) & 255) - 21) << 1);
      Csize = EdwinMuldiv(Attributes[2], One, 12);
      if (Csize < 8) continue;
      if (Attributes[1]) {
        Lsave = Attributes[1];
        Drivedev(7,1,0);
      } else
        Lsave = -1;
      Ox = Cx;
      Oy = Cy;
      Interpret(Charpdf[P], Csize, Ra[(Attributes[3] / 90) & 3]);
      Clip(Ox + Charoffset('X'), Oy + Charoffset('Y'), 0);
      if (Lsave >= 0) Drivedev(7,1,Lsave);
    }
    Active = True;
    continue;
  C_15:;
  C_10:
    if (Code == 10)
      Code = ((unsigned)Word >> 4) & 255;
    else
      Get(&Code);
    Setattribute(((unsigned)Word >> 12) & 15, Code);
    continue;
  C_11:
    if (!Active) return;
  }
C_13:
C_14:
  _imp_signal(14, 5, 0, _imp_str_literal(""));
C_7:
C_12:;
  goto C_skip;
C_default:
  fprintf(stderr, "\nSwitch label 'C(%d):' not set in %s\n", C_sw,
          __PRETTY_FUNCTION__);
  fflush(stderr);
  abort();
C_skip:;
}
static void Window(int A, int B, int C, int D);
static void Viewport(int A, int B, int C, int D);
void Initialisefor(int Type) {
  Drivedev(0, Type, 0);
  if (0 <= Device && Device <= Numdev) Viewport(0, *Dvx, 0, *Dvy);
  Window(0, 1023, 0, 1023);
}
void Terminateedwin(void) {
  Drivedev(1, 0, 0);
  Insert(12, 0, 0);
}
void Lineabs(int X, int Y) {
  Insert(0, X, Y);
  Clip(X, Y, 1);
}
void Moveabs(int X, int Y) {
  Insert(1, X, Y);
  Clip(X, Y, 0);
}
void Markerabs(int N, int X, int Y) {
  Insert(N << 12 | 2, X, Y);
  Clip(X, Y, 0);
  Marker(N);
}
void Linerel(int X, int Y) {
  Insert(3, X, Y);
  Clip(X + Cx, Y + Cy, 1);
}
void Moverel(int X, int Y) {
  Insert(4, X, Y);
  Clip(X + Cx, Y + Cy, 0);
}
void Markerrel(int N, int X, int Y) {
  Insert(N << 12 | 5, X, Y);
  Clip(X + Cx, Y + Cy, 0);
  Marker(N);
}
void Character(int Ch) {
  static const int Unit = 12;
  int Lsave;
  int Size;
  int Ox;
  int Oy;
  Insert(Ch << 4 | 9, 0, 0);
  if (Viewing < 0) return;
  if (!(Attributes[4] & 1)) {
    if (!Vis) Drivedev(6,Ch,0);
    Cx += Attributes[2];
  } else {
    if (32 > Ch || Ch > 127) return;
    Size = EdwinMuldiv(Attributes[2], One, Unit);
    if (Size < 8) return;
    if (Attributes[1]) {
      Lsave = Attributes[1];
      Drivedev(7, 1, 0);
    } else
      Lsave = -1;
    Ox = Cx;
    Oy = Cy;
    Interpret(Charpdf[2000-((Ch-21)<<1)], Size, Ra[Attributes[3] / 90 & 3]);
    Clip(Ox + Charoffset('X'), Oy + Charoffset('Y'), 0);
    if (Lsave >= 0) Drivedev(7, 1, Lsave);
  }
}
void Text(_imp_string St) {
  int I;
  for (I = 1; I <= *Length(St); I++) Character(*Charno(St, I));
}
void Newframe(void) {
  Drivedev(3, 0, 0);
  Insert(11, 0, 0);
  Cx = 0;
  Cy = 0;
}
void Update(void) { Drivedev(2, 0, 0); }
void Clipon(void) { Clipping = 0; }
void Clipoff(void) { Clipping = Off; }
void Storeon(int Stream) { Storing = Stream; }
void Storeoff(void) { Storing = Off; }
void Viewon(int Stream) { Viewing = Stream; }
void Viewoff(void) { Viewing = Off; }
void Window(int A, int B, int C, int D) {
  Xl = A;
  Owxl = A;
  Xr = B;
  Owxr = B;
  Yb = C;
  Owyb = C;
  Yt = D;
  Owyt = D;
  Insert(8, A, B);
  Insert(8, C, D);
  if (Attributes[15]) Doaspect();
  Xv = Xr - Xl;
  Yv = Yt - Yb;
}
void Viewport(int A, int B, int C, int D) {
  int S;
  if (Device < 0) return;
  if (A > B) Swop(&A, &B);
  if (C > D) Swop(&C, &D);
  if (A < 0) A = 0;
  if (C < 0) C = 0;
  if (B > Xlim[Device]) B = Xlim[Device];
  if (D > Ylim[Device]) D = Ylim[Device];
  Drivedev(8, A, C);
  Drivedev(9, B, D);
  Xo = A;
  Xs = B - A;
  Yo = C;
  Ys = D - C;
  S = Storing;
  Storing = Off;
  Window(Owxl, Owxr, Owyb, Owyt);
  Storing = S;
}
static void Storeattribute(int What, int It) {
  Insert((What << 8 | It) << 4 | 10, 0, 0);
}
void Setcolour(int To) {
  static const int This = 0;
  static const int Def = 1;
  static const int Max = 255;
  if (0 > To || To > Max) To = Def;
  Storeattribute(This, To);
  if (Attributes[This] == To) return;
  Setattribute(This, To);
}
void Setlinestyle(int To) {
  static const int This = 1;
  static const int Def = 0;
  static const int Max = 4;
  if (0 > To || To > Max) To = Def;
  Storeattribute(This, To);
  if (Attributes[This] == To) return;
  Setattribute(This, To);
}
void Setcharsize(int To) {
  static const int This = 2;
  Insert(This << 12 | 15, To, 0);
  if (Attributes[This] == To) return;
  Setattribute(This, To);
}
void Setcharrot(int To) {
  static const int This = 3;
  if (To < 0) do
      To += 360;
    while (To < 0);
  else if (To >= 360)
    do
      To -= 360;
    while (To >= 360);
  Insert((This << 12) + 15, To, 0);
  if (Attributes[This] == To) return;
  Setattribute(This, To);
}
void Setcharquality(int To) {
  static const int This = 4;
  static const int Def = 0;
  static const int Max = 2;
  if (0 > To || To > Max) To = Def;
  Storeattribute(This, To);
  if (Attributes[This] == To) return;
  Setattribute(This, To);
}
void Setcharfont(int To) {
  static const int This = 5;
  static const int Def = 0;
  static const int Max = 7;
  if (0 > To || To > Max) To = Def;
  Storeattribute(This, To);
  if (Attributes[This] == To) return;
  Setattribute(This, To);
}
void Setcharslant(int To) {
  static const int This = 6;
  static const int Def = 90;
  static const int Max = 180;
  if (0 > To || To > Max) To = Def;
  Storeattribute(This, To);
  if (Attributes[This] == To) return;
  Setattribute(This, To);
}
void Setintensity(int To) {
  static const int This = 7;
  static const int Def = 5;
  static const int Max = 7;
  if (0 > To || To > Max) To = Def;
  Storeattribute(This, To);
  if (Attributes[This] == To) return;
  Setattribute(This, To);
}
void Setspeed(int To) {
  static const int This = 8;
  static const int Def = 36;
  static const int Max = 255;
  if (0 > To || To > Max) To = Def;
  Storeattribute(This, To);
  if (Attributes[This] == To) return;
  Setattribute(This, To);
}
void Aspectratioing(int Mode) {
  static const int This = 15;
  static const int Def = 1;
  int S;
  if (Mode) Mode = Def;
  Storeattribute(This, Mode);
  Setattribute(This, Mode);
  S = Storing;
  Storing = Off;
  Window(Owxl, Owxr, Owyb, Owyt);
  Storing = S;
}
void Inquireposition(int *X, int *Y) {
  *X = Cx;
  *Y = Cy;
}
void Inquirewindow(int *A, int *B, int *C, int *D) {
  *A = Xl;
  *B = Xr;
  *C = Yb;
  *D = Yt;
}
void Inquireviewport(int *A, int *B, int *C, int *D) {
  *A = Xo;
  *B = Xs + Xo;
  *C = Yo;
  *D = Ys + Yo;
}
void Review(void) { Interpret(0, One, 0); }

// #include "pmplib.h" - Poor Man's Profiler
int _imp_mainep(int _imp_argc, char **_imp_argv) {
  static const int Pdfstream = 2;
  static const int Good = 1;
  int I;
  int X;
  int Y;

  Openoutput(2, _imp_str_literal("EDWIN4.stream2"));
  Selectoutput(1);
  Initialisefor(0);
  Newframe();
  Storeon(Pdfstream);
  Moveabs(400, 20);
  for (I = 1; I <= 4; I++) {
    Moverel(-36, 0);
    Linerel(-36, 0);
  }
  Linerel(0, 720);
  for (I = 1; I <= 4; I++) {
    Moverel(36, 0);
    Linerel(36, 0);
  }
  for (I = 1; I <= 4; I++) {
    Linerel(0, -720);
    Linerel(-36, 0);
    Linerel(0, 720);
    Linerel(-36, 0);
  }
  for (I = 1; I <= 6; I++) {
    Moverel(0, -60);
    Linerel(288, 0);
    Moverel(0, -60);
    Linerel(-288, 0);
  }
  X = 112;
  Y = 697;
  Moveabs(X, Y);
  I = 32;
  Setcharsize(36);
  Setcharquality(Good);
  Clipoff();
  for (;;) {
    //fprintf(stderr, "Loop %d\n", I);
    Markerrel(4, 0, 0);
    Character(I);
    I++;
    if (I == 127) break;
    if (X == 364) {
      Markerrel(4, 0, 0);
      Y -= 60;
      X = 112;
    } else
      X += 36;
    Moveabs(X, Y);
  }
  Terminateedwin();
  // pmp("edwin4-pmp");
  exit(0);
  return (1);
}
