MODULE display INTERFACE;

{*
	Este módulo proporciona distintos procedimientos relacionados
	con el control de la terminal. Dichos procedimientos permiten
	borrar la pantalla, mover el cursor por la misma y escribir 
	con diferentes formatos (colores y texto en negrita).
	
	Este módulo ha sido probado con éxito en gnome-terminal (objetivo
	de la práctica), xterm y la terminal inicial de Linux (modo sin
	iniciar el servidor X, no se como denominarlo).
	
	NOTA: Para crear este módulo empecé por buscar información
	acerca de imprimir la secuencia chr(27) + '[2J' para borrar la
	pantalla, facilitada por el profesor de la asignatura en clase.
	A partir de ahí llegué a métodos similares para borrar la pantalla
	en C/C++ y otros lenguajes. Posteriormente encontré un módulo
	de ejemplo que circula entre los alumnos de FIC y que
	incluyo como ./doc/ansi.pas (parece que está escrito en
	Free Pascal), el cual me dio las claves para seguir buscando,
	hasta que encontré en diversas páginas de Internet
	la especificación del estándar ANSI X3.64 (o ECMA-48).
*}

export display = (	BOLD, clear, clearDisplay, clearDown, clearLine, downLines, goToXY, 
					memoLine, memoXY, moveHorizontally, moveMargin, returnToLine, 
					returnToXY, REVERSE, resetFormat, setError, setFormat, setMssg, setTitle);

CONST
	
	{Inicia las secuencias de escape ANSI.}
	ESC						= chr(27) + '[';
	{Códigos para el formato del texto.}
	BOLD					= 1;	
	FOREGROUND_BLUE			= 34;
	FOREGROUND_RED			= 31;
	REVERSE					= 7;
	{Márgenes y decoración en el título.}
	MARGIN_LEFT				= 10;
	MARGIN_UP				= 4;
	TITLE_LEFT_DECORATION	= '| ';
	TITLE_RIGHT_DECORATION	= ' |   ';
	
{*
	Limpia la pantalla. Pretende ser un análogo
	al clear de Linux.
*}
procedure clear;

{*
	"Inicia" la pantalla. Resetea los márgenes,
	limpia la pantalla y sitúa el cursor
	en el lugar inicial.
*}
procedure clearDisplay;

{*
	Limpia la pantalla a partir de la línea donde
	está el cursor, hacia abajo.
*}
procedure clearDown;

{*
	Borra el mensaje bajo el título de la aplicación.
*}
procedure clearMssg;

{*
	Sitúa el cursor al inicio de la línea en la que se encuentre
	(respetando el margen) y la limpia.
*}
procedure clearLine;

{*
	Mueve la "línea de escritura" lines líneas, hacia
	abajo si lines es positivo y hacia arriba si es negativo.
	Sitúa el cursor al comienzo de dicha línea (respetando el margen).
*}
procedure downLines(lines:integer);

{*
	Mueve el cursor a la línea y, columna x.
*}
procedure goToXY(x, y:integer);

{*
	Memoriza la "línea de escritura".
*}
procedure memoLine;

{*
	Memoriza la posición del cursor.
*}
procedure memoXY;

{*
	Mueve el cursor n columnas, hacia la
	derecha si n es positivo o hacia
	la izquierda si es negativo.
*}
procedure moveHorizontally(n:integer);

{*
	Mueve el margen izquierdo de la aplicación n columnas,
	hacia la derecha si n es positivo o hacia la izquierda
	si es negativo, y sitúa el cursor al principio
	de la línea (respetando dicho margen).
*}
procedure moveMargin(n:integer);

{*
	Establece "línea de escritura" en la línea
	memorizada por memoLine y situa el
	cursor al inicio de la misma (respetando
	el margen).
*}
procedure returnToLine;

{*
	Sitúa el cursor en la posición almacenada
	por memoXY.
*}
procedure returnToXY;

{*
	Establece el formato del texto a su valor por defecto.
*}
procedure resetFormat;

{*
	Hace sonar la campana del sistema (de estar esta activada) e
	imprime el mensaje error bajo el título de la aplicación.
*}
procedure setError(error:string);

{*
	Establece el formato del texto enviado a la terminal
	a partir de la llamada a este procedimiento. Format es
	un entero entre 1 y 47 (incluídos). Más información
	en la especificación del estándar ANSI X3.64
*}
procedure setFormat(format:integer);

{*
	Imprime el mensaje m bajo el título de la aplicación.
*}
procedure setMssg(m:string);

{*
	Establece title como título de la aplicación, rodeado
	por la decoración indicada por las constantes
	TITLE_LEFT_DECORATION y TITLE_RIGHT_DECORATION.
*}
procedure setTitle(title:string);

END.


MODULE display IMPLEMENTATION;

IMPORT

	standardoutput;
	
VAR
	
	lineToWriteIn:integer value MARGIN_UP;
	marginLeft:integer value MARGIN_LEFT;
	memo:integer value 1;
	
procedure clear;
begin
	
	write(chr(1), ESC, 'H', ESC, '2J');
	
end;

procedure clearDisplay;
begin
	
	marginLeft := MARGIN_LEFT;
	lineToWriteIn := MARGIN_UP;
	
	clear;
	
	goToXY(MARGIN_LEFT, MARGIN_UP);
	
end;

procedure clearDown;
begin
	
	write(ESC, 'J');
	
end;

procedure clearLine;
begin
	
	goToXY(marginLeft, lineToWriteIn);
	
	write(ESC, 'K');
	
end;

procedure clearMssg;
var
	
	i:integer;
	
begin
	
	memoXY;
	
	for i := 2 to MARGIN_UP - 1 do
	begin
		
		goToXY(1, i);
		write(ESC, 'K');
		
	end;
	
	returnToXY;
	
end;

procedure downLines;
begin
	
	lineToWriteIn := lineToWriteIn + lines;
	writeln;
	goToXY(marginLeft, lineToWriteIn);
	
end;

procedure goToXY;
begin
	
	write(ESC, y:0, ';', x:0, 'H');
	
end;

procedure memoLine;
begin
	
	memo := lineToWriteIn;
	
end;

procedure memoXY;
begin
	
	write(ESC, 's');
	
end;

procedure moveHorizontally;
begin
	
	if n > 0 then write(ESC, n:0, 'C')
	else write(ESC, abs(n):0, 'D');
	
end;

procedure moveMargin;
begin
	
	marginLeft := marginLeft + n;
	if marginLeft < 1 then marginLeft := 1;
	
	goToXY(marginLeft, lineToWriteIn);
	
end;

procedure returnToLine;
begin
	
	lineToWriteIn := memo;
	goToXY(marginLeft, lineToWriteIn);
	
end;

procedure returnToXY;
begin
	
	write(ESC, 'u');
	
end;

procedure resetFormat;
begin
	
	write(ESC, 0:0, 'm');
	
end;

procedure setError;
begin
	
	clearMssg;
	memoXY;
	goToXY(1, 2);
	setFormat(FOREGROUND_RED);
	write(chr(7), 'Error: ', error);
	resetFormat;
	returnToXY;
	
end;

procedure setFormat;
begin
	
	if (format > 0) and (format < 48) then write(ESC, format:0, 'm');
	
end;

procedure setMssg;
begin
	
	clearMssg;
	memoXY;
	goToXY(1, 2);
	setFormat(FOREGROUND_BLUE);
	write(m);
	resetFormat;
	returnToXY;
	
end;

procedure setTitle;
begin
	
	memoXY;
	goToXY(1, 1);
	write(TITLE_LEFT_DECORATION, title, TITLE_RIGHT_DECORATION);
	returnToXY;
	
end;

to end do writeln;

END.