/*************************************************************
**         Europäisches Institut für Systemsicherheit        *
**         Praktikum "Kryptoanalyse"                         *
**                                                           *
**   Versuch 2: Permutations-Chiffre                         *
**                                                           *
**************************************************************
**
** attacke.c: Implementierung einer Permutations-Chiffre
**            Rahmenprogramm zur Lösung
**/

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

#include "libperm.h"

#define PERIODE 20
#define CHIFFRAT "chiffrat"
#define PERMUTATION "permutation"
#define LOESUNG "klartext"

int loesung[PERIODE];
int mat[PERIODE][PERIODE] = {0};        //initialisiere die Matrix mit Nullen
int laenge;
char *chiffrat;

char *scratch1, *scratch2;

void attacke (void);

int main (void)
{
  FILE *f;

  f = fopen (CHIFFRAT, "r");
  if (! f) {
    perror ("fopen");
    fprintf (stderr, "Konnte Datei %s nicht oeffnen\n", CHIFFRAT);
    exit (2);
  }
  fseek (f, 0, SEEK_END);
  laenge = ftell (f);
  rewind (f);
  chiffrat = malloc (laenge);
  scratch1 = malloc (laenge);
  scratch2 = malloc (laenge);
  if (! chiffrat || ! scratch1 || ! scratch2) {
    fprintf (stderr, "Konnte Puffer nicht allozieren\n");
    exit (2);
  }
  if (fread (chiffrat, 1, laenge, f) != laenge) {
    fprintf (stderr, "Fehler beim einlesen der Datei %s\n", CHIFFRAT);
    exit (2);
  }
  fclose (f);

  {
    attacke ();
  }
  if (writeperm (PERMUTATION, PERIODE, loesung) < 0) {
    fprintf (stderr, "Fehler beim Schreiben der Loesung auf Datei %s\n",
             PERMUTATION);
    exit (2);
  }
  printf ("Nun kannst Du versuchen, die Datei mit dem Befehl:\n");
  printf ("./decrypt %s %s %s\n", PERMUTATION, CHIFFRAT, LOESUNG);
  printf ("zu entschluesseln, um zu sehen, ob die Loesung stimmt.\n");
  exit (0);
}

int isUpperCase (char a)
{
    return (a >= 'A' && a <= 'Z');
}

int isLowerCase (char a)
{
    return (a >= 'a' && a <= 'z');
}

int prob(char u, char v)
{
    if (isspace(u) && (isUpperCase(v)))
        return 1;
    if (isUpperCase(u) && isLowerCase(v))
        return 1;
    if (u == 't' && v == 'h')
        return 1;
    if (u == 'h' && v == 'e')
        return 1;
    if (isspace(u) && v == '(')
        return 1;
    if (u == '.' && isspace(v))
        return 1;
    if (u == ',' && isspace(v))
        return 1;
    return 0;
}

int maxInZeile (int zeile)
{
    int max = 0;
    for (int i = 0; i < PERIODE; i++)
    {
        if (mat[zeile][i] > max)
            max = mat[zeile][i];
    }
    return max;
}

int maxInSpalte (int spalte)
{
    int max = 0;
    for (int i = 0; i < PERIODE; i++)
    {
        if (mat[i][spalte] > max)
            max = mat[i][spalte]; 
    }
    return max;
}


int minInZeile (int zeile)
{
    int min = 4000;
    for (int i = 0; i < PERIODE; i++)
    {
    //printf("\nVergleiche %i mit %i\n", min, mat[zeile][i]);
        if ((mat[zeile][i] < min) && (zeile != i))
        {
            min = mat[zeile][i];
        }
    }
    return min;
}

int posMinInZeile (int zeile)
{
    int min = 4000;
    int pos = 0;
    for (int i = 1; i < PERIODE; i++)
    {
        if (mat[zeile][i] <= min && zeile != i)
        {
            min = mat[zeile][i];
            pos = i;
        }
    }
    return pos + 1;
}

void attacke (void)
{
	/* *** Hier soll die Attacke implementiert werden *** */
	/* Globale Variablen:
	*   laenge         Laenge des Chiffrats
	*   chiffrat       Puffer, in dem das Chiffrat vorliegt
	*   scratch1  und
	*   scratch2       2 Puffer der Laenge 'laenge', die beliebig verwendet
	*                  werden koennen (char *)
	*   loesung        int loesung[PERIODE], dort sollte nach dem Ende
	*                  dieser Funktion die gesuchte Permutation stehen!
	*   PERIODE        In diesem #define steht die Periodenlaenge, die
	*                  in diesem Versuch benutzt wurde.
	*/

  /* Aufgabe */
    
    printf("Länge des Textes: %i\n", laenge);
    int anzahlBloecke = laenge / PERIODE;   //hier wird der letzte, eventuell unvollständige Block bewusst unterschlagen.
    printf("Anzahl Blöcke im Chiffrat: %i\n", anzahlBloecke);
    printf("Matrix A:\n");
    for (int zeile = 0; zeile < PERIODE; zeile++)
    {
        for (int spalte = 0; spalte < PERIODE; spalte++)
        {
            if (zeile != spalte){      //verändere nur die nicht-diagonalen, diese bleiben 0
                int b = 0;      //Summe, Bigrammstatistik
                for (int i = 0; i < anzahlBloecke; i++)
                {
                    b += prob(chiffrat[i * 20 + zeile], chiffrat[i * 20 + spalte]);
                }
            mat[zeile][spalte] = b;
            }
            printf("%i\t", mat[zeile][spalte]);
        }
        printf("\n");
    }

    int mi[PERIODE];    //Zeilenvektor aus Spaltenmaximum
    int c[PERIODE];     //Spaltenvektor aus Zeilenmaximum
    printf("Zeilenvektor aus Spaltenmaximum: [");

    for (int i = 0; i < PERIODE; i++)
    {
        int max = 0;
        for (int j = 0; j < PERIODE; j++)
        {
            if (mat[j][i] > max)
            {
                max = mat[j][i];        //finde Maximum
            }
        }
        mi[i] = max;    //fülle Zeilenvektor mit dem Maximum aus der Spalte
        printf("%i,", mi[i]);   //gebe ihn aus. Debug lol
    }    
    printf("]\nSpaltenvektor aus Zeilenmaximum: [");

    for (int i = 0; i < PERIODE; i++)
    {
        c[i] = maxInZeile(i);
        printf("%i,", c[i]);
    }

    printf("]\n");


    int min = mi[0];
    int minPos = 0;
    for (int i = 1; i < PERIODE; i++)
    {
        if (mi[i] < min)
        {
            min = mi[i];
            minPos = i;
        }
    }
    printf("\nMinimum des Zeilenvektors aus Spaltenmaxima: %i an Position %i\n", min, minPos);

    loesung[0] = minPos;

    printf("Zeile %i: ", loesung[0]);
    
    for (int i = 0; i < PERIODE; i++)
    {
        printf("%i", mat[loesung[0] - 1][i]);
    }

    printf(" und hat das Minimum %i bei %i\n", minInZeile(loesung[0] - 1), posMinInZeile(loesung[0] - 1));
    for (int i = 1; i < PERIODE; i++)
        loesung[i] = posMinInZeile(loesung[i - 1] - 1);    

    printf("Lösungsvektor: [");     //gebe den Lösungsvektor aus
    for (int i = 0; i < PERIODE; i++)
    {
        printf("%i,", loesung[i]);
    }
    printf("]\n");

}

