/*
** Copyright 2017-2020,2025 Thomas E. Dickey
** Copyright 1997-2010,2017 Free Software Foundation, Inc.
**
** This file is part of TACK.
**
** TACK 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, version 2.
**
** TACK 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 TACK; see the file COPYING.  If not, write to
** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
** Boston, MA 02110-1301, USA
*/

#include <tack.h>

MODULE_ID("$Id: modes.c,v 1.15 2025/04/26 23:32:09 tom Exp $")

/*
 * Tests boolean flags and terminal modes.
 */

/*
**	subtest_os(test_list, status, ch)
**
**	test over strike mode (os)
*/
static void
subtest_os(
	      TestList * t,
	      int *state,
	      int *ch)
{
    ptext("(os) should be true, not false.");
    put_cr();
    ptextln("(os) should be           false.");
    sprintf(temp, "(os) over-strike is %s in the data base.  ",
	    over_strike ? "true" : "false");
    ptext(temp);
    generic_done_message(t, state, ch);
}

/*
**	subtest_rmam(test_list, status, ch)
**
**	test exit automatic margins mode (rmam)
*/
static void
subtest_rmam(
		TestList * t,
		int *state,
		int *ch)
{
    int j;

    if (!exit_am_mode) {
	ptext("(rmam) not present.  ");
    } else if (!can_go_home) {
	ptext("(rmam) not tested, no way to home cursor.  ");
    } else if (over_strike) {
	put_clear();
	go_home();
	tc_putp(exit_am_mode);
	ptext("\n(rmam) will     reset (am)");
	go_home();
	for (j = 0; j < columns; j++)
	    put_this(' ');
	ptext("(rmam) will not reset (am)");
	go_home();
	put_newlines(2);
    } else {
	put_clear();
	go_home();
	tc_putp(exit_am_mode);
	ptext("\n(rmam) will reset (am)");
	go_home();
	for (j = 0; j < columns; j++)
	    put_this(' ');
	ptext("(rmam) will not reset (am) ");
	go_home();
	put_str("                          ");
	go_home();
	put_newlines(2);
    }
    ptext("Exit-automatic-margins ");
    generic_done_message(t, state, ch);
}

/*
**	subtest_smam(test_list, status, ch)
**
**	test enter automatic margins mode (smam)
*/
static void
subtest_smam(
		TestList * t,
		int *state,
		int *ch)
{
    int i, j;

    if (!enter_am_mode) {
	ptext("(smam) not present.  ");
    } else if (!can_go_home) {
	ptext("(smam) not tested, no way to home cursor.  ");
    } else if (over_strike) {
	put_clear();
	go_home();
	tc_putp(enter_am_mode);
	ptext("\n(smam) will ");
	i = char_count;
	ptext("not set (am)");
	go_home();
	for (j = -i; j < columns; j++)
	    put_this(' ');
	put_str("@@@");
	put_newlines(2);
    } else {
	put_clear();
	go_home();
	tc_putp(enter_am_mode);
	ptext("\n(smam) will not set (am)");
	go_home();
	for (j = 0; j < columns; j++)
	    put_this(' ');
	ptext("(smam) will set (am)    ");
	go_home();
	put_str("                          ");
	put_newlines(2);
    }
    ptext("Enter-automatic-margins ");
    generic_done_message(t, state, ch);
}

/*
**	subtest_am(test_list, status, ch)
**
**	test automatic margins (am)
*/
static void
subtest_am(
	      TestList * t,
	      int *state,
	      int *ch)
{
    int i, j;

    if (!can_go_home) {
	ptextln("(am) not tested, no way to home cursor.  ");
    } else if (over_strike) {
	put_clear();
	go_home();
	ptext("\n(am) should ");
	i = char_count;
	ptext("not be set");
	go_home();
	for (j = -i; j < columns; j++)
	    put_this(' ');
	put_str("@@@");
	go_home();
	put_newlines(2);
	sprintf(temp, "(am) is %s in the data base",
		auto_right_margin ? "true" : "false");
	ptextln(temp);
    } else {
	put_clear();
	go_home();
	ptext("\n(am) should not be set");
	go_home();
	for (j = 0; j < columns; j++)
	    put_this(' ');
	ptext("(am) should be set    ");
	go_home();
	put_str("                       \n\n");
	sprintf(temp, "(am) is %s in the data base",
		auto_right_margin ? "true" : "false");
	ptextln(temp);
    }
    ptext("Automatic-right-margin ");
    generic_done_message(t, state, ch);
}

/* Note: uprint() sends underscore back-space character, and
        ucprint() sends character back-space underscore.  */

/*
**	uprint(string)
**
**	underline string for (ul) test
*/
static void
uprint(const char *s)
{
    if (s) {
	while (*s) {
	    put_str("_\b");
	    putchp(*s++);
	}
    }
}

/*
**	ucprint(string)
**
**	underline string for (uc) test
*/
static void
ucprint(const char *s)
{
    if (s) {
	while (*s) {
	    putchp(*s++);
	    putchp('\b');
	    tc_putp(underline_char);
	}
    }
}

/*
**	subtest_ul(test_list, status, ch)
**
**	test transparent underline (ul)
*/
static void
subtest_ul(
	      TestList * t,
	      int *state,
	      int *ch)
{
    if (!over_strike) {
	/* (ul) is used only if (os) is reset */
	put_crlf();
	sprintf(temp, "This text should %sbe underlined.",
		transparent_underline ? "" : "not ");
	uprint(temp);
	put_crlf();
	ptextln("If the above line is not underlined the (ul) should be false.");
	sprintf(temp, "(ul) Transparent-underline is %s in the data base",
		transparent_underline ? "true" : "false");
	ptextln(temp);
	generic_done_message(t, state, ch);
    }
}

/*
**	subtest_uc(test_list, status, ch)
**
**	test underline character (uc)
*/
static void
subtest_uc(
	      TestList * t,
	      int *state,
	      int *ch)
{
    if (!over_strike) {
	if (underline_char) {
	    ucprint("This text should be underlined.");
	    put_crlf();
	    ptextln("If the above text is not underlined the (uc) has failed.");
	    ptext("Underline-character ");
	} else {
	    ptext("(uc) underline-character is not defined.  ");
	}
	generic_done_message(t, state, ch);
    }
}

/*
**	subtest_bw(test_list, status, ch)
**
**	test auto left margin (bw)
*/
static void
subtest_bw(
	      TestList * t,
	      int *state,
	      int *ch)
{
    int i;

    if (over_strike) {
	int j;

	/* test (bw) */
	ptext("\n(bw) should ");
	i = char_count;
	ptextln("not be set.");
	for (j = i; j < columns; j++)
	    put_str("\b");
	put_str("@@@");
	put_crlf();
	sprintf(temp, "(bw) Auto-left-margin is %s in the data base",
		auto_left_margin ? "true" : "false");
	ptextln(temp);
    } else {
	/* test (bw) */
	ptextln("(bw) should not be set.");
	for (i = 12; i < columns; i++)
	    put_str("\b");
	if (delete_character) {
	    for (i = 0; i < 4; i++)
		tc_putp(delete_character);
	} else {
	    put_str("   ");
	}
	put_crlf();
	sprintf(temp, "(bw) Auto-left-margin is %s in the data base",
		auto_left_margin ? "true" : "false");
	ptextln(temp);
    }
    generic_done_message(t, state, ch);
}

/*
**	subtest_tbc(test_list, status, ch)
**
**	test clear tabs (tbc)
*/
static void
subtest_tbc(
	       TestList * t,
	       int *state,
	       int *ch)
{
    int tabat;			/* the tab spacing we end up with */
    int i;

    if (clear_all_tabs && !set_tab) {
	ptext("(tbc) Clear-all-tabs is defined but (hts) set-tab is not.  ");
	ptext("Once the tabs are cleared there is no way to set them.  ");
    } else if (clear_all_tabs) {
	tabat = set_tab ? 8 : init_tabs;
	tc_putp(clear_all_tabs);
	ptext("Clear tabs (tbc)");
	go_home();
	put_crlf();
	putchp('\t');
	putchp('T');
	go_home();
	put_newlines(2);
	for (i = 0; i < columns; i++) {
	    if (i == tabat) {
		putchp('T');
	    } else {
		putchp('.');
	    }
	}
	go_home();
	ptext("\n\n\nIf the above two lines have T's in the same column then (tbc) has failed.  ");
    } else {
	ptext("(tbc) Clear-all-tabs is not defined.  ");
    }
    generic_done_message(t, state, ch);
}

/*
**	subtest_hts(test_list, status, ch)
**
**	(ht) and set tabs with (hts)
*/
static void
subtest_hts(
	       TestList * t,
	       int *state,
	       int *ch)
{
    int tabat;			/* the tab spacing we end up with */
    int i;

    tabat = init_tabs;
    if (set_tab) {
	ptext("Tabs set with (hts)");
	put_crlf();
	for (i = 1; i < columns; i++) {
	    if (i % 8 == 1) {
		tc_putp(set_tab);
	    }
	    putchp(' ');
	}
	tabat = 8;
    } else {
	sprintf(temp,
		"(hts) Set-tabs not defined.  (it) Initial-tabs at %d",
		init_tabs);
	ptext(temp);
    }
    go_home();
    put_newlines(2);
    if (tabat <= 0) {
	tabat = 8;
    }
    for (i = tabat; i < columns; i += tabat) {
	putchp('\t');
	putchp('T');
    }
    go_home();
    put_newlines(3);
    for (i = 1; i < columns; i++) {
	putchp('.');
    }
    go_home();
    put_newlines(3);
    for (i = tabat; i < columns; i += tabat) {
	putchp('\t');
	putchp('T');
    }
    go_home();
    put_newlines(4);
    putchp('.');
    for (i = 2; i < columns; i++) {
	if (i % tabat == 1) {
	    putchp('T');
	} else {
	    putchp('.');
	}
    }
    go_home();
    put_newlines(5);
    if (set_tab) {
	ptextln("If the last two lines are not the same then (hts) has failed.");
    } else if (init_tabs > 0) {
	ptextln("If the last two lines are not the same then (it) is wrong.");
    } else {
	ptextln("If the last two lines are the same then maybe you do have tabs and (it) should be changed.");
    }
    generic_done_message(t, state, ch);
}

/*
**	subtest_xt(test_list, status, ch)
**
**	(xt) glitch
*/
static void
subtest_xt(
	      TestList * t,
	      int *state,
	      int *ch)
{
    int tabat;			/* the tab spacing we end up with */

    tabat = set_tab ? 8 : init_tabs;

    if (!over_strike && (tabat > 0)) {
	int cc;

	ptext("(xt) should not ");
	put_cr();
	ptext("(xt) should");
	cc = char_count;
	while (cc < 16) {
	    putchp('\t');
	    cc = ((cc / tabat) + 1) * tabat;
	}
	putln("be set.");
	sprintf(temp, "(xt) Destructive-tab is %s in the data base.",
		dest_tabs_magic_smso ? "true" : "false");
	ptextln(temp);
	generic_done_message(t, state, ch);
    }
}

/*
**	subtest_cbt(test_list, status, ch)
**
**	(cbt) back tab
*/
static void
subtest_cbt(
	       TestList * t,
	       int *state,
	       int *ch)
{
    if (back_tab) {
	int i;

	put_clear();
	ptext("Back-tab (cbt)");
	go_home();
	put_crlf();
	for (i = 1; i < columns; i++) {
	    putchp(' ');
	}
	for (i = 0; i < columns; i += 8) {
	    tc_putp(back_tab);
	    putchp('T');
	    tc_putp(back_tab);
	}
	go_home();
	put_newlines(2);
	for (i = 1; i < columns; i++) {
	    if (i % 8 == 1) {
		putchp('T');
	    } else {
		putchp(' ');
	    }
	}
	go_home();
	put_newlines(3);
	ptextln("The preceding two lines should be the same.");
    } else {
	ptextln("(cbt) Back-tab not present");
    }
    generic_done_message(t, state, ch);
}

/*
**	subtest_xenl(test_list, status, ch)
**
**	(xenl) eat newline glitch
*/
static void
subtest_xenl(
		TestList * t,
		int *state,
		int *ch)
{
    if (over_strike) {
	int i, j, k;

	/* test (xenl) on overstrike terminals */
	if (!can_go_home || !can_clear_screen) {
	    ptextln("(xenl) Newline-glitch not tested, can't home cursor and clear.");
	    generic_done_message(t, state, ch);
	    return;
	}
	put_clear();
	/*
	   this test must be done in raw mode.  Otherwise UNIX will
	   translate CR to CRLF.
	 */
	if (stty_query(TTY_OUT_TRANS))
	    tty_raw(1, char_mask);
	ptext("\nreset (xenl). Does ");
	i = char_count;
	put_str("not ignore CR, does ");
	k = char_count;
	put_str("not ignore LF");
	go_home();
	for (j = 0; j < columns; j++)
	    put_this(' ');
	put_cr();
	for (j = 0; j < i; j++)
	    putchp(' ');
	put_str("@@@\n@@");
	go_home();
	for (j = 0; j < columns; j++)
	    put_this(' ');
	put_lf();
	for (j = 0; j < k; j++)
	    putchp(' ');
	put_str("@@@\r@@");
	tty_set();
	go_home();
	put_newlines(4);
	sprintf(temp, "(xenl) Newline-glitch is %s in the data base",
		eat_newline_glitch ? "true" : "false");
	ptextln(temp);
    } else {
	int j;

	/* test (xenl) when (os) is reset */
	if (!can_go_home) {
	    ptextln("(xenl) Newline-glitch not tested, can't home cursor");
	    generic_done_message(t, state, ch);
	    return;
	}
	/* (xenl) test */
	put_clear();
	/*
	   this test must be done in raw mode.  Otherwise
	   UNIX will translate CR to CRLF.
	 */
	if (stty_query(TTY_OUT_TRANS))
	    tty_raw(1, char_mask);
	for (j = 0; j < columns; j++)
	    put_this(' ');
	put_cr();
	ptext("(xenl) should be set. Does not ignore CR");
	go_home();
	put_crlf();
	for (j = 0; j < columns; j++)
	    put_this(' ');
	put_lf();		/* test (cud1) */
	ptext("(xenl) should be set. Ignores (cud1)");
	go_home();
	put_newlines(3);
	if (scroll_forward && cursor_down &&
	    strcmp(scroll_forward, cursor_down)) {
	    for (j = 0; j < columns; j++)
		put_this(' ');
	    put_ind();		/* test (ind) */
	    ptext("(xenl) should be set. Ignores (ind)");
	    go_home();
	    put_newlines(5);
	}
	tty_set();
	ptextln("If you don't see text above telling you to set it, (xenl) should be false");
	sprintf(temp, "(xenl) Newline-glitch is %s in the data base",
		eat_newline_glitch ? "true" : "false");
	ptextln(temp);
    }
    generic_done_message(t, state, ch);
}

/*
**	subtest_eo(test_list, status, ch)
**
**	(eo) erase overstrike
*/
static void
subtest_eo(
	      TestList * t,
	      int *state,
	      int *ch)
{
    if (transparent_underline || over_strike || underline_char) {
	ptext("(eo) should ");
	if (underline_char) {
	    ucprint("not");
	} else {
	    uprint("not");
	}
	put_cr();
	ptextln("(eo) should     be set");
	sprintf(temp, "\n(eo) Erase-overstrike is %s in the data base",
		erase_overstrike ? "true" : "false");
	ptextln(temp);
	generic_done_message(t, state, ch);
    }
}

/*
**	subtest_xmc(test_list, status, ch)
**
**	(xmc) magic cookie glitch
*/
static void
subtest_xmc(
	       TestList * t,
	       int *state,
	       int *ch)
{
    if (enter_standout_mode) {
	int i, j;

	sprintf(temp, "\n(xmc) Magic-cookie-glitch is %d in the data base", magic_cookie_glitch);
	ptextln(temp);
	j = magic_cookie_glitch * 8;
	for (i = 0; i < j; i++) {
	    put_str(" ");
	}
	ptextln("        These two lines should line up.");
	if (j > 0) {
	    char_count += j;
	}
	for (i = 0; i < 4; i++) {
	    put_mode(enter_standout_mode);
	    putchp(' ');
	    put_mode(exit_standout_mode);
	    putchp(' ');
	}
	ptextln("These two lines should line up.");
	ptext("If they don't line up then (xmc) magic-cookie-glitch should be greater than zero.  ");
	generic_done_message(t, state, ch);
    }
}

/*
**	subtest_xhp(test_list, status, ch)
**
**	(xhp) erase does not clear standout mode
*/
static void
subtest_xhp(
	       TestList * t,
	       int *state,
	       int *ch)
{
    if (enter_standout_mode) {
	put_crlf();
	put_mode(enter_standout_mode);
	put_str("Stand out");
	put_mode(exit_standout_mode);
	put_cr();
	ptextln("If any part of this line is standout then (xhp) should be set.");
	sprintf(temp, "(xhp) Erase-standout-glitch is %s in the data base",
		ceol_standout_glitch ? "true" : "false");
	ptextln(temp);
	generic_done_message(t, state, ch);
    }
}

/*
**	subtest_mir(test_list, status, ch)
**
**	(mir) move in insert mode
*/
static void
subtest_mir(
	       TestList * t,
	       int *state,
	       int *ch)
{
    if (enter_insert_mode && exit_insert_mode && cursor_address) {
	int i;
	char *s;

	put_clear();
	i = line_count;
	put_str("\nXXX\nXXX\nXXX\nXXX");
	tc_putp(enter_insert_mode);
	s = TPARM_2(cursor_address, i + 1, 0);
	tputs(s, lines, tc_putch);
	putchp('X');
	s = TPARM_2(cursor_address, i + 2, 1);
	tputs(s, lines, tc_putch);
	putchp('X');
	s = TPARM_2(cursor_address, i + 3, 2);
	tputs(s, lines, tc_putch);
	putchp('X');
	s = TPARM_2(cursor_address, i + 4, 3);
	tputs(s, lines, tc_putch);
	putchp('X');
	tc_putp(exit_insert_mode);
	put_newlines(2);
	ptextln("If you see a 4 by 4 block of X's then (mir) should be true.");
	sprintf(temp, "(mir) Move-in-insert-mode is %s in the data base",
		move_insert_mode ? "true" : "false");
	ptextln(temp);
    } else {
	ptext("(mir) Move-in-insert-mode not tested, ");
	if (!enter_insert_mode) {
	    ptext("(smir) ");
	}
	if (!exit_insert_mode) {
	    ptext("(rmir) ");
	}
	if (!cursor_address) {
	    ptext("(cup) ");
	}
	ptext("not present.  ");
    }
    generic_done_message(t, state, ch);
}

/*
**	subtest_msgr(test_list, status, ch)
**
**	(msgr) move in sgr mode
*/
static void
subtest_msgr(
		TestList * t,
		int *state,
		int *ch)
{
    if (cursor_address &&
	((enter_standout_mode && exit_standout_mode) ||
	 (enter_alt_charset_mode && exit_alt_charset_mode))) {
	int i;

	put_crlf();
	i = line_count + 1;
	tputs(TPARM_2(cursor_address, i, 0), lines, tc_putch);
	put_mode(enter_alt_charset_mode);
	put_crlf();
	/*
	   some versions of the wy-120 can not clear lines or
	   screen when in alt charset mode.  If (el) and (ed)
	   are defined then I can test them.  If they are not
	   defined then they can not break (msgr)
	 */
	tc_putp(clr_eos);
	tc_putp(clr_eol);
	put_mode(exit_alt_charset_mode);
	put_mode(enter_standout_mode);
	putchp('X');
	tputs(TPARM_2(cursor_address, i + 2, 1), lines, tc_putch);
	putchp('X');
	tputs(TPARM_2(cursor_address, i + 3, 2), lines, tc_putch);
	putchp('X');
	tputs(TPARM_2(cursor_address, i + 4, 3), lines, tc_putch);
	putchp('X');
	put_mode(exit_standout_mode);
	put_crlf();
	tc_putp(clr_eos);	/* OK if missing */
	put_crlf();
	ptextln("If you see a diagonal line of standout X's then (msgr) should be true.  If any of the blanks are standout then (msgr) should be false.");
	sprintf(temp, "(msgr) Move-in-SGR-mode is %s in the data base",
		move_standout_mode ? "true" : "false");
	ptextln(temp);
    } else {
	ptextln("(smso) (rmso) (smacs) (rmacs) missing; (msgr) Move-in-SGR-mode not tested.");
    }
    generic_done_message(t, state, ch);
}

/*
**	subtest_in(test_list, status, ch)
**
**	(in) insert null glitch
*/
static void
subtest_in(
	      TestList * t,
	      int *state,
	      int *ch)
{
    if (enter_insert_mode && exit_insert_mode) {
	ptextln("\nTesting (in) with (smir) and (rmir)");
	putln("\tIf these two lines line up ...");
	put_str("\tIf these two lines line up ...");
	put_cr();
	tc_putp(enter_insert_mode);
	putchp(' ');
	tc_putp(exit_insert_mode);
	ptext("\nthen (in) should be set.  ");
	sprintf(temp,
		"(in) Insert-null-glitch is %s in the data base.",
		insert_null_glitch ? "true" : "false");
	ptextln(temp);
	generic_done_message(t, state, ch);
    }
}

/*
**	subtest_dadb(test_list, status, ch)
**
**	(da) (db) data above, (db) data below
*/
static void
subtest_dadb(
		TestList * t,
		int *state,
		int *ch)
{
    if (can_clear_screen && scroll_reverse && scroll_forward) {
	put_clear();
	if (scroll_reverse)
	    ptext("(da) Data-above should be set\r");
	home_down();
	if (scroll_forward)
	    ptext("(db) Data-below should be set\r");
	tc_putp(scroll_forward);
	go_home();
	tc_putp(scroll_reverse);
	tc_putp(scroll_reverse);
	home_down();
	tc_putp(scroll_forward);
	go_home();
	ptextln("\n\n\n\n\nIf the top line is blank then (da) should be false.");
	ptextln("If the bottom line is blank then (db) should be false.");
	sprintf(temp,
		"\n(da) Data-above is %s, and (db) Data-below is %s, in the data base.",
		memory_above ? "true" : "false",
		memory_below ? "true" : "false");
	ptextln(temp);
	line_count = lines;
    } else {
	ptextln("(da) Data-above, (db) Data-below not tested, scrolls or (clear) is missing.");
    }
    generic_done_message(t, state, ch);
}

TestList mode_test_list[] =
{
    MY_EDIT_MENU
    {MENU_NEXT, 3, "os", NULL, NULL, subtest_os, NULL},
    {MENU_NEXT, 1, "rmam", NULL, NULL, subtest_rmam, NULL},
    {MENU_NEXT, 1, "smam", NULL, NULL, subtest_smam, NULL},
    {MENU_NEXT, 1, "am", NULL, NULL, subtest_am, NULL},
    {MENU_NEXT, 3, "ul", NULL, NULL, subtest_ul, NULL},
    {MENU_NEXT, 3, "uc", NULL, NULL, subtest_uc, NULL},
    {MENU_NEXT, 3, "bw", NULL, NULL, subtest_bw, NULL},
    {MENU_NEXT, 4, "xenl", NULL, NULL, subtest_xenl, NULL},
    {MENU_NEXT, 3, "eo", NULL, NULL, subtest_eo, NULL},
    {MENU_NEXT, 3, "xmc", NULL, NULL, subtest_xmc, NULL},
    {MENU_NEXT, 3, "xhp", NULL, NULL, subtest_xhp, NULL},
    {MENU_NEXT, 6, "mir", NULL, NULL, subtest_mir, NULL},
    {MENU_NEXT, 6, "msgr", NULL, NULL, subtest_msgr, NULL},
    {MENU_NEXT | MENU_CLEAR, 0, "tbc", "it", NULL, subtest_tbc, NULL},
    {MENU_NEXT | MENU_CLEAR, 0, "hts", "it", NULL, subtest_hts, NULL},
    {MENU_NEXT, 4, "xt", "it", NULL, subtest_xt, NULL},
    {MENU_NEXT, 1, "cbt", "it", NULL, subtest_cbt, NULL},
    {MENU_NEXT, 6, "in", NULL, NULL, subtest_in, NULL},
    {MENU_NEXT, 1, "da) (db", NULL, NULL, subtest_dadb, NULL},
    {MENU_LAST, 0, NULL, NULL, NULL, NULL, NULL}
};
