/*
 *  newdemo.c	-	A demo program using PDCurses. The program illustrate
 *  	 		the use of colours for text output.
 *
 * $Id: newdemo.c,v 1.49 2025/02/15 15:15:25 tom Exp $
 */

#include <test.priv.h>

#include <time.h>

/*
 *  The Australian map
 */
static CONST_MENUS char *AusMap[16] =
{
    "           A           A ",
    "    N.T. AAAAA       AAAA ",
    "     AAAAAAAAAAA  AAAAAAAA ",
    "   AAAAAAAAAAAAAAAAAAAAAAAAA Qld.",
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAA ",
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ",
    " AAAAAAAAAAAAAAAAAAAAAAAAAAAA ",
    "   AAAAAAAAAAAAAAAAAAAAAAAAA N.S.W.",
    "W.A. AAAAAAAAA      AAAAAA Vic.",
    "       AAA   S.A.     AA",
    "                       A  Tas.",
    ""
};

/*
 *  Funny messages
 */
#define NMESSAGES   6

static const char *messages[] =
{
    "Hello from the Land Down Under",
    "The Land of crocs. and a big Red Rock",
    "Where the sunflower runs along the highways",
    "the dusty red roads lead one to loneliness",
    "Blue sky in the morning and",
    "freezing nights and twinkling stars",
    ""
};

/*
 *  Trap interrupt
 */
static void
trap(int sig GCC_UNUSED)
{
    stop_curses();
    ExitProgram(EXIT_FAILURE);
}

/*
 *  Wait for user
 */
static int
WaitForUser(WINDOW *win)
{
    time_t t;

    nodelay(win, TRUE);
    t = time((time_t *) 0);

    while (1) {
	chtype key;
	if ((int) (key = (chtype) wgetch(win)) != ERR) {
	    if (key == 'q' || key == 'Q')
		return 1;
	    else
		return 0;
	}
	if (time((time_t *) 0) - t > 5)
	    return 0;
    }
}

static void
set_colors(WINDOW *win, int pair, int foreground, int background)
{
    if (has_colors()) {
	if (pair > COLOR_PAIRS)
	    pair = COLOR_PAIRS;
	init_pair((short) pair, (short) foreground, (short) background);
	(void) wattrset(win, AttrArg(COLOR_PAIR(pair), 0));
    }
}

static chtype
use_colors(WINDOW *win, int pair, chtype attrs)
{
    if (has_colors()) {
	if (pair > COLOR_PAIRS)
	    pair = COLOR_PAIRS;
	attrs |= (chtype) COLOR_PAIR(pair);
    }
    (void) wattrset(win, AttrArg(attrs, 0));
    return attrs;
}

/*
 * Test sub windows
 */
static int
SubWinTest(WINDOW *win)
{
    int w, h, sw, sh, bx, by;
    WINDOW *swin1, *swin2, *swin3;

    getmaxyx(win, h, w);
    getbegyx(win, by, bx);
    sw = w / 3;
    sh = h / 3;

    if ((swin1 = subwin(win, sh, sw, by + 3, bx + 5)) == NULL) {
	return 1;
    }
    if ((swin2 = subwin(win, sh, sw, by + 4, bx + 8)) == NULL) {
	delwin(swin1);
	return 1;
    }
    if ((swin3 = subwin(win, sh, sw, by + 5, bx + 11)) == NULL) {
	delwin(swin1);
	delwin(swin2);
	return 1;
    }

    set_colors(swin1, 8, COLOR_RED, COLOR_BLUE);
    werase(swin1);
    MvWAddStr(swin1, 0, 3, "Sub-window 1");
    wrefresh(swin1);

    set_colors(swin2, 9, COLOR_CYAN, COLOR_MAGENTA);
    werase(swin2);
    MvWAddStr(swin2, 0, 3, "Sub-window 2");
    wrefresh(swin2);

    set_colors(swin3, 10, COLOR_YELLOW, COLOR_GREEN);
    werase(swin3);
    MvWAddStr(swin3, 0, 3, "Sub-window 3");
    wrefresh(swin3);

    delwin(swin1);
    delwin(swin2);
    delwin(swin3);
    WaitForUser(win);
    return 0;
}

static int
bounce(int n, int *dir, int len)
{
    if (*dir > 0)
	++n;
    else
	--n;
    if (n <= 1 || n >= len - 2)
	*dir = *dir ? 0 : 1;
    return n;
}

/*
 *  Bouncing balls
 */
static int
BouncingBalls(WINDOW *win)
{
    int w, h;
    int x1, y1, xd1, yd1;
    int x2, y2, xd2, yd2;
    int x3, y3, xd3, yd3;

    getmaxyx(win, h, w);

    x1 = 2 + rand() % (w - 4);
    y1 = 2 + rand() % (h - 4);
    x2 = 2 + rand() % (w - 4);
    y2 = 2 + rand() % (h - 4);
    x3 = 2 + rand() % (w - 4);
    y3 = 2 + rand() % (h - 4);

    xd1 = 1;
    yd1 = 1;
    xd2 = 1;
    yd2 = 0;
    xd3 = 0;
    yd3 = 1;

    nodelay(win, TRUE);

    while (wgetch(win) == ERR) {
	x1 = bounce(x1, &xd1, w);
	y1 = bounce(y1, &yd1, h);
	x2 = bounce(x2, &xd2, w);
	y2 = bounce(y2, &yd2, h);
	x3 = bounce(x3, &xd3, w);
	y3 = bounce(y3, &yd3, h);

	set_colors(win, 11, COLOR_RED, COLOR_BLUE);
	MvWAddCh(win, y1, x1, 'O');

	set_colors(win, 12, COLOR_BLUE, COLOR_RED);
	MvWAddCh(win, y2, x2, '*');

	set_colors(win, 13, COLOR_YELLOW, COLOR_WHITE);
	MvWAddCh(win, y3, x3, '@');

	wmove(win, 0, 0);
	wrefresh(win);
	delay_output(100);
    }
    return 0;
}

static void
usage(int ok)
{
    static const char *msg[] =
    {
	"Usage: newdemo [options]"
	,""
	,USAGE_COMMON
    };
    size_t n;

    for (n = 0; n < SIZEOF(msg); n++)
	fprintf(stderr, "%s\n", msg[n]);

    ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
}
/* *INDENT-OFF* */
VERSION_COMMON()
/* *INDENT-ON* */

/*
 *  Main driver
 */
int
main(int argc, char *argv[])
{
    WINDOW *win;
    int x, y, i, k;
    char buffer[SIZEOF(messages) * 80];
    int width, height;
    chtype save[80];
    int ch;

    while ((ch = getopt(argc, argv, OPTS_COMMON)) != -1) {
	switch (ch) {
	case OPTS_VERSION:
	    show_version(argv);
	    ExitProgram(EXIT_SUCCESS);
	default:
	    usage(ch == OPTS_USAGE);
	    /* NOTREACHED */
	}
    }
    if (optind < argc)
	usage(FALSE);

    setlocale(LC_ALL, "");

    InitAndCatch(initscr(), trap);
    if (has_colors())
	start_color();
    cbreak();
    curs_set(0);
    width = 48;
    height = 14;		/* Create a drawing window */
    win = newwin(height, width, (LINES - height) / 2, (COLS - width) / 2);
    if (win == NULL) {
	stop_curses();
	ExitProgram(EXIT_FAILURE);
    }

    while (1) {
	int w;
	int j;
	chtype c;
	const char *message;

	set_colors(win, 1, COLOR_WHITE, COLOR_BLUE);
	werase(win);

	set_colors(win, 2, COLOR_RED, COLOR_RED);
	box(win, ACS_VLINE, ACS_HLINE);
	wrefresh(win);
	/* Do random output of a character */
	use_colors(win, 1, A_NORMAL);
	c = 'a';
	for (i = 0; i < 5000; ++i) {
	    x = rand() % (width - 2) + 1;
	    y = rand() % (height - 2) + 1;
	    MvWAddCh(win, y, x, c);
	    wrefresh(win);
	    nodelay(win, TRUE);
	    if (wgetch(win) != ERR)
		break;
	    if (i == 2000) {
		c = 'b';
		set_colors(win, 3, COLOR_CYAN, COLOR_YELLOW);
	    }
	}

	SubWinTest(win);
	/* Erase and draw green window */
	set_colors(win, 4, COLOR_YELLOW, COLOR_GREEN);
	wbkgd(win, use_colors(win, 4, A_BOLD));
	werase(win);
	wrefresh(win);
	/* Draw RED bounding box */
	use_colors(win, 2, A_NORMAL);
	box(win, ' ', ' ');
	wrefresh(win);
	/* Display Australia map */
	use_colors(win, 4, A_BOLD);
	i = 0;
	while (*AusMap[i]) {
	    MvWAddStr(win, i + 1, 8, AusMap[i]);
	    wrefresh(win);
	    delay_output(50);
	    ++i;
	}

	set_colors(win, 5, COLOR_BLUE, COLOR_WHITE);
	use_colors(win, 5, A_BLINK);
	MvWAddStr(win, height - 2, 6, " PDCurses 2.1 for DOS, OS/2 and Unix");
	wrefresh(win);

	/* Draw running messages */
	set_colors(win, 6, COLOR_YELLOW, COLOR_WHITE);
	message = messages[j = 0];
	i = 1;
	w = width - 2;
	_nc_STRCPY(buffer, message, sizeof(buffer));
	while (j < NMESSAGES) {
	    while ((int) strlen(buffer) < w) {
		_nc_STRCAT(buffer, " ... ", sizeof(buffer));
		_nc_STRCAT(buffer, messages[++j % NMESSAGES], sizeof(buffer));
	    }

	    if (i < w)
		(void) mvwaddnstr(win, height / 2, w - i, buffer, i);
	    else
		(void) mvwaddnstr(win, height / 2, 1, buffer, w);

	    wrefresh(win);
	    nodelay(win, TRUE);
	    if (wgetch(win) != ERR) {
		flushinp();
		break;
	    }
	    if (i++ >= w) {
		for (k = 0; (buffer[k] = buffer[k + 1]) != '\0'; k++) ;
	    }
	    delay_output(100);
	}

	j = 0;
	/*  Draw running As across in RED */
	set_colors(win, 7, COLOR_RED, COLOR_GREEN);
	memset(save, ' ', sizeof(save));
	for (i = 2; i < width - 4; ++i) {
	    k = (int) mvwinch(win, 4, i);
	    if (k == ERR)
		break;
	    save[j++] = c = (chtype) k;
	    c &= A_CHARTEXT;
	    MvWAddCh(win, 4, i, c);
	}
	wrefresh(win);

	/* Put a message up wait for a key */
	i = height - 2;
	use_colors(win, 5, A_NORMAL);
	MvWAddStr(win, i, 5, " Type a key to continue or 'Q' to quit ");
	wrefresh(win);

	if (WaitForUser(win) == 1)
	    break;

	j = 0;			/* Restore the old line */
	for (i = 2; i < width - 4; ++i)
	    MvWAddCh(win, 4, i, save[j++]);
	wrefresh(win);

	BouncingBalls(win);
	/* Put a message up wait for a key */
	i = height - 2;
	use_colors(win, 5, A_NORMAL);
	MvWAddStr(win, i, 5, " Type a key to continue or 'Q' to quit ");
	wrefresh(win);
	if (WaitForUser(win) == 1)
	    break;
    }
    stop_curses();
    ExitProgram(EXIT_SUCCESS);
}
