package fr.iutfbleau.sae.mhuffman;
import java.util.*;

/**
 * 
 * Classe pour generer des codes canoniques de Huffman.
 * Les codes canoniques sont des codes de Huffman reorganises 
 * pour faciliter la compression et le decodage.
 */
public class CanonicalCode {

    /**
     * Genere des codes canoniques a partir de codes de Huffman.
     * 
     * @param codesHuffman les codes de Huffman initiaux
     * @return les codes canoniques generes
     */
    public Map<Integer, String> generateCodes(Map<Integer, String> codesHuffman) {
        // On recupere les entrees des codes Huffman pour pouvoir les trier
        List<Map.Entry<Integer, String>> liste = new ArrayList<>(codesHuffman.entrySet());

        // On trie par longueur de la valeur ou sinon par la cle
        Collections.sort(liste, new ComparateurCanonique());
        
        Map<Integer, String> canonicalCodes = new HashMap<>();
        int code = 0; // Code canonique a attribuer 
        int temp = 0; // Garde la longueur du code precedent, pour gerer le decalage 

        for (Map.Entry<Integer, String> entree : liste) {
            int valeur = entree.getKey(); // Symbole actuel 
            int longueur = entree.getValue().length(); // Longueur du code actuel 

            code <<= (longueur - temp); // Permet de decaler le code actuel si la longueur augmente
            String codeBinaire = Integer.toBinaryString(code);

            // Permet d'ajouter des zeros si necessaire
            while (codeBinaire.length() < longueur) {
                codeBinaire = "0" + codeBinaire;
            } 

            canonicalCodes.put(valeur, codeBinaire); // Ajout dans le dictionnaire 
            code++; // Incrementation pour la valeur qui suit 
            temp = longueur; // Mise a jour de la longueur precedente
        }

        return canonicalCodes;
    }

    /**
     * Recupere le code canonique d'une valeur.
     * 
     * @param canonicalCodes la table des codes canoniques
     * @param value la valeur dont on veut le code
     * @return le code canonique correspondant
     */
    public String getCode(Map<Integer, String> canonicalCodes, int value) {
        return canonicalCodes.get(value);
    }

    /**
     * Recupere la longueur d'un code pour une valeur donnee.
     * 
     * @param codesH la table des codes
     * @param value la valeur dont on veut la longueur
     * @return la longueur du code en bits
     */
    public int getLength(Map<Integer, String> codesH, int value) {
        return codesH.get(value).length();
    }
}