package hamming.Models;

public class HammingCalcul {

    // --- PARTIE 1 : GÉNÉRATION ---
    public static String genererHamming(String message) {
        int m = message.length();
        int r = 0;

        // Calcul du nombre de bits de parité nécessaires : 2^r >= m + r + 1
        while (Math.pow(2, r) < (m + r + 1)) {
            r++;
        }
        System.out.println("Longueur du message (m) : " + m);
        System.out.println("Bits de parité nécessaires (r) : " + r);
        System.out.println("Longueur totale du mot (m+r) : " + (m + r));

        int totalLength = m + r;
        int[] hammingCode = new int[totalLength + 1]; // +1 car on travaille souvent en base 1 (indices 1..N)
        
        // Initialisation : Placement des bits de données et des places vides pour la parité
        int j = 0; // index du message
        for (int i = 1; i <= totalLength; i++) {
            // Si i est une puissance de 2, c'est une place pour la parité
            if ((Math.ceil(Math.log(i) / Math.log(2)) - Math.floor(Math.log(i) / Math.log(2))) == 0) {
                hammingCode[i] = 0; // Place réservée (initialisée à 0)
                System.out.println("Position " + i + " réservée pour Parité P" + i);
            } else {
                hammingCode[i] = Character.getNumericValue(message.charAt(j));
                System.out.println("Position " + i + " contient la donnée D : " + hammingCode[i]);
                j++;
            }
        }

        System.out.println("\n--- Calcul des Parités ---");
        // Calcul des bits de parité
        for (int i = 0; i < r; i++) {
            int positionParite = (int) Math.pow(2, i);
            int parite = 0;

            System.out.print("Calcul P" + positionParite + " (vérifie positions) : ");
            // On vérifie les bits concernés par cette parité
            for (int k = 1; k <= totalLength; k++) {
                // Astuce bit à bit : si la position k a un '1' au même endroit binaire que positionParite
                if (((k >> i) & 1) == 1) {
                    if (k != positionParite) { // On ne se compte pas soi-même au début
                         System.out.print(k + " ");
                         parite = parite ^ hammingCode[k]; // XOR
                    }
                }
            }
            hammingCode[positionParite] = parite;
            System.out.println("-> Valeur P" + positionParite + " = " + parite);
        }

        // Construction de la chaîne finale
        StringBuilder sb = new StringBuilder();
        // Attention : on lit souvent du poids fort au faible, mais ici on va afficher de 1 à N pour la lisibilité
        // Selon l'énoncé, on concatène juste. Inverser l'ordre si le prof demande (N vers 1).
        // Ici je fais de l'index 1 à Total.
        for (int i = 1; i <= totalLength; i++) {
            sb.append(hammingCode[i]);
        }
        
        // Inverser pour l'affichage classique (bit de poids fort à gauche) si besoin
        // return sb.reverse().toString(); 
        
        // Pour ce TP, on retourne souvent dans l'ordre des indices croissants :
        return sb.toString();
    }

    // --- PARTIE 2 : VÉRIFICATION ---
    public static String verifierHamming(String messageRecu) {
        int totalLength = messageRecu.length();
        int[] data = new int[totalLength + 1];
        
        // Conversion String -> Tableau (indices 1 à N)
        for (int i = 0; i < totalLength; i++) {
            data[i + 1] = Character.getNumericValue(messageRecu.charAt(i));
        }

        // Calcul du nombre de bits de parité r implicite
        int r = 0;
        while (Math.pow(2, r) < totalLength + 1) {
            r++;
        }
        System.out.println("Longueur reçue : " + totalLength + ". Nombre supposé de bits de parité : " + r);

        int errorPosition = 0;

        // Recalcul des parités (Syndrome)
        System.out.println("\n--- Vérification des Parités ---");
        for (int i = 0; i < r; i++) {
            int positionParite = (int) Math.pow(2, i);
            int pariteCalculee = 0;
            
            // On refait le XOR sur TOUS les bits concernés (y compris le bit de parité reçu)
            for (int k = 1; k <= totalLength; k++) {
                 if (((k >> i) & 1) == 1) {
                     pariteCalculee = pariteCalculee ^ data[k];
                 }
            }
            
            System.out.println("Contrôle C" + positionParite + " (XOR de son groupe) = " + pariteCalculee);
            
            // Si pariteCalculee != 0, ça veut dire qu'il y a une erreur.
            // On ajoute la valeur du bit de parité à la position d'erreur
            if (pariteCalculee != 0) {
                errorPosition += positionParite;
            }
        }

        if (errorPosition == 0) {
            return "Le message est CORRECT.";
        } else {
            return "ERREUR détectée à la position : " + errorPosition + " !";
        }
    }
}
