program Vigenere;

{ Restituisce False se il carattere deve esser ingorato, True altrimenti }
function filter_char(VAR c:char):boolean;
begin
	if c in ['A' .. 'Z'] then
	begin
		{ Lettera maiuscola, viene ridotta}
		c := chr((ord(c)) + 32);
		filter_char := true;
	end
	else if c in ['a' .. 'z'] then
		{ Lettera minuscola, ok}
		filter_char := true
	else
		{ Carattere da ignorare}
		filter_char := false
end;	


{ Somma, sottrazione e inverso di caratteri a-z
 Visti come elementi 0-25 implementa operazioni modulo 26
 'c' + 'x' = 3 + 23 = 26 mod 26 = 0  = 'a' = 97
 'c' - 'x' = 3 - 23 = 20 mod 26 = 20 = 'u' = 117
 -'c' = -3 = 0-3 mod 26 = 23 = 'x' =  120
 Il tutto è allineato all'intervallo 97-122 del codice ASCII}	
function plus(a,b : char):char;
begin
	plus := chr((ord(a) + ord(b) -97-97) mod 26 + 97)
end;

function minus(a,b : char):char;
begin
	minus := chr((ord(a) - ord(b)+26) mod 26 + 97)
end;

function inv(a : char):char;
begin
	inv := minus('a',a)
end;

procedure help_and_exit(msg : string);
begin
	writeln(StdErr,msg);
	writeln(StdErr,'Utilizzo: vig [Opzioni] chiave');
	writeln(StdErr,'   chiave è una sequenza di caratteri a-z');
	writeln(StdErr,'Opzioni:');
	writeln(StdErr,'-d         Opera in decifrazione');	
	writeln(StdErr,'-i <file>  File di input, se omesso utilizza stdin');	
	writeln(StdErr,'-o <file>  File di output, se omesso utilizza stdout');
	writeln(StdErr,'-h         Visualizza questo messaggio');
	writeln(StdErr,'-p         Stampa spaziatura e punteggiatura');
	exit(program)
end;

{ Funzione ausiliaria per i parametri che richiedono un argomento
  Dato un indice controlla che questo sia seguito da un ulteriore parametro.
  Restituisce l'indice dell'argomento
  In caso contrario notifica l'errore e termina il programma }
function check_next_param(VAR i : integer) : integer;
begin
	if(paramCount > i) then
	begin
		i := i + 1;
		check_next_param := i;
	end
	else
		help_and_exit(paramStr(i) + ' richiede un argomento.')
end;

begin
	var c : char;
	var key : string[30] = '#';
	var key_len, i : integer = 1;
	var flag_dec, flag_p : boolean = false;
	{ lettura dei parametri del programma}
	while (i <= paramcount) do
	begin
		if paramStr(i) = '-i' then
			reset(input, paramStr(check_next_param(i)))
		else if paramStr(i) = '-o' then
			rewrite(output, paramStr(check_next_param(i)))
		else if paramStr(i) = '-d' then
			flag_dec := true
		else if paramStr(i) = '-p' then
			flag_p := true
		else if paramStr(i) = '-h' then
			help_and_exit('')
		else
		begin
			key := paramStr(i);
			if i<paramcount then 
				help_and_exit('non è stato possibile riconoscere alcuni parametri.')
		end;
		i := i + 1;
	end;
	{ Controllo dei parametri}
	key_len := length(key);
	for i := 1 to key_len do
		if not (key[i] in ['a' .. 'z']) then
			help_and_exit('la chiave deve contenere solamente lettere minuscole.');
			
	{ se flag_dec = 1 il programma lavora in decifrazione.
	  grazie alla simmetria del cifrario decifrare rispetto a una chiave k
	  equivale a cifrare con un'opportuna chiave k'.
	  k' è ottenibile da k (e viceversa) calcolando, per ogni suo carattere,
	  l'inverso.
	  a <-> z, b <-> y, c <-> x ...
	  Visti come numeri modulo 26, a = 0 <-> 25 = z
	  Quindi, dato x, numero modulo 26, il suo inverso sarà -x modulo 26.
	  A causa del comportamento di % in C rispetto ai numeri negativi, invece
	  di considerare -x % 26 si utilizzerà (26-x) % 26.
	  Il tutto deve esser spostato dall'intervallo 0-25 a 97-122.}
	if flag_dec then
		for i := 1 to key_len do
			key[i] := inv(key[i]);
			
	{ Processa un carattere alla volta dell'input.
	  la procedura filter_char restituisce True se il carattere deve essere 
	  cifrato e False se deve essere ignorato.
	  Se questo è maiuscolo lo rimpiazza con l'equivalente minuscolo. }
	i := 1;
	while (not eof) do
	begin
		read(c);
		if filter_char(c) then
		begin
			{ L'indice i seleziona il carattere della chiave key, e 
			  conseguentemente il cifrario mono-alfabetico, da utilizzare
			  per cifrare c.
			  Se k = key[i] allora c è sostituito con c + k (in modulo)
			  Visti come numeri modulo 26 :
			  'c' = 3, k = 'k' = 10 --> 'c' + 'k' = c + k mod 26 = 13 = 'n'
			  Lavorando in ASCII, il tutto è spostato nell'intervallo 97-122}
			write(plus(c,key[i]));
			{ Cicla sui caratteri della chiave }
			if i = key_len then
				i := 1
			else
				i := i + 1;
		end
		else if flag_p then
			{ visualizza i caratteri ignorati}
			write(c);	
	end;
	close(input);
	close(output);	
end. 


