/*
 *             File: iban.c
 *
 *           Autore: Roberto FULIGNI
 *  Ultima modifica: 22/04/2022   
 * 
 *      Descrizione: Calcolo dei codici BBAN e IBAN 
 *                   di un conto corrente italiano.
 */     


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#define MAX_ABI      5
#define MAX_CAB      5
#define MAX_CONTO   12
#define MAX_BBAN    23
#define MAX_IBAN    (MAX_BBAN + 4)
#define MAX_TABELLA 36

char TABELLA[MAX_TABELLA+1] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

int VAL_PARI[MAX_TABELLA] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 
                             5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 
                             17, 18, 19, 20, 21, 22, 23, 24, 25};
                               
int VAL_DISPARI[MAX_TABELLA] = {1, 0, 5, 7, 9, 13, 15, 17, 19, 21, 1, 0, 5, 
                                7, 9, 13, 15, 17, 19, 21, 2, 4, 18, 20, 11, 
                                3, 6, 8, 12, 14, 16, 10, 22, 25, 24, 23};


// La funzione cin_italiano riceve in ingresso una stringa 
// contenente un codice BBAN e restituisce il CIN italiano

char cin_italiano(char bban[]) {
    int lung_bban = strlen(bban);
    if (lung_bban != MAX_BBAN) {
        printf("Codice BBAN non valido\n");
        exit(1);
    }
    // Si calcola il CIN a partire dal secondo carattere, tralasciando così 
    // l'eventuale cin gia' presente
    int somma = 0;

    for(int i = 1; i < lung_bban; i++) {
        // Individua la posizione del carattere i-esimo scandendo la tabella
        // dall'ultimo carattere al primo
        int pos = MAX_TABELLA - 1;
        while (pos >= 0 && TABELLA[pos] != toupper(bban[i]))
            pos--;
        if (pos < 0) {
            printf("Il codice BBAN contiene un carattere non valido: %c\n", bban[i]);
            exit(1);
        }
        if (i % 2 == 0)
            // Posizione pari
            somma += VAL_PARI[pos];
        else
            somma += VAL_DISPARI[pos];
    }

    int resto = somma % 26;
    char cin = 'A' + resto;
    return cin;
        
}

// Funzione calcola_bban
//
// Parametro di output: codice BBAN elaborato
// Parametri di input:  codici ABI e CAB, conto corrente
 
void calcola_bban(char bban[], char abi[], char cab[], char conto[]) {
    if (strlen(abi) != MAX_ABI) {
        printf("Codice ABI non valido\n");
        exit(1);
    }
    if (strlen(cab) != MAX_CAB) {
        printf("Codice CAB non valido\n");
        exit(1);
    }
    int lung_conto = strlen(conto);
    if (lung_conto == 0 || lung_conto > MAX_CONTO) {
        printf("Numero di conto corrente non valido\n");
        exit(1);
    }
    
    // Si copia il contenuto a partire dalla seconda posizione (indice zero-based)
    // tralasciando così, per il momento, il CIN italiano
    bban[0] = ' ';
    int pos = 1;   
    for(int i = 0; i < MAX_ABI; i++) {
        bban[pos] = abi[i];
        pos++;
    }
    for(int i = 0; i < MAX_CAB; i++) {
        bban[pos] = cab[i];
        pos++;
    }
    // Copiamo ora il numero di conto, partendo dall'ultima cifra (quella più a destra)
    // fino alla prima: in questo potremo aggiungere alla fine gli eventuali zeri di padding
    pos = MAX_BBAN - 1;
    for (int i = lung_conto - 1; i >= 0; i--) {
        bban[pos] = conto[i];
        pos--;
    }
    
    int num_zeri = MAX_CONTO - lung_conto;
    
    while (num_zeri > 0) {
        bban[pos] = '0';
        pos--;
        num_zeri--;
    }

    
    char cin = cin_italiano(bban);
    bban[0] = cin;
    
    // Inserisco il terminatore di stringa
    bban[MAX_BBAN] = '\0';
}

// La funzione cin_europeo riceve in ingresso due stringhe 
// contenenti un codice di nazione e un codice BBAN.
// Restituisce un numero rappresentante il CIN europeo

int cin_europeo(char nazione[], char bban[]) {
    if (strlen(nazione) != 2) {
        printf("Codice di nazione non valido\n");
        exit(1);
    }
    
    int lung_bban = strlen(bban);
    if(strcmp(nazione, "IT") == 0 && lung_bban != MAX_BBAN) {
        printf("Codice BBAN non valido\n");
        exit(1);
    }
    
    int lung_buffer = MAX_BBAN + 4;
    char buffer[lung_buffer+1];
    strcpy(buffer, bban);
    strcat(buffer, nazione);
    strcat(buffer, "00");
    
    int somma = 0;
    for(int i = 0; i < lung_buffer; i++) {
        char c = toupper(buffer[i]);
        int val;
        if (isdigit(c)) {
            val = c - '0';
            somma = somma * 10 + val;
        }
        else if (isalpha(c)) {
            val = c - 'A' + 10;
            somma = somma * 100 + val;
        }
        else {
            printf("Carattere non valido: %c\n", c);
            exit(1);
        }
        
        // Alla fine di ogni iterazione si riduce il valore della somma
        somma = somma % 97;
    }
    int cin = 98 - somma;
    return cin;
}

// Funzione calcola_iban
//
// Parametro di output: codice IBAN elaborato
// Parametri di input: codice nazione, codice BBAN

void calcola_iban(char iban[], char nazione[], char bban[]) {
    if (strlen(nazione) != 2) {
        printf("Codice di nazione non valido\n");
        exit(1);
    }
    
    if(strcmp(nazione, "IT") == 0 && strlen(bban) != MAX_BBAN) {
        printf("Codice BBAN non valido\n");
        exit(1);
    }
    
    int cin_eu = cin_europeo(nazione, bban);
    char str_cin[3];
    str_cin[0] = '0' + (cin_eu / 10) % 10;     // Cifra delle decine
    str_cin[1] = '0' + (cin_eu) % 10;          // Cifra delle unita'
    str_cin[2] = '\0';                         // Terminatore
    strcpy(iban, nazione);
    strcat(iban, str_cin);
    strcat(iban, bban);
}
     
int main() {
    char abi[6] = "08327";
    char cab[6] = "38941";
    char cc[13] = "172964";
    char nazione[3] = "IT";

    char bban[MAX_BBAN+1];
    char iban[MAX_IBAN+1];
    
    
    calcola_bban(bban, abi, cab, cc);
    printf("BBAN: %s\n", bban);
    
    calcola_iban(iban, nazione, bban);
    printf("IBAN: %s\n", iban);
    
    return 0;
}