#undef __STRICT_ANSI__		/* we need to use fileno() */
#include <stdlib.h>
# include <unistd.h>
# include <fcntl.h>

#include "private.h"

typedef struct {
	const char terminator;
	tinyrl_vt100_escape_t code;
} vt100_decode_t;

/* This table maps the vt100 escape codes to an enumeration */
static vt100_decode_t cmds[] = {
	{'A', tinyrl_vt100_CURSOR_UP},
	{'B', tinyrl_vt100_CURSOR_DOWN},
	{'C', tinyrl_vt100_CURSOR_RIGHT},
	{'D', tinyrl_vt100_CURSOR_LEFT},
};

/*--------------------------------------------------------- */
static void _tinyrl_vt100_setInputNonBlocking(const tinyrl_vt100_t * this)
{
#if defined(STDIN_FILENO)
	int flags = (fcntl(STDIN_FILENO, F_GETFL, 0) | O_NONBLOCK);
	fcntl(STDIN_FILENO, F_SETFL, flags);
#endif				/* STDIN_FILENO */
}

/*--------------------------------------------------------- */
static void _tinyrl_vt100_setInputBlocking(const tinyrl_vt100_t * this)
{
#if defined(STDIN_FILENO)
	int flags = (fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
	fcntl(STDIN_FILENO, F_SETFL, flags);
#endif				/* STDIN_FILENO */
}

/*--------------------------------------------------------- */
tinyrl_vt100_escape_t tinyrl_vt100_escape_decode(const tinyrl_vt100_t * this)
{
	tinyrl_vt100_escape_t result = tinyrl_vt100_UNKNOWN;
	char sequence[10], *p = sequence;
	int c;
	unsigned i;
	/* before the while loop, set the input as non-blocking */
	_tinyrl_vt100_setInputNonBlocking(this);

	/* dump the control sequence into our sequence buffer 
	 * ANSI standard control sequences will end 
	 * with a character between 64 - 126
	 */
	while (1) {
		c = getc(this->istream);

		/* ignore no-character condition */
		if (-1 != c) {
			*p++ = (c & 0xFF);
			if ((c != '[') && (c > 63)) {
				/* this is an ANSI control sequence terminator code */
				result = tinyrl_vt100_CURSOR_UP;	/* just a non-UNKNOWN value */
				break;
			}
		} else {
			result = tinyrl_vt100_UNKNOWN;
			break;
		}
	}
	/* terminate the string (for debug purposes) */
	*p = '\0';

	/* restore the blocking status */
	_tinyrl_vt100_setInputBlocking(this);

	if (tinyrl_vt100_UNKNOWN != result) {
		/* now decode the sequence */
		for (i = 0; i < sizeof(cmds) / sizeof(vt100_decode_t); i++) {
			if (cmds[i].terminator == c) {
				/* found the code in the lookup table */
				result = cmds[i].code;
				break;
			}
		}
	}

	return result;
}

/*-------------------------------------------------------- */
int tinyrl_vt100_printf(const tinyrl_vt100_t * this, const char *fmt, ...)
{
	va_list args;
	int len;

	va_start(args, fmt);
	len = tinyrl_vt100_vprintf(this, fmt, args);
	va_end(args);

	return len;
}

/*-------------------------------------------------------- */
int
tinyrl_vt100_vprintf(const tinyrl_vt100_t * this, const char *fmt, va_list args)
{
	return vfprintf(this->ostream, fmt, args);
}

/*-------------------------------------------------------- */
int tinyrl_vt100_getchar(const tinyrl_vt100_t * this)
{
	return getc(this->istream);
}

/*-------------------------------------------------------- */
int tinyrl_vt100_oflush(const tinyrl_vt100_t * this)
{
	return fflush(this->ostream);
}

/*-------------------------------------------------------- */
int tinyrl_vt100_ierror(const tinyrl_vt100_t * this)
{
	return ferror(this->istream);
}

/*-------------------------------------------------------- */
int tinyrl_vt100_oerror(const tinyrl_vt100_t * this)
{
	return ferror(this->ostream);
}

/*-------------------------------------------------------- */
int tinyrl_vt100_ieof(const tinyrl_vt100_t * this)
{
	return feof(this->istream);
}

/*-------------------------------------------------------- */
int tinyrl_vt100_eof(const tinyrl_vt100_t * this)
{
	return feof(this->istream);
}

/*-------------------------------------------------------- */
unsigned tinyrl_vt100__get_width(const tinyrl_vt100_t * this)
{
	this = this;
	/* hard code until we suss out how to do it properly */
	return 80;
}

/*-------------------------------------------------------- */
static void
tinyrl_vt100_init(tinyrl_vt100_t * this, FILE * istream, FILE * ostream)
{
	this->istream = istream;
	this->ostream = ostream;
}

/*-------------------------------------------------------- */
static void tinyrl_vt100_fini(tinyrl_vt100_t * this)
{
	/* nothing to do yet... */
	this = this;
}

/*-------------------------------------------------------- */
tinyrl_vt100_t *tinyrl_vt100_new(FILE * istream, FILE * ostream)
{
	tinyrl_vt100_t *this = NULL;

	this = malloc(sizeof(tinyrl_vt100_t));
	if (NULL != this) {
		tinyrl_vt100_init(this, istream, ostream);
	}

	return this;
}

/*-------------------------------------------------------- */
void tinyrl_vt100_delete(tinyrl_vt100_t * this)
{
	tinyrl_vt100_fini(this);
	/* release the memory */
	free(this);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_ding(const tinyrl_vt100_t * this)
{
	tinyrl_vt100_printf(this, "%c", KEY_BEL);
	(void)tinyrl_vt100_oflush(this);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_attribute_reset(const tinyrl_vt100_t * this)
{
	tinyrl_vt100_printf(this, "%c[0m", KEY_ESC);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_attribute_bright(const tinyrl_vt100_t * this)
{
	tinyrl_vt100_printf(this, "%c[1m", KEY_ESC);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_attribute_dim(const tinyrl_vt100_t * this)
{
	tinyrl_vt100_printf(this, "%c[2m", KEY_ESC);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_attribute_underscore(const tinyrl_vt100_t * this)
{
	tinyrl_vt100_printf(this, "%c[4m", KEY_ESC);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_attribute_blink(const tinyrl_vt100_t * this)
{
	tinyrl_vt100_printf(this, "%c[5m", KEY_ESC);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_attribute_reverse(const tinyrl_vt100_t * this)
{
	tinyrl_vt100_printf(this, "%c[7m", KEY_ESC);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_attribute_hidden(const tinyrl_vt100_t * this)
{
	tinyrl_vt100_printf(this, "%c[8m", KEY_ESC);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_erase_line(const tinyrl_vt100_t * this)
{
	tinyrl_vt100_printf(this, "%c[2K", KEY_ESC);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_clear_screen(const tinyrl_vt100_t * this)
{
	tinyrl_vt100_printf(this, "%c[2J", KEY_ESC);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_cursor_save(const tinyrl_vt100_t * this)
{
	tinyrl_vt100_printf(this, "%c7", KEY_ESC);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_cursor_restore(const tinyrl_vt100_t * this)
{
	tinyrl_vt100_printf(this, "%c8", KEY_ESC);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_cursor_forward(const tinyrl_vt100_t * this, unsigned count)
{
	tinyrl_vt100_printf(this, "%c[%dC", KEY_ESC, count);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_cursor_back(const tinyrl_vt100_t * this, unsigned count)
{
	tinyrl_vt100_printf(this, "%c[%dD", KEY_ESC, count);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_cursor_up(const tinyrl_vt100_t * this, unsigned count)
{
	tinyrl_vt100_printf(this, "%c[%dA", KEY_ESC, count);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_cursor_down(const tinyrl_vt100_t * this, unsigned count)
{
	tinyrl_vt100_printf(this, "%c[%dB", KEY_ESC, count);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_cursor_home(const tinyrl_vt100_t * this)
{
	tinyrl_vt100_printf(this, "%c[H", KEY_ESC);
}

/*-------------------------------------------------------- */
void tinyrl_vt100_erase(const tinyrl_vt100_t * this, unsigned count)
{
	tinyrl_vt100_printf(this, "%c[%dP", KEY_ESC, count);
}

/*-------------------------------------------------------- */
void tinyrl_vt100__set_istream(tinyrl_vt100_t * this, FILE * istream)
{
	this->istream = istream;
}

/*-------------------------------------------------------- */
FILE *tinyrl_vt100__get_istream(const tinyrl_vt100_t * this)
{
	return this->istream;
}

/*-------------------------------------------------------- */
FILE *tinyrl_vt100__get_ostream(const tinyrl_vt100_t * this)
{
	return this->ostream;
}

/*-------------------------------------------------------- */