MODULE sort INTERFACE;

{*
	Este módulo proporciona los procedimientos y funciones relacionados
	con la ordenación de los datos.
*}

export sort = (bubbleSortAlphabetical, bubbleSortByMarks);

IMPORT
	
	filesystem;
	
{*
	Implementa el método de la burbuja para ordenar
	el array de alumnos link^ alfabéticamente.
*}
procedure bubbleSortAlphabetical(link:PtrToDump);

{*
	Implementa el método de la burbuja para ordenar
	el array de alumnos link^ mediante sus notas.
	option indica la convocatoria para la cual se
	está haciendo el ordenamiento (0 para Febrero/Junio,
	1 para Septiembre y 2 para Diciembre).
*}
procedure bubbleSortByMarks(link:PtrToDump; option:integer);

{*
	Compara los login a y b y devuelve -1 si a < b,
	1 si a > b o 0 si a = b.
*}
function compareLogin(protected var a, b:array of char):integer;

{*
	Compara las cadenas a y b y devuelve -1 si a < b,
	1 si a > b o 0 si a = b.
*}
function compareString(protected var a, b:string):integer;

{*
	Es utilizada por bubbleSortAlphabetical, devuelve true
	si a y b deben ser intercambiados o false en caso contrario.
*}
function needSwapAlphabetical(a, b:PtrToStudent):boolean;

{*
	Es utilizada por bubbleSortAlphabetical, devuelve true
	si a y b deben ser intercambiados o false en caso contrario.
*}
function needSwapByMarks(a, b:PtrToStudent; option:integer):boolean;

{*
	Comprueba si el caracter en las posiciones second -1 y second de 
	str (2 bytes) está en la variable global wanted y lo intercambia 
	por su contrapartida en la variable global sanitized, de un byte, 
	y le asigna el valor 255 al segundo byte.
*}
procedure sanitizeChar(var str:string; second:integer);

{*
	Pasa a minúsculas los caracteres de str y llama
	a sanitizeChar si encuentra un byte susceptible
	de ser la primera parte de un caracter de 2 bytes
	de los presentes en la variable global wanted.
*}
procedure sanitizeString(var str:string);

{*
	Intercambia la zona a la que apuntan a y b.
*}
procedure swap(var a, b:PtrToStudent);

END.


MODULE sort IMPLEMENTATION;

CONST
	
	{*
		Diferencia entre las letras mayúsculas y las minúsculas
		del alfabeto inglés en ASCII y UTF-8.
	*}
	ASCII_UPPER_TO_LOWER	= 32;
	{*
		Pesos de las notas práctica y teórica a la hora de calcular
		la global, mediante una media ponderada.
	*}
	WEIGHT_PRACTICE			= 2;
	WEIGHT_THEORY			= 8;
	
VAR
	
	{*
		Arrays globales para almacenar las correspondencias
		entre caracteres de 2 y 1 byte. Son globales para evitar
		tener que inicializarlos cada vez que se llama a la función
		sanitizeChar (se inicializan al cargar el módulo por
		primera vez).
	*}
	sanitized:array[0..11] of char;
	wanted:array[0..11] of string(2);
		
procedure bubbleSortAlphabetical;
var
	
	i:integer;
	ordered:boolean;
	
begin
	
	repeat
		
		ordered := true;
		
		for i := 0 to link^.eod - 1 do
		begin
			
			if needSwapAlphabetical(link^[i], link^[i + 1]) then
			begin
				
				swap(link^[i], link^[i + 1]);
				
				ordered := false;
				
			end;
			
		end;
		
	until ordered;
	
end;

procedure bubbleSortByMarks;
var
	
	i:integer;
	ordered:boolean;
	
begin
	
	repeat
		
		ordered := true;
		
		for i := 0 to link^.eod - 1 do
		begin
			
			if needSwapByMarks(link^[i], link^[i + 1], option) then
			begin
				
				swap(link^[i], link^[i + 1]);
				
				ordered := false;
				
			end;
			
		end;
		
	until ordered;
	
end;

function compareLogin;
var
	
	done:integer value 0;
	i:integer value 0;
	
begin
	
	while (done = 0) and (i < 8) do
	begin
		
		if a[i] < b[i] then done := -1
		else if a[i] > b[i] then done := 1;
		;
		
		i := i + 1;
		
	end;
	
	compareLogin := done;
	
end;

function compareString;
var
	
	aCopy, bCopy:string(40);
	
begin
	
	aCopy := a;
	bCopy := b;
	
	sanitizeString(aCopy);
	sanitizeString(bCopy);
	
	if aCopy < bCopy then compareString := -1
	else if aCopy > bCopy then compareString := 1
	else compareString := 0;
	;
	
end;

function needSwapAlphabetical;
var
	
	compared:integer;
	
begin
	
	compared := compareString(a^.surname, b^.surname);
	
	if compared = 1 then needSwapAlphabetical := true
	else if compared = -1 then needSwapAlphabetical := false
	else
	begin
		
		compared := compareString(a^.name, b^.name);
		
		if compared = 1 then needSwapAlphabetical := true
		else if compared = -1 then needSwapAlphabetical := false
		else
		begin
			
			if compareLogin(a^.login, b^.login) = 1 then needSwapAlphabetical := true
			else needSwapAlphabetical := false;
			
		end;
		;
		
	end;
	;
	
end;

function needSwapByMarks;
var
	
	globalA, globalB:real;
	
begin
	
	globalA := (a^.marks[option].practice * WEIGHT_PRACTICE 
				+ a^.marks[option].theory * WEIGHT_THEORY)
					/ (WEIGHT_PRACTICE + WEIGHT_THEORY);
	
	globalB := (b^.marks[option].practice * WEIGHT_PRACTICE 
				+ b^.marks[option].theory * WEIGHT_THEORY)
					/ (WEIGHT_PRACTICE + WEIGHT_THEORY);
	
	if globalA > globalB then needSwapByMarks := false
	else if globalA < globalB then needSwapByMarks := true
	else
	begin
		
		if a^.marks[option].theory > b^.marks[option].theory then
			needSwapByMarks := false
		else if a^.marks[option].theory < b^.marks[option].theory then
			needSwapByMarks := true
		else
		begin
			
			if a^.marks[option].practice > b^.marks[option].practice then
				needSwapByMarks := false
			else if a^.marks[option].practice < b^.marks[option].practice then
				needSwapByMarks := true
			else
				needSwapByMarks := needSwapAlphabetical(a, b);
			;
			
		end;
		;
		
	end;
	;
	
end;

procedure sanitizeChar;
var
	
	done:boolean value false;
	i:integer value 0;
	
begin
	
	while (not done) and (i < 12) do
	begin 
		
		if str[second] = wanted[i][2] then
		begin
			
			str[second - 1] := sanitized[i];
			str[second] := chr(255);
			
			done := true;
			
		end
		else i := i + 1;
		
	end;
	
end;

procedure sanitizeString;
var
	
	i:integer;
	
begin
	
	for i := 1 to length(str) - 1 do
	begin
		
		if str[i] = chr(195) then
		begin
			
			sanitizeChar(str, i + 1);
			
		end;
		
	end;
	
	for i := 1 to length(str) do
	begin
		
		if (ord(str[i]) > 64) and (ord(str[i]) < 91) then
		begin
			
			str[i] := chr(ord(str[i]) + ASCII_UPPER_TO_LOWER);
			
		end;
		
	end;
	
end;

procedure swap;
var
	
	aux:PtrToStudent;
	
begin
	
	aux := a;
	a := b;
	b := aux;
	
end;

to begin do
begin
	
	wanted[0] := 'á';
	wanted[1] := 'é';
	wanted[2] := 'í';
	wanted[3] := 'ó';
	wanted[4] := 'ú';
	wanted[5] := 'ñ';
	wanted[6] := 'Á';
	wanted[7] := 'É';
	wanted[8] := 'Í';
	wanted[9] := 'Ó';
	wanted[10] := 'Ú';
	wanted[11] := 'Ñ';
	
	sanitized[0] := 'a';
	sanitized[1] := 'e';
	sanitized[2] := 'i';
	sanitized[3] := 'o';
	sanitized[4] := 'u';
	sanitized[5] := 'n';
	sanitized[6] := 'a';
	sanitized[7] := 'e';
	sanitized[8] := 'i';
	sanitized[9] := 'o';
	sanitized[10] := 'u';
	sanitized[11] := 'n';
	
end;

END.