/*
 * L C G D E M O
 *
 * This is a Learn C graphics demonstration program.  Its purpose
 * is to show how to select a graphics mode, draw some shapes and
 * text, and then return to DOS after restoring the user's
 * previous video environment.
 */

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <graph.h>
#include <math.h>

#define A_BRIGHTYELLOW  14
#define K_ESC           27
#define MSG_ROW         25
#define MSG_COL         2
#define MSG_WIDTH       38
#define M_NOWAIT        0
#define M_WAIT          1
#define MODE_ERR        0
#define NCOLORS         16
#define R_HORIZ         -140
#define R_VERT          0
#define R_HEIGHT        80
#define R_WIDTH         150
#define R_BORDER        0
#define R_FILL          1

/*
 * Function prototypes
 */
void Message(short, short, char *, short);
void Rectangle(short, short, short, short, short);

int
main(void)
{
	struct videoconfig config;  /* video configuration data */
	short x_org, y_org;         /* coordinates of the origin */
	short x_ul, y_ul;           /* upper left corner */
	short x_lr, y_lr;           /* lower right corner */
	short x, y;                 /* current coordinates */
	short x_offset, y_offset;   /* offsets */
	short color;                /* color number */
	short divisor;              /* to keep color number in range */
	short delta;                /* columnar offset */

	/*
	 * Set up the medium-resloution graphics,
	 * set the logical origin to center of screen,
	 * and set up a text window.
	 */
	divisor = 16;
	if (_setvideomode(_MRES16COLOR) == MODE_ERR) {
		divisor = 4;
		if (_setvideomode(_MRES4COLOR) == MODE_ERR) {
			fprintf(stderr, "Can't set graphics mode\n");
			exit (1);
		}
	}
	_getvideoconfig(&config);
	x_org = config.numxpixels / 2 - 1;
	y_org = config.numypixels / 2 - 1;
	_setlogorg(x_org, y_org);
	_settextwindow(MSG_ROW, MSG_COL, MSG_ROW,
		       MSG_COL + MSG_WIDTH - 1);
	_settextposition(MSG_ROW, MSG_COL);
	_settextcolor(A_BRIGHTYELLOW);

	/*
	 * Draw a stack of rectangles in different colors.
	 */
	x = R_HORIZ;
	y = R_VERT;
	x_offset = 0;
	y_offset = 0;
	for (color = 0; color < NCOLORS; ++color) {
		_setcolor(color % divisor);
		Rectangle(R_FILL, y + y_offset, x + x_offset,
			  y + R_HEIGHT + y_offset, x + R_WIDTH +
			  x_offset);
		_setcolor((NCOLORS - 1 - color) % divisor);
		Rectangle(R_BORDER, y + y_offset, x + x_offset,
			  y + R_HEIGHT + y_offset,
			  x + R_WIDTH + x_offset);
		x_offset += 8;
		y_offset -= 6;
	}

	/*
	 * Create a text window and slide the "quit" message into
	 * view from right to left and then wait for a keypress.
	 */
	_wrapon(_GWRAPOFF);
	for (delta = MSG_WIDTH; delta >= 0; --delta)
		Message(MSG_ROW, MSG_COL + delta,
			"Press a key to quit.", M_NOWAIT);
	Message(MSG_ROW, MSG_COL + delta,
	"Press a key to quit.", M_WAIT);

	/*
	 * Restore the original video mode and
	 * return control to the operating system.
	 */
	_setvideomode(_DEFAULTMODE);

	return (0);
}


/*
 * Rectangle()
 *
 * Draw a rectangle in the current color.  The type
 * parameter controls whether the rectangle is drawn
 * as a border only or as a filled (solid) object.
 */

void
Rectangle(short type, short top, short left, short bottom,
	  short right)
{
	short x, y;

	switch (type) {
	case R_BORDER:
		/*
		 * Construct a box from four line segments.
		 */
		_moveto(left, top);
		_lineto(right, top);
		_lineto(right, bottom);
		_lineto(left, bottom);
		_lineto(left, top);
		break;
	case R_FILL:
		/*
		 * Make a solid box from a series of
		 * adjacent horizontal lines.
		 */
		for (y = top; y <= bottom; ++y) {
			_moveto(left, y);
			_lineto(right, y);
		}
		break;
	default:
		break;
	}
}


/*
 * Message()
 *
 * Display the message text at the specified screen
 * location (row, col).  If wait has a non-zero value,
 * wait for the user to press a key.  When the user complies,
 * grab the character from the keyboard buffer so it won't
 * interfere with the calling program following the return.
 */

void
Message(row, col, text, wait)
short row, col; /* text position */
char *text;     /* text pointer */
short wait;     /* wait flag */
		/* (wait != 0 means wait for a keypress) */
{
	int k;      /* key code */

	/*
	 * Write the prompt text at the specified location.
	 */
	_settextposition(row, col);
	_outtext(text);

	/*
	 * If the wait flag is set, wait for a key to be pressed,
	 * then remove the code from the keyboard buffer.  Handle
	 * extended codes by grabbing two bytes if the first is NUL.
	 */
	if (wait) {
		k = getch();            /* read the character */
		if (k == '\0')
			/* extended code -- get the scan code */
			getch();
	}
}
