# include <stdlib.h>
# include <stdio.h>	//fuer printf, scanf
# include <string.h>	//fuer strcat, strlen
# include <ctype.h>     //fuer isalpha & co.
# include <regex.h>

char szStaatsAng[4];	//Staatsangehoerigkeit
char szBKZ [100];         //Behoerdenkennzahl
char szLaufZnr [100];     //Laufende Zaehlnummer

struct grGebDat{		//Geburtsdatum
    char tag [3], monat [3], jahr [5];
} grGD;

struct grAblDat{		//Ablaufdatum
    char tag [3], monat [3], jahr [5];
} grAD;

//RegExp fuer Datum pruefen
char[] match(const char *string, char *pattern)
{
   int status;
   regex_t re;
   if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0) {
        return(0);      /* report error */
   }
   status = regexec(&re, string, (size_t) 0, NULL, 0);
   regfree(&re);
   if (status != 0) {
         return(0);      /* report error */
   }
   return("123");
}

//char[] fuer Staatsangehoerigkeit basteln
void Staat() {
    switch(strlen(szStaatsAng)) {
        case 1:
            strcat(szStaatsAng, "<<\0" );
            break;

        case 2:
            strcat(szStaatsAng, "<\0" );
            break;
    }
}

int pruefDatum(int monat, int jahr){
    if(monat<=7 && monat%2!=0 && monat!=2)
        return 31;
    //printf("31 tage");
    else if (monat<=7 && monat%2==0 && monat!=2)
        return 30;
    //printf("30 tage");
    else if (monat>7 && monat%2==0)
        return 31;
    //printf("31 tage");
    else if (monat>7 && monat%2!=0)
        return 30;
    //printf("30 tage");

    else if (monat==2){
        if ( (jahr%4==0 && jahr%100!=0) || jahr%400==0 )
            return 29;
        //printf("29 tage");
        else
            return 28;
        //printf("28 tage");
    }
}

//Pruefsumme berechnen
int pSumme( char szZiffern[] ){
    int iPs0 = 0, iPs1 = 0, iPs2 = 0;	//Jeweilige Summen (1. 4. 7. Stelle usw)
    long iPs = 0;	//Preufziffernsumme

    //Jeweils alle +3 versetzten Stellen aufsummieren
    int i;
    int lol = strlen(szZiffern);
    for (i=0; i<strlen(szZiffern); i=i+3) {
        //-48 ist nur fuer 1331 hacker :-)
        if (i < strlen(szZiffern)){    //schon ausserhalb des char[]?
            iPs0 = iPs0 + szZiffern[i]-48;
        }

        if (i+1 < strlen(szZiffern)){
            iPs1 = iPs1 + szZiffern[i+1]-48;
        }

        if (i+2 < strlen(szZiffern)){
            iPs2 = iPs2 + szZiffern[i+2]-48;
        }
    }

    //Einzelne Summen multiplizieren und Gesamtsumme bilden
    iPs =  iPs0*7 + iPs1*3 + iPs2;

    //Nur letzte Ziffer nehmen, 1331 edition
    iPs = iPs%10;

    return iPs;
}

int getBKZ(){
    e1: printf("   Behoerdenkennzahl (4-stelig): ");
    scanf("%s", szBKZ); //normal einfach scanf("%4s", szBKZ);
    if(match(szBKZ, "^[0-9]{4}$") != 1){
        printf("Fehler, Behoerdenkennzahl hat falsche Formatierung!\n");
        goto e1;
        return 1;
    }

    if(abs(atoi(szBKZ)) != atoi(szBKZ)){
        printf("Fehler, Behoerdenkennzahl ist nicht positiv!\n");
        goto e1;
        return 1;
    }
    return 0;
}

int getLaufZnr(){
    e2: printf("   Laufende Zaehlnummer (5-stelig): ");
    fflush(stdin); //Tastaturpuffer leeren


    scanf("%s", szLaufZnr);
    if(match(szLaufZnr, "^[0-9]{5}$") != 1){
        printf("Fehler, Laufende Zaehlnummer hat falsche Formatierung!\n");
        goto e2;
        return 1;
    }
    return 0;
}

int getStaatsAng(){
    e5: printf("   Staatsangehoerigkeit (A/B/BG/CY/CZ/D/DK/E/EST/F\n   /FIN/GB/GR/H/"
            "I/IRL/LV/LT/L/M/NL/P/PL/RO/S/SK/SLO): ");
    scanf("%s", szStaatsAng);

    int strOK = 0;

    if(strcmp(szStaatsAng, "A") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "B") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "BG") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "CY") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "CZ") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "D") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "DK") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "E") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "EST") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "F") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "FIN") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "GB") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "GR") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "H") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "I") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "IRL") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "LV") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "LT") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "L") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "M") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "NL") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "P") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "PL") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "RO") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "S") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "SK") == 0) strOK = 1;
    if(strcmp(szStaatsAng, "SLO") == 0) strOK = 1;

    if(strOK != 1){
        printf("Fehler, das Land %s ist nicht Mitglied der EU!\n", szStaatsAng);
        goto e5;
    }
}

int getGebDat(){
    e3: printf("   Geburtsdatum (TT.MM.JJJJ): ");
    char eingabePuffer[10] = "000000000";

    scanf("%s", eingabePuffer); //Fuer RegExp Formatierungscheck
    sscanf(eingabePuffer, "%2s.%2s.%4s", grGD.tag, grGD.monat, grGD.jahr);

    //Auf Laengen der einzelnen Bloecke und . als Delimiter testen
    if(match(eingabePuffer, "^[0-9]{2}\\.[0-9]{2}\\.[0-9]{4}$") != 1){
        printf("Fehler, falsches Datumsformat!\n");
        goto e3;
    }

    if(atoi(grGD.monat) > 12){
        printf("Fehler, es gibt nur 12 Monate!\n");
        goto e3;
    }

    if(atoi(grGD.jahr) < 1900 || atoi(grGD.jahr) > 2100){
        printf("Fehler, Jahr kleiner 1900 oder groesser 2100!\n");
        goto e3;
    }

    if(atoi(grGD.tag) > pruefDatum(atoi(grGD.monat), atoi(grGD.jahr))){
        printf("Fehler, der Monat %s des Jahres %s hat %d Tage!\n",
                grGD.monat, grGD.jahr, pruefDatum(atoi(grGD.monat), atoi(grGD.jahr)));
        goto e3;
    }

    //Muss ja leider auf 1900<=JJJJ<=2100 pruefen
    grGD.jahr[0] = grGD.jahr[2];
    grGD.jahr[1] = grGD.jahr[3];
    grGD.jahr[2] = '\0';
    fflush(stdin); //Tastaturpuffer leeren
}

int getAblDat(){
    e4: printf("   Ablaufsdatum (TT.MM.JJJJ): ");
    char eingabePuffer[10] = "000000000";

    scanf("%s", eingabePuffer); //Fuer RegExp Formatierungscheck
    sscanf(eingabePuffer, "%2s.%2s.%4s", grAD.tag, grAD.monat, grAD.jahr);

    //Auf Laengen der einzelnen Bloecke und . als Delimiter testen
    if(match(eingabePuffer, "^[0-9]{2}\\.[0-9]{2}\\.[0-9]{4}$") != 1){
        printf("Fehler, falsches Datumsformat!\n");
        goto e4;
    }

    if(atoi(grAD.monat) > 12){
        printf("Fehler, es gibt nur 12 Monate!\n");
        goto e4;
    }

    if(atoi(grAD.jahr) < 1900 || atoi(grAD.jahr) > 2100){
        printf("Fehler, Jahr kleiner 1900 oder groesser 2100!\n");
        goto e4;
    }

    if(atoi(grAD.tag) > pruefDatum(atoi(grAD.monat), atoi(grAD.jahr))){
        printf("Fehler, der Monat %s des Jahres %s hat %d Tage!\n",
                grAD.monat, grAD.jahr, pruefDatum(atoi(grAD.monat), atoi(grAD.jahr)));
        goto e4;
    }

    //Muss ja leider auf 1900<=JJJJ<=2100 pruefen
    grAD.jahr[0] = grAD.jahr[2];
    grAD.jahr[1] = grAD.jahr[3];
    grAD.jahr[2] = '\0';
    fflush(stdin); //Tastaturpuffer leeren
}



int main(int argc, char *argv[]) {
    char szZiffSamm[24];          //Speichert alle Ziffernbloecke zwecks
    //spaeterem generieren der gesamt Pruefsumme

    char szAwNr[37];	//generierte Ausweisnummer
    char szPzR[3];	//Zwischenspeicher für die jeweils aktuelle Pruefziffer
    char szDatum[7];	//jjMMtt Daten

    printf("---EU Personalausweisnummerngenerator---\n\n Zur generierung einer"
            " EU Personalausweisnummer\n geben Sie bitte"
            " die folgenden Daten ein:\n\n");

    getBKZ();
    getLaufZnr();
    getGebDat();
    getAblDat();
    getStaatsAng();

    strncat(szAwNr, szBKZ, 4);	//Behoerdenkennziffer in finalen
    //Ausweisnummernstring kopieren
    strncat(szAwNr, szLaufZnr, 5);//Laufende Zaehlnummer noch hintendran anhaengen
    snprintf(szPzR, sizeof(szPzR)+1, "%d", pSumme(szAwNr));
    //Pruefsumme fuer Behoerdenkennziffer und
    //Laufende Zaehlnummer berechnen und gleich
    //in char[] umwandeln
    strcat(szAwNr, szPzR );	//Prufeziffer von BKZ und Laufender Nr anhaengen

    strcat(szZiffSamm, szAwNr);   //BKZ, LaufZnr und deren Pruefziffer sammeln

    Staat();                      //Staats-3er-Block richtig basteln
    strcat(szAwNr, szStaatsAng);	//und an Ausweisnummern String anhaengen

    strncat(szDatum, grGD.jahr, 2);   //Gesamtstring fuer Geburtsdatum
    strncat(szDatum, grGD.monat, 2);  //zusammenbasteln
    strncat(szDatum, grGD.tag, 2);
    snprintf(szPzR, sizeof(szPzR)+1, "%d", pSumme(szDatum));
    strcat(szAwNr, szDatum);          //Datum anhaengen
    strcat(szAwNr, szPzR);            //Pruefziffer vom Datum anhaengen

    strcat(szZiffSamm, szDatum);      //GebDat und dessen Pruefziffer sammeln
    strcat(szZiffSamm, szPzR);

    strcat(szAwNr, "<");

    strcpy(szDatum, grAD.jahr);       //JJ an den Anfang des char[]
    strncat(szDatum, grAD.monat, 2);  //zusammenbasteln
    strncat(szDatum, grAD.tag, 2);
    snprintf(szPzR, sizeof(szPzR)+1, "%d", pSumme(szDatum));
    strcat(szAwNr, szDatum);          //Datum anhaengen
    strcat(szAwNr, szPzR);            //Pruefziffer vom Datum anhaengen

    strcat(szZiffSamm, szDatum);      //AblaufDat und dessen Pruefziffer sammeln
    strcat(szZiffSamm, szPzR);

    strcat(szAwNr, "<<<<<<<");

    snprintf(szPzR, sizeof(szPzR)+1, "%d", pSumme(szZiffSamm)); //Gesamt PZ ber.
    strcat(szAwNr, szPzR);            //Pruefziffer vom alen Ziffern anhaengen

    printf("\n Personalausweisnummer:\n\n   %s\n\n", szAwNr);
    return(0);
}