// Alfaint.c
// Biblioteca de funciones para ordenar por orden alfabético cadenas de
// caracteres que utilicen UTF-8 o ANSI.
// Autor: Francisco Lloret López-Nieto, e-mail: pcmstr@terra.es
// Este librería se distribuye bajo licencia GPL versión 2 o posterior.


#include <string.h>
#include <math.h>


// Valores de letras. Estos códigos son los mismos en ANSI que en UTF-8
// aunque en UTF-8 al tener un código superior a 127 ocupan 2 bytes.

#define AIA 224; // a minuscula con acento abierto
#define AIC 225; // a minuscula con acento cerrado
#define AIF 226; // a minuscula con acento circunflejo
#define AIT 227; // a minuscula con tilde
#define AID 228; // a minuscula con dieresis
#define AIO 229; // a minuscula con una o encima
#define AAA 192; // a mayuscula con acento abierto
#define AAC 193; // a mayuscula con acento cerrado
#define AAF 194; // a mayuscula con acento circunflejo
#define AAT 195; // a mayuscula con tilde
#define AAD 196; // a mayuscula con dieresis
#define AAO 197; // a mayuscula con una o encima
#define EIA 232; // e minuscula con acento abierto
#define EIC 233; // e minuscula con acento cerrado
#define EIF 234; // e minuscula con acento circunflejo
#define EID 235; // e minuscula con dieresis
#define EAA 200; // e mayuscula con acento abierto
#define EAC 201; // e mayuscula con acento cerrado
#define EAF 202; // e mayuscula con acento circunflejo
#define EAD 203; // e mayuscula con dieresis
#define IIA 236; // i minuscula con acento abierto
#define IIC 237; // i minuscula con acento cerrado
#define IIF 238; // i minuscula con acento circunflejo
#define IID 239; // i minuscula con dieresis
#define IAA 204; // i mayuscula con acento abierto
#define IAC 205; // i mayuscula con acento cerrado
#define IAF 206; // i mayuscula con acento circunflejo
#define IAD 207; // i mayuscula con dieresis
#define OIA 242; // o minuscula con acento abierto
#define OIC 243; // o minuscula con acento cerrado
#define OIF 244; // o minuscula con acento circunflejo
#define OIT 245; // o minuscula con tilde
#define OID 246; // o minuscula con dieresis
#define OAA 210; // o mayuscula con acento abierto
#define OAC 211; // o mayuscula con acento cerrado
#define OAF 212; // o mayuscula con acento circunflejo
#define OAT 213; // o mayuscula con tilde
#define OAD 214; // o mayuscula con dieresis
#define UIA 249; // u minuscula con acento abierto
#define UIC 250; // u minuscula con acento cerrado
#define UIF 251; // u minuscula con acento circunflejo
#define UID 252; // u minuscula con dieresis
#define UAA 217; // u mayuscula con acento abierto
#define UAC 218; // u mayuscula con acento cerrado
#define UAF 219; // u mayuscula con acento circunflejo
#define UAD 220; // u mayuscula con dieresis
#define YIC 253; // y minuscula con acento cerrado
#define YAC 221; // y mayuscula con acento cerrado
#define  NI 241; // letra enye minuscula
#define  NA 209; // letra enye mayuscula
#define  CI 231; // cedilla minuscula
#define  CA 199; // cedilla mayuscula


// Juegos de caracteres posibles
#define UTF8 0
#define ANSI 1


// Funciones de comparación disponibles
#define IGUAL_QUE 0
#define MENOR_QUE 1
#define MAYOR_QUE 2


size_t UTF8long (char *s, char juego) {
// Calcula la longitud en caracteres (no bytes) de una cadena UTF-8 o ANSI
	int n;

	n = 0;
	while (*s  != '\0') {
		if (((*s & 128) == 0) || ((*s & 192) == 192) || (juego == ANSI)) n++;
		s++;
	}
	return (n);
}


char NumBytes (char *s, char juego) {
// Obtiene el número de bytes que debe tener un caracter UTF-8 o ANSI (en ANSI siempre es 1)
	if (juego == UTF8) {
		return (1 + ((*s & 192) == 192) + ((*s & 224) == 224) + ((*s & 240) == 240));
	} else return 1;
}


int ValidaUTF (char *s) {
// Comprueba si la secuencia de bytes UTF-8 de la cadena s es válida.
	char n, i;

	while (*s == '\0') {
		n = NumBytes (s, UTF8);
		if (n > 1){
			for (i=1; i<n; i++) {
				switch (n) {
					case 2: if ((*s & 224) != 192) return 0;
					case 3: if ((*s & 240) != 224) return 0; 
					case 4: if ((*s & 248) != 240) return 0; 
				}
				s++;
				if (*s == '\0') {
					return 0;
				} else if ((*s & 192) != 128) return 0;
			}
		} else s++;
        }
	return 1;
}


int utf24bit (char *s, char juego) {
// Pasa a int un caracter UTF-8 o ANSI
	char n, i;
	int c;
	
	if (((*s & 255) > 127) && (juego == UTF8)) {
		n = ((*s & 192) == 192) + ((*s & 224) == 224) + ((*s & 240) == 240);
		c = pow (2,4-n) * 4 -1;
		c = (*s & c);
		for (i = 0; i < n; i++) { // tantas veces como diga n
			s++;
			c = (c << 6) + (*s & 63);
		}
	} else c = (*s & 255);
	return (c);
}


char MeteChar (char *s, int c, char juego){
// Introduce el caracter c UTF o ANSI en la cadena a la que
// apunta s, y devuelve el numero de bytes introducidos
	char buffer[4], i, n, t;

	if (((c & 255) > 127) && (juego == UTF8)) { // Son varios bytes
		n= 2 + (c > 2047) + (c > 65535);// Calcula número de bytes necesario
		for (i = 0; i < n; i++) {	// divide el número en los bytes necesarios
			buffer[i] = ((char)(c & 63));	// y los introduce en el buffer
			c = (c >> 6);
		}
		switch (n) {
			case 2: t = 192; break;
			case 3: t = 224; break;
			case 4: t = 250; break;
		}
		*s = (buffer[n-1] | t);
		for (i=n; i>1; i--) {
			s++;
			*s = (buffer [i-2] | 128);
		}
	} else { // Es 1 byte
		*s = (char) c;
		n = 1; 
	}
	return n;
}	


void Mayuscula (char *s, char juego) {
// Pasa a Mayúscula el primer caracter de una cadena UTF-8 o ANSI
	int c,  n;
	
	c = utf24bit (s, juego);
	if (((c > 96) && (c < 123 )) || ((c > 223) && (c < 247)) || ((c > 247) && (c < 255))) {
		c = c - 32;
		MeteChar (s, c, juego); 
	}
}


void Minuscula (char *s, char juego) {
// Pasa a minúscula el primer caracter de una cadena UTF-8 o ANSI
	int c;

	c = utf24bit (s, juego);
	if (((c > 64) && (c < 91)) || ((c > 191) && (c < 215)) || ((c > 215) && (c < 223))) {
		c = c + 32;
		MeteChar (s, c, juego);	
	}
}


void MaysCadena (char *s, char juego) {
// Convierte a mayúsculas una cadena de caracteres UTF-8 o ANSI
	while (*s != '\0') {
		if (((*s & 128) == 0) || ((*s & 192) == 192) || (juego == ANSI))
			Mayuscula (s, juego);
		s++;
	}
}


void MinsCadena (char *s, char juego) {
// Convierte a minúsculas una cadena de caracteres UTF-8 o ANSI
	while (*s != '\0') {
		if (((*s & 128) == 0) || ((*s & 192) == 192) || (juego == ANSI))
			Minuscula (s, juego);
		s++;
	}
}


void QuitarAcentos (char *s, char *e, char juego) {
// Copia e en s substituyendo las vocales acentuadas por las equivalentes sin acentuar
	int c;
	char n,i;
	
	while (*e != '\0') {
		if (((*e & 128) == 0) || ((*e & 192) == 192) || (juego == ANSI)) {
			c = utf24bit (e, juego);
			// Comprueba acentos
			if ((c>191) && (c<198)) c=65;
			if ((c>199) && (c<204)) c=69;
			if ((c>203) && (c<208)) c=73;
			if ((c>209) && (c<215)) c=79;
			if ((c>216) && (c<221)) c=85;
			if ((c>223) && (c<230)) c=97;
			if ((c>231) && (c<236)) c=101;
			if ((c>235) && (c<240)) c=105;
			if ((c>241) && (c<247)) c=111;
			if ((c>248) && (c<253)) c=117;
			n = MeteChar (s, c, juego);
			for (i=1; i<=n; i++) s++;
		}
		e++;
	}
	*s = '\0';
}



void convertir (char *ss, char *se, char *temp, char juego, char modo) {
// Cambia los códigos de los caracteres para poder ordenar por orden alfabético real.
// Utilizada por la función AlfaComp
	int c;
	char *t, *s, i, n;

	strcpy (temp,se);		// Copiamos la entrada en temp, para no modificar la cadena de origen
	MaysCadena (temp, juego);	// Pasamos temp a mayúsculas
	QuitarAcentos (ss, temp, juego);// Quitamos los acentos de temp, y guardamos el resultado en ss
	// Ahora tratamos con las letras Ñ y Ç, convirtiéndolas a 1 byte si son
	// UTF-8, reordenando y almacenando el resultado de nuevo en temp
	t = temp;
	s = ss;
	while (*s != '\0') {
		c = utf24bit (s, juego);
		if ((c > 67) && (c < 79) && (modo == 0)) c = c+1;
		if ((c > 78) && (c < 97)) c = c+2-modo;			
		switch (c) {
			case 199: c = 68-modo; s++; break;		
			case 209: c = 80-modo; s++; break;
		}
		n = MeteChar (t, c, juego);
		for (i=1; i<=n; i++) t++;
		s++;
	}
	*t = '\0';
	strcpy (ss,temp); // Devolvemos el resultado
}


int max (int a, int b) {
	if (a > b){
		return a;
	}
	else return b;
}


int AlfaComp (char *a, char *b, char juego, char func) {
// Compara dos cadenas ANSI o UTF-8 teniendo en cuenta el correcto orden alfabético.
// Devuelve 1 si se cumple la condición indicada (MAYOR_QUE, MENOR_QUE o IGUAL_QUE)
// y 0 si no se cumple. En caso de error devuelve -1 para indicar que se le ha pasado
// una condición no válida o -2 si se ha producido un error de asignación de memoria.
	char *aa, *bb, *tt;
	int ln1, ln2, ln3 ,r;

	// Reserva memoria
	ln1 = strlen (a);
	if ((aa = malloc(ln1 + 1)) == NULL) return -2;
	ln2 = strlen (b);
	if ((bb = malloc(ln2 + 1)) == NULL) {
		free (aa);
		return -2;
	}
	ln3=max (ln1, ln2);
	if ((tt = malloc(ln3 + 1)) == NULL) {
		free (aa);
		free (bb);
		return -2;
	}
	// Compara las cadenas
	convertir (aa, a, tt, juego, 1);
	convertir (bb, b, tt, juego, 1);
	r = strcmp (aa, bb);
	if ((r == 0) && (func != IGUAL_QUE)) {
		convertir (aa, a, tt, juego, 0);
		convertir (bb, b, tt, juego, 0);
		r = strcmp (aa, bb);
	}
	// Libera memoria
	free (aa);
	free (bb);
	free (tt);
	// Devuelve resultado
	switch (func) {
		case IGUAL_QUE: return (r == 0); break;
		case MAYOR_QUE: return (r > 0); break;
		case MENOR_QUE: return (r < 0); break;
		default: return -1; // error
	}
}

