import core.stdc.stdio: fwrite; //fwrite
import std.stdio;
import core.sys.posix.termios;
import core.sys.posix.unistd; // has isatty()


// https://www.cs.uleth.ca/~holzmann/C/system/ttyraw.c
// https://zig.news/lhp/want-to-create-a-tui-application-the-basics-of-uncooked-terminal-io-17gm
// https://gitlab.com/dejan/iv.d/-/blob/master/rawtty.d?ref_type=heads
// https://github.com/adamdruppe/arsd/blob/master/terminal.d

struct TUI {
	File ttyFile;
	termios origTermios; // original termios saved so that we can restore it at the end.
	termios tuiTermios; // termios structure modified for our needs.
}

void enterCookMode(ref TUI tui) {
// Right before we return to the OS we change terminal settings back to where they were before we
	// started the application.
	if (tcsetattr(tui.ttyFile.fileno, TCSAFLUSH, &tui.origTermios) < 0) {
		writeln("Error - can't restore TTY");
	}

	write("\033[?1049l"); // Disable alternative buffer.
	write("\033[?47l"); // Restore screen.
	writeln("\033[u"); // Restore cursor position.
}

void main() {
	writeln("hello");

	TUI tui;
	tui.ttyFile = File("/dev/tty", "rw"); // open for writing
	// File automatically calls close
	writeln("fileD:", tui.ttyFile.fileno());
	writeln();

	if (!isatty(tui.ttyFile.fileno())) {
		return;
	}

	if (!isatty(stdin.fileno)) {
		return;
	}

	if (!isatty(stdout.fileno)) {
		return;
	}

	/*
    tcflag_t = uint;
    struct termios
    {
        tcflag_t   c_iflag;
        tcflag_t   c_oflag;
        tcflag_t   c_cflag;
        tcflag_t   c_lflag;
        cc_t       c_line;
        cc_t[NCCS] c_cc;
        speed_t    c_ispeed;
        speed_t    c_ospeed;
    }

    */

	auto origTermios = termios();

	int iret = tcgetattr(tui.ttyFile.fileno, &origTermios);
	tui.origTermios = origTermios;
	writeln(origTermios);

	// Copy the original termios structure into a new value that we modify later below.
	auto tuiTermios = origTermios;

	writeln(tuiTermios.c_lflag);
	//   ECHO: Stop the terminal from displaying pressed keys.
	// ICANON: Disable canonical ("cooked") input mode. Allows us to read inputs
	//         byte-wise instead of line-wise.
	//   ISIG: Disable signals for Ctrl-C (SIGINT) and Ctrl-Z (SIGTSTP), so we
	//         can handle them as "normal" escape sequences.
	// IEXTEN: Disable input preprocessing. This allows us to handle Ctrl-V,
	//         which would otherwise be intercepted by some terminals.
	tuiTermios.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN);
	writeln(tuiTermios.c_lflag);

	writeln(tuiTermios.c_iflag);
	//   IXON: Disable software control flow. This allows us to handle Ctrl-S
	//         and Ctrl-Q.
	//  ICRNL: Disable converting carriage returns to newlines. Allows us to
	//         handle Ctrl-J and Ctrl-M.
	// BRKINT: Disable converting sending SIGINT on break conditions. Likely has
	//         no effect on anything remotely modern.
	//  INPCK: Disable parity checking. Likely has no effect on anything
	//         remotely modern.
	// ISTRIP: Disable stripping the 8th bit of characters. Likely has no effect
	//         on anything remotely modern.
	tuiTermios.c_iflag &= ~(IXON | ICRNL | BRKINT | INPCK | ISTRIP);
	writeln(tuiTermios.c_iflag);

	writeln(tuiTermios.c_oflag);
	// Disable output processing. Common output processing includes prefixing
	// newline with a carriage return.
	tuiTermios.c_oflag &= ~(OPOST);
	writeln(tuiTermios.c_oflag);

	// Set the character size to 8 bits per byte. Likely has no efffect on
	// anything remotely modern.
	tuiTermios.c_cflag |= CS8;

	/*
    Two fields of the termios structs cc member deserve special attention, vtime and vmin. These are used
    to control the read syscall when getting input from the terminal interface. The first sets the timeout,
    after which the syscall will return, the second the minimum amount of bytes it reads before returning.
    Note that timeout takes precedence over the minimum byte count.
    */
	tuiTermios.c_cc[VTIME] = 0;
	tuiTermios.c_cc[VMIN] = 1;
	tui.tuiTermios = tuiTermios;

	// Finally set the terminal attributes:
	if (tcsetattr(tui.ttyFile.fileno, TCSAFLUSH, &tuiTermios) < 0) {
		writeln("Error - the terminal can't enter the RAW mode.");
	}

	auto readBuf = new char[1];
	while (true) {
		auto buffer = tui.ttyFile.rawRead(readBuf);
		if (buffer[0] == 'q') {
			enterCookMode(tui);
			// exit
			return;
		} else if (buffer[0] == '\x1B') {
			writeln("input: excape\n");
		} else if (buffer[0] == '\n' || buffer[0] == '\r') {
			writeln("input: return\n");
		} else {
			writeln("input:", buffer[0]);
		}
	}

	enterCookMode(tui);
}