/****************************************************************************
* Michael Main -- Sept 25, 1991
*
* This C module contains routines to simulate the Turbo C++ graphics
* routines in a simple way, using X-windows.
*
* To use these routines in your C-program, follow these steps:
*  1. Put this code in a file called Xturbo.c
*  2. Compile this code with the command:
*      g++ -c Xturbo.c -I/usr/local/X11/include
*  3. Put the include directive #include "Xturbo.h" in your program.
*     Documentation for the functions is also present in Xturbo.h.
*  4. When you compile your program, include Xturbo.o on the g++ line, and
*     also include the X11 library, like this:
*      g++ myprogram.c Xturbo.o -L/usr/local/X11/lib -lX11
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
//#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/Xresource.h>
#include "Xturbo.h"

static Display *mydisplay;
static Window mywindow;
static GC context[2];
static int screen;
Colormap cmap;
long unsigned int plane_masks[2];
long unsigned int colors[2];
XColor NeitherPlane, NotVisualPlaneOnly, VisualPlaneOnly, BothPlanes;
unsigned long black_pix, white_pix;

static int screenheight, screenwidth; // Height and width of screen in pixels
static int monitortype; // Currently indicates the number of pages available
static int lines, fills; // Line and fill colors
static int visual, active; // Current visual and active page

void clearviewport() {
  int savelines;
  int savefills;
  short wholescreen[8] = 
    { 0, 0, 
      0, screenheight,
      screenwidth, screenheight,
      screenwidth, 0
    }; 
  if (!monitortype) {
    XClearWindow(mydisplay, mywindow);
    XMapWindow(mydisplay, mywindow);
    /* XFlush(mydisplay); */
    return;
  }
  savelines = lines;
  savefills = fills;
  lines = 1;
  fills = 1;
  fillpoly(4, wholescreen);
  lines = savelines;
  fills = savefills;
}

void line(int x1, int y1, int x2, int y2) {
  if (lines)
    XSetForeground(mydisplay, context[active], white_pix);
  else
    XSetForeground(mydisplay, context[active], black_pix);
  XDrawLine(mydisplay, mywindow, context[active], x1, y1, x2, y2);
  XMapWindow(mydisplay, mywindow);
  /* XFlush(mydisplay); */
  /* printf("Line %d %d %d %d\n", x1,y1,x2,y2); */ 
}

void plot(int x, int y) {
  if (lines)
    XSetForeground(mydisplay, context[active], white_pix);
  else
    XSetForeground(mydisplay, context[active], black_pix);
  XDrawPoint(mydisplay, mywindow, context[active], x, y);
  XMapWindow(mydisplay, mywindow);
  /* XFlush(mydisplay); */
  /* printf("Point %d %d\n", x,y); */
}

void drawpoly(int numpoints, short polypoints[]) {
/* Draws a polygon with numpoints vertices.  The array
** polypoints contains the x,y coordinates of the first point of the polygon
** in polypoints[0] and polypoints[1].  The next point is in polypoints[2]
** and polypoints[3], and so on.
*/
  // char response;
  if (lines)
    XSetForeground(mydisplay, context[active], white_pix);
  else
    XSetForeground(mydisplay, context[active], black_pix);
  XDrawLines(mydisplay, mywindow, context[active], (XPoint *) polypoints, numpoints, CoordModeOrigin);
  if (numpoints >= 3) 
    XDrawLine(mydisplay, mywindow, context[active], polypoints[2*numpoints-2], polypoints[2*numpoints-1], polypoints[0], polypoints[1]);
  XMapWindow(mydisplay, mywindow);
  /* scanf("%c", &response); */
  /* XFlush(mydisplay); */
  /* printf("Drawpoly has been called\n"); */
}

void updateall() {
  XFlush(mydisplay);
}

void fillpoly(int numpoints, short polypoints[]) {
/* Draws and fills a polygon with numpoints vertices.  The array
** polypoints contains the x,y coordinates of the first point of the polygon
** in polypoints[0] and polypoints[1].  The next point is in polypoints[2]
** and polypoints[3], and so on.
*/
  if (fills)
    XSetForeground(mydisplay, context[active], white_pix);
  else
    XSetForeground(mydisplay, context[active], black_pix);
  XFillPolygon(mydisplay, mywindow, context[active], (XPoint *) polypoints, numpoints, Complex, CoordModeOrigin);
  drawpoly(numpoints, polypoints);
  /* printf("Fillpoly has been called\n"); */
}

void initgraphics()
{
  unsigned int line_wid = 1;
  int line_style = LineSolid;
  int cap_style = CapRound;
  int join_style = JoinMiter;
  int dash_offset = 0;
  static char dash_list[] = { 12, 24 };
  int list_len = 2;
  char response;

  /* First connect to a server -- page 53 Volume 1 of manual */
  if ((mydisplay=XOpenDisplay(NULL)) == NULL ) {
    fprintf( stderr, "XOpenDisplay failure.\n");
    fprintf( stderr, "Make sure the DISPLAY variable is set in environment.");
    exit( -1 );
  }
  screen = DefaultScreen(mydisplay);

  /* Get the display dimensions -- page 56 Volume 1 of manual */
  screenwidth = DisplayWidth(mydisplay, screen) >> 1;
  screenheight = screenwidth; /* Make it square */

  /* Create a window -- page 57 Volume 1 of manual */
  mywindow = XCreateSimpleWindow(mydisplay,
    RootWindow(mydisplay, screen),
    0, 0, /* Coordinates of upper left corner of window */
    screenwidth, screenheight,
    4, /* Border width */
    BlackPixel(mydisplay, screen), WhitePixel(mydisplay, screen)
  );

  /* Select the events to handle */
  (void) XSelectInput(mydisplay, mywindow, ExposureMask);

  /* Set the graphics context[active] -- page 270 Volume 1 of manual */
  context[0] = XCreateGC(mydisplay, mywindow, 0, NULL);
  context[1] = XCreateGC(mydisplay, mywindow, 0, NULL);
  XSetWindowBackground(mydisplay, mywindow, WhitePixel(mydisplay,screen));
  XSetForeground(mydisplay, context[active], BlackPixel(mydisplay,screen));
  XSetLineAttributes(mydisplay,context[active],line_wid,line_style,cap_style,join_style);
  XSetDashes(mydisplay, context[active],dash_offset,dash_list,list_len);

  /* Make the window visible */
  XMapWindow(mydisplay, mywindow);
  XFlush(mydisplay); 
  /* Note: A scanf operation seems to be needed here, but I'm not sure why */
  printf("Please press Return to complete initialization\n");
  scanf("%c",&response); 

  /* Set up the variables for pages and colors */
  visual = active = 0;
  lines = 0;  fills = 1;

  cmap = DefaultColormap(mydisplay, screen);
  if ((DisplayPlanes(mydisplay, screen) <= 1)
  || (!XAllocColorCells(mydisplay, cmap, False, plane_masks, 2, colors, 1)))
  { // Set up with just one page
    monitortype = 0;
    black_pix = BlackPixel(mydisplay, screen);
    white_pix = WhitePixel(mydisplay, screen);
  }
  else { // Set up with two pages using the planes and pixels allocated
    monitortype = 1; // Legal page numbers are 0 and 1
    /* The following four pixels are now allocated, and will be displayed
    ** according to the current visual page, as shown in the table:
    ** colors[0]                     Always white
    ** colors[0] | plane_masks[0]    Black if page 0 is visual, else white
    ** colors[0] | plane_masks[1]    Black if page 1 is visual, else white
    ** colors[0] | both              Always black
    */
    // Set up four Xcolors to represent these four pixels
    XParseColor(mydisplay, cmap, "white", &NeitherPlane);
    XParseColor(mydisplay, cmap, "white", &NotVisualPlaneOnly);
    XParseColor(mydisplay, cmap, "black", &VisualPlaneOnly);
    XParseColor(mydisplay, cmap, "black", &BothPlanes);
    // Store the two non-changing pixels in the color map
    NeitherPlane.pixel = colors[0];
    XStoreColor(mydisplay, cmap, &NeitherPlane);
    BothPlanes.pixel = colors[0] | plane_masks[0] | plane_masks[1];
    XStoreColor(mydisplay, cmap, &BothPlanes);
    // Call setvisualpage to store the two changing pixels
    setvisualpage(0);
    // Call setactivepage to give initial values to black_pix.
    setactivepage(0);
    white_pix = colors[0];
    XSetPlaneMask(mydisplay, context[0], colors[0] | plane_masks[0]);
    XSetPlaneMask(mydisplay, context[1], colors[0] | plane_masks[1]);
  }
}

void closegraph() { }

int getmaxx() { return screenwidth-1; }
int getmaxy() { return screenheight-1; }
int maxpage() { return monitortype; }
void setcolor(int linecolor) { lines = linecolor; }
void setfill(int fillcolor)  { fills = fillcolor; }

void setactivepage(int pagenumber) { 
  if ((pagenumber >= 0) && (pagenumber <= monitortype) && monitortype) {
    active = pagenumber;
    black_pix = colors[0] | plane_masks[active];
  }
}

void setvisualpage(int pagenumber) { 
  if ((pagenumber >= 0) && (pagenumber <= monitortype) && monitortype) {
    visual = pagenumber;
    VisualPlaneOnly.pixel = colors[0] | plane_masks[visual];
    XStoreColor(mydisplay, cmap, &VisualPlaneOnly);
    NotVisualPlaneOnly.pixel = colors[0] | plane_masks[!visual];
    XStoreColor(mydisplay, cmap, &NotVisualPlaneOnly);
  }
}

